我很缺乏使用C ++的经验,但我正在尝试在64位XP平台上为matlab编译SBML toolbox版本2.0.2。 SBML工具箱取决于Xerces 2.8和libsbml 2.3.5。
我已经能够在32位机器上构建和编译工具箱,并且在我测试时它可以工作。但是,在64位计算机(这是一个巨大的PITA!)上重建它之后,当我尝试用它读取长.xml文件时,我遇到了分段错误。
我怀疑问题是由pointer addresses issues造成的。
分段故障的堆栈跟踪以:
开头[ 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;
}
ensureCapacity看起来像这样:
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 ++,特别是对指针和内存寻址感到困惑,所以我很可能不知道你在谈论什么 - 我需要一些手持。
另外,我将我的构建过程的详细信息放在网上here,以防其他人正在尝试使用Microsoft Visual C ++ Express Edition为64位系统编译C ++。
提前致谢!
-Ben
答案 0 :(得分:1)
尝试打印或使用调试器查看您获得的某些中间变量的值。在StringBuffer_append()O / P len中,在StringBuffer_ensureCapacity()中,在循环之前和循环中观察sb-&gt; capacity和c。看看这些数值是否有意义。
通过访问字符串末尾之外的数据可能会导致分段错误。
它在32位机器而不是64位O / S上工作的奇怪事实也是一个线索。两台机器的物理和页面文件内存大小是否相同?此外,在64位计算机中,内核空间可能比32位计算机大,并且占用了32位O / S的内存空间的用户部分中的一些可用内存空间。对于XML,整个文档必须适合内存。如果这是问题,可能有一些开关来设置大小。只有在使用非常大的字符串时才会出现导致问题的机器差异。如果字符串不是很大,那么库或实用程序方法可能会在64位环境中无法正常工作。
另外,如果您没有其他任何可以尝试的话,请使用简单/小的xml文件开始。
你在哪里初始化sb-&gt;长度。你的问题很可能是strncpy(),虽然我不知道为什么32bit - &gt; 64位O / S变化很重要。最好的选择是查看中间值,然后您的问题就会很明显。
答案 1 :(得分:1)
作为libsbml的开发人员之一,我偶然发现了这一点。这对你来说还是个问题吗?与此同时,我们发布了libsbml 5,分别有64位和32位版本以及改进的测试,请看看:
答案 2 :(得分:0)
这个问题几乎可以解决。是的,可能是strncpy做坏事,但最有可能的是,它只是传递了一个坏指针。哪个可能来自任何地方。段错误(或Windows中的访问冲突)仅表示应用程序尝试读取或写入其无权访问的地址。所以真正的问题是,该地址来自哪里?尝试跟随指针的函数可能没问题。但它传递了来自其他地方的坏指针。可能。
不幸的是,在最好的时候调试C代码并非易事。如果代码不是您自己的代码,则不会使代码更容易。 :)
答案 3 :(得分: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)
答案 4 :(得分:0)
看起来它是否在任何StringBuffer_ *函数中,它将在堆栈跟踪中。我不同意如何实现_ensureCapacity和_grow。这些函数都没有检查realloc是否有效。 Realloc失败肯定会导致段错误。我会在_ensureCapacity之后插入一个null的检查。使用_ensureCapacity和_grow的方式,似乎有可能得到一个一个错误。如果您在Windows上运行,则64位和32位系统可能具有不同的页面保护机制,导致其失败。 (在页面保护较弱的系统上,你经常会遇到malloc内存中的逐个错误。)
答案 5 :(得分:0)
让我们假设safe_malloc和safe_realloc做了一些明智的事情,比如在程序无法获取请求的内存时中止程序。这样你的程序就不会继续使用无效指针执行。
现在让我们看看与所需容量相比,StringBuffer_ensureCapacity增加了多大的缓冲区。这不是一个错误的错误。这是一个两个因素的错误。
你的程序是如何在x32中运行的,我无法猜测。
答案 6 :(得分:0)
回应bk1e对这个问题的评论 - 不幸的是,我需要2.0.2版本与COBRA工具箱一起使用,它不适用于较新的版本3.所以,我现在仍然坚持使用这个旧版本
我也试图调试一些墙 - 我正在构建一个.dll,所以除了重新编译xerces以确保它在MSVC ++中具有相同的调试设置之外,我还需要附加到Matlab进程到进行调试 - 对于我在这种环境中的有限经验来说,这是一个相当大的跳跃,我还没有深入研究它。
我原本希望缓冲区分配或扩展有一些明显的语法问题。但是看起来我已经忍受了几天的痛苦。
答案 7 :(得分:0)
unsigned long
不是用于Windows中64位计算机上的大小的安全类型。与Linux不同,Windows在32位和64位架构上将long
定义为32位。因此,如果附加的缓冲区大小超过4 GB(或者如果您尝试追加长度大于4 GB的字符串),则需要将unsigned long
类型声明更改为size_t
(在所有操作系统中,64位体系结构为64位)。
但是,如果您只读取一个1.5 MB的文件,我看不出你如何让StringBuffer的大小超过4 GB,所以这可能是一条死胡同。