0

我对使用 C++ 非常缺乏经验,但我正在尝试在 64 位 XP 平台上为 matlab编译SBML 工具箱的 2.0.2 版。SBML 工具箱依赖于Xerces 2.8 和libsbml 2.3.5。

我已经能够在 32 位机器上构建和编译工具箱,并且在我测试它时它可以工作。但是,在 64 位机器上重建它(这是一个巨大的 PITA!)后,当我尝试用它读取长 .xml 文件时出现分段错误。

我怀疑这个问题是由指针地址问题引起的。

分段错误的堆栈跟踪开始于:

[0] 000000003CB3856E libsbml.dll+165230 (StringBuffer_append+000030)
[6] 000000003CB1BFAF libsbml.dll+049071 (EventAssignment_createWith+001631)
[12]000000003CB1C1D7 libsbml.dll+049623(SBML_formulaToString+000039)
[18] 000000003CB2C154 libsbml.dll+115028 (

所以我正在查看 libsbml 代码中的 StringBuffer_append 函数:

LIBSBML_EXTERN
void
StringBuffer_append (StringBuffer_t *sb, const char *s)
{
  unsigned long len = strlen(s);


  StringBuffer_ensureCapacity(sb, len);

  strncpy(sb->buffer + sb->length, s, len + 1);
  sb->length += len;
}

确保容量看起来像这样:

LIBSBML_EXTERN
void
StringBuffer_ensureCapacity (StringBuffer_t *sb, unsigned long n)
{
  unsigned long wanted = sb->length + n;
  unsigned long c;


  if (wanted > sb->capacity)
  {
    /**
     * Double the total new capacity (c) until it is greater-than wanted.
     * Grow StringBuffer by this amount minus the current capacity.
     */
    for (c = 2 * sb->capacity; c < wanted; c *= 2) ;
    StringBuffer_grow(sb, c - sb->capacity);
  }                   
}

和 StringBuffer_grow 看起来像这样:

LIBSBML_EXTERN
void
StringBuffer_grow (StringBuffer_t *sb, unsigned long n)
{
  sb->capacity += n;
  sb->buffer    = (char *) safe_realloc(sb->buffer, sb->capacity + 1);
}

有没有可能

strncpy(sb->buffer + sb->length, s, len + 1);

在 StringBuffer_append 是我的段错误的来源?

如果是这样,有人可以建议修复吗?我真的不懂 C++,而且对指针和内存寻址特别困惑,所以我可能不知道你在说什么——我需要一些帮助。

此外,我将构建过程的详细信息放在此处,以防其他人正在尝试使用 Microsoft Visual C++ Express Edition 为 64 位系统编译 C++。

提前致谢!

-本

4

8 回答 8

1

尝试打印或使用调试器来查看您为某些中间变量获得的值。在 StringBuffer_append() O/P len 中,在 StringBuffer_ensureCapacity() 中观察循环之前和循环中的 sb->capacity 和 c。看看这些值是否有意义。

访问超出字符串末尾的数据可能会导致分段错误。

它在 32 位机器而不是 64 位操作系统上工作的奇怪事实也是一个线索。两台机器的物理和页面文件内存大小是否相同?此外,在 64 位机器中,内核空间可能比 32 位机器大,并且占用了 32 位操作系统的用户部分内存空间中的一些可用内存空间。对于 XML,整个文档必须适合内存。如果这是问题,可能有一些开关可以设置大小。仅当您使用非常大的字符串时,机器的差异才是问题的原因。如果字符串不是很大,则可能是库或实用程序方法在 64 位环境中无法正常工作的问题。

此外,如果您没有其他尝试,请使用简单/小型 xml 文件开始。

你在哪里初始化 sb->length。您的问题可能出在 strncpy() 中,尽管我不知道为什么 32 位 -> 64 位 O/S 更改很重要。最好的办法是查看中间值,然后你的问题就会很明显。

于 2009-02-25T16:22:19.843 回答
1

作为 libsbml 的开发人员之一,我偶然发现了这一点。这对您来说仍然是个问题吗?同时我们发布了 libsbml 5,有 64 位和 32 位的版本,测试也做了很多改进,请看:

http://sf.net/projects/sbml/files/libsbml

于 2011-06-25T16:53:07.097 回答
0

StringBuffer 定义如下:

/**
 * Creates a new StringBuffer and returns a pointer to it.
 */
LIBSBML_EXTERN
StringBuffer_t *
StringBuffer_create (unsigned long capacity)
{
  StringBuffer_t *sb;


  sb           = (StringBuffer_t *) safe_malloc(sizeof(StringBuffer_t));
  sb->buffer   = (char *)           safe_malloc(capacity + 1);
  sb->capacity = capacity;

  StringBuffer_reset(sb);

  return sb;
}

更多的堆栈跟踪是:

[  0] 000000003CB3856E              libsbml.dll+165230 (StringBuffer_append+000030)
[  6] 000000003CB1BFAF              libsbml.dll+049071 (EventAssignment_createWith+001631)
[ 12] 000000003CB1C1D7              libsbml.dll+049623 (SBML_formulaToString+000039)
[ 18] 000000003CB2C154              libsbml.dll+115028 (Rule::setFormulaFromMath+000036)
[ 20] 0000000001751913              libmx.dll+137491 (mxCheckMN_700+000723)
[ 25] 000000003CB1E7B2              libsbml.dll+059314 (KineticLaw_getFormula+000018)
[ 37] 0000000035727749              TranslateSBML.mexw64+030537 (mexFunction+009353)
于 2009-02-25T18:06:29.250 回答
0

为了回应 bk1e 对该问题的评论 - 不幸的是,我需要 2.0.2 版才能与 COBRA 工具箱一起使用,它不适用于较新的版本 3。所以,我现在坚持使用这个旧版本。

我也在尝试调试时遇到了一些问题 - 我正在构建一个 .dll,因此除了重新编译 xerces 以确保它在 MSVC++ 中具有相同的调试设置之外,我还需要附加到 Matlab 进程来进行调试- 对于我在这种环境中的有限经验来说,这是一个相当大的飞跃,而且我还没有深入研究它。

我曾希望缓冲区分配或扩展存在一些明显的语法问题。不过,看起来我还要忍受几天的痛苦。

于 2009-02-26T18:25:56.027 回答
0

让我们假设 safe_malloc 和 safe_realloc 做一些明智的事情,比如当它们无法获得请求的内存时中止程序。这样您的程序就不会继续使用无效指针执行。

现在让我们看看与所需容量相比,StringBuffer_ensureCapacity 将缓冲区增长到多大。这不是一个错误的错误。这是一个二分之一的错误。

你的程序是如何在 x32 中工作的,我猜不出来。

于 2009-02-26T04:26:19.523 回答
0

似乎它是否在任何 StringBuffer_* 函数中,那将在堆栈跟踪中。我不同意 _ensureCapacity 和 _grow 的实现方式。这些函数都不会检查 realloc 是否有效。realloc 失败肯定会导致段错误。我会在 _ensureCapacity 之后插入一个空检查。使用 _ensureCapacity 和 _grow 的方式,似乎可能会出现一个错误。如果您在 Windows 上运行,则 64 位和 32 位系统可能具有不同的页面保护机制,从而导致其失败。(在页面保护较弱的系统上,您经常会遇到 malloc'ed 内存中的一个错误。)

于 2009-02-26T03:48:52.733 回答
0

问题可能几乎是任何事情。诚然,它可能是 strncpy 做了坏事,但最有可能的是,它只是传递了一个错误的指针。这可能来自任何地方。段错误(或 Windows 中的访问冲突)仅仅意味着应用程序试图读取或写入它没有访问权限的地址。所以真正的问题是,这个地址是从哪里来的?试图跟随指针的函数可能没问题。但是它从其他地方传递了一个错误的指针。大概。

不幸的是,在最好的情况下,调试 C 代码并非易事。如果代码不是您自己的,那并不容易。:)

于 2009-02-25T16:07:42.467 回答
0

unsigned long不是用于 Windows 中 64 位计算机上的大小的安全类型。与 Linux 不同,Windowslong在 32 位和 64 位架构上都定义为 32 位。因此,如果要附加到的缓冲区的大小超过 4 GB(或者如果您尝试附加长度大于 4GB 的字符串),则需要将unsigned long类型声明更改为size_t(在 64 位架构上为 64 位,在所有操作系统中)。

但是,如果您只读取 1.5 MB 的文件,我看不出您如何让 StringBuffer 的大小超过 4 GB,所以这可能是一条死胡同。

于 2011-03-10T19:24:03.397 回答