运行时使用fscanf()出现"访问冲突"错误,但在调试时却没有。

3
这里可能发生了什么?
我得到的是
在这行的位置发生了未处理的异常:0x5081f508(msvcr100d.dll)中的myProgram.exe:0xC0000005:访问违规写入位置0x041e0010。
fscanf(fp, " %lf %lf %lf\n", &vertices[i].x, &vertices[i].y, &vertices[i].z );

当运行我的程序时,但是当我在调试模式下逐步执行它(Visual Studio 2010),一切都很顺利;fscanf()按预期读取文件。
实际上,异常是在input.c的这一行抛出的。
#else  /* _UNICODE */
       _FASSIGN( longone-1, (char*)pointer , pFloatStr, (char)decimal, _loc_update.GetLocaleT());
#endif  /* _UNICODE */

如果我没记错的话。而且我不知道这些关于UNICODE的评论是什么意思。这也是为什么我在这里包含它们的原因。

附加信息

调用堆栈:

 msvcr100d.dll!_fassign_l(int flag, char * argument, char * number, localeinfo_struct * plocinfo)  Line 258 + 0x6 bytes    C++

 >msvcr100d.dll!_input_l(_iobuf * stream, const unsigned char * format,    localeinfo_struct * plocinfo, char * arglist)  Line 1281 + 0x21 bytes    C++

 msvcr100d.dll!vfscanf(int (_iobuf *, const unsigned char *, localeinfo_struct *, char *)* inputfn, _iobuf * stream, const char * format, localeinfo_struct * plocinfo, char * arglist)  Line 61 + 0x13 bytes    C

 msvcr100d.dll!fscanf(_iobuf * stream, const char * format, ...)  Line 99 + 0x18 bytes    C

 myProgram.exe!main(int argc, char * * argv)  Line 166 + 0x49 bytes    C++

 myProgram.exe!__tmainCRTStartup()  Line 555 + 0x19 bytes    C

 myProgram.exe!mainCRTStartup()  Line 371    C

其他一些信息

这个程序是关于使用OpenGL进行着色的,而你在fscanf()调用中看到的vertices是一个这样的数组:

typedef struct _Vertex {
    double    x, y, z;
    int polygonsThisPartOf; // Number of polygons this vertex is a part of
    Point normal;
} Vertex;

在我的程序的第一个版本中,vertices是一个数组的数组,一切都运行得很好;在我修改代码以将vertices用作上述struct的数组后,这个异常开始出现。

数组的分配

    //                                              ˇ THIS is the mistake
vertices = (Vertex *) malloc(vcount * sizeof(Vertex *));
if (vertices == NULL) exit(-2);

vcount 正确。


1
你能将这个问题简化成一个最小测试案例吗?也就是说,能否编写一个只有10行代码的程序来展示这个问题? - Oliver Charlesworth
1
展示一下你如何初始化“vertices”。 - Ed S.
1
分配顶点数组的代码是什么样子的? - John Watts
好的,分配。对不起,这里来了。检查编辑。 - Bloke
哦,天啊。我真是个白痴。现在我明白了。该死。谢谢大家。通过试用你们的建议,我意识到了错误。 - Bloke
显示剩余2条评论
3个回答

1

1) 你的"fscanf()"语法看起来没问题。

2) "_UNICODE"消息(在你进入的MSVC内部)只是意味着你正在使用所有Win32代码的16位Unicode版本,这些代码期望16位Unicode格式字符串(而不是8位ASCII格式字符串)。

这是正常和预期的。如果你在Visual Studio中从源代码编译所有内容,这不应该是一个问题。

3) 我建议你把精力集中在确保你的数组元素"vertices[i]"已经成功分配上。

建议:

在你的"fscanf()"处设置断点,在调试器中查看变量之前调用fscanf。

此外,你可能想在fscanf之前添加以下内容,并在此调试行处设置断点:

 vertices[i].x = vertices[i].y = vertices[i].z = 0;

1
问题可能出现在代码中,甚至可能与您的fscanf调用无关,在某个内存位置上写入的字节数超过了其所能容纳的范围。当您以调试模式运行时,它会分配比必要更多的内存,因此通常不会在调试模式下看到错误,但是一个好的调试器应该会告诉您何时正在超出缓冲区长度的限制。

1
过去,当我遇到在调试版本中无法重现的错误时,几乎总是由于缓冲区溢出或指针错误引起的。
调试版本可能会稍微不同地构建堆栈;堆栈上可能有额外的东西,有时当你写入缓冲区末尾时,写入会进入调试信息而没有明显的错误发生。当然,这假设缓冲区是在堆栈上分配的。或者,如果缓冲区在堆中,则可能您的调试版本在堆上有额外的东西(例如附加字符串)。
因此,请检查您的变量i,确保它没有超出数组的索引范围。并检查您的变量fp,确保它指向某个合理的位置;如果它是通过指针算术确定的,请确保指针算术是正确的。
如果在调试版本中无法重现该错误,则应将您的发布版本工具仪器化以打印i的值、fp的值和其他一些内容(例如,如果fp是指向缓冲区内部的指针,则该缓冲区的当前地址和当前长度,以便您可以看到fp是否在缓冲区内)。

顺便说一下,我听说这种错误被称为“Heisenbug”。试图调试它会改变它的行为!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接