问题描述
因此,我有一个奇怪的案例,无法完全弄清楚我做错了什么.这是场景:
So, I have a curious case and can't quite figure out what I've done wrong. Here's the scenario:
我写了一个创建函数,应该返回一个指向函数的指针.为了用数据填充结构,我读取了一个文本文件.根据我用作输入的文本文件,是发生错误还是不发生错误. (该错误发生在具有〜4000行的文本文件中,而不是具有〜200行的文件,如果有所不同的话).奇怪的是,代码一直执行到return
语句之前.但是它然后不返回而只是挂起.没有错误,没有编译器警告(intel编译器).我想知道是否有人经历过类似的事情或知道什么地方可能出问题了.
I have written a creator function that should return a pointer to a function. To fill the structure with data, I read in a text file. Depending on what text file I use as input, the error either occurs or it doesn't occur. (The error occurs for a text file with ~4000 lines and not for a file with ~200 if that makes a difference). The strange thing is that the code executes until right before the return
statement. But it then does not return but just hangs. No errors, no compiler warnings (intel compiler). I wonder if anyone has experienced something similar or has an idea what could be going wrong.
下面的代码被简化以说明该问题.由于我正在使用Schreiner的Approach来处理C语言中的对象,所以实际的代码稍微复杂一些.
The code below is simplified to illustrate the problem. The actual code is somewhat more complex since I'm using Schreiner's Approach to play with objects in C.
struct Somestruct {
int A;
int B;
int C;
}
static void *Somestruct_ctor(void *_self) {
struct Somestruct *self = _self;
fillDataFromFile(self);
printf("This line gets executed.\n");
return self; // <- this one doesn't
}
int main(int argc, char *argv[]) {
void *myObject;
myObject = Somestruct_ctor(myObject);
printf("The code does NOT get until here\n");
return 0;
}
推荐答案
void * myObject;
未初始化,并且不指向有效存储.读取其值(将其作为arg按值传递给Somestruct_ctor(myObject)
)是不确定的行为.
void * myObject;
is uninitialized, and not pointing at valid storage. Reading its value (to pass it as an arg by value to Somestruct_ctor(myObject)
) is undefined behaviour.
您的代码并不总是崩溃的事实告诉我们,在ICC的代码生成中,它恰好指向有效的某个地方,可能是堆栈中的某个地方.对于更大的文件,我们可能会出现缓冲区溢出,该溢出会覆盖局部变量和/或返回地址,并最终陷入无限循环. 令人惊奇的是,由于意外发生,它仍然没有崩溃.(在ICC的x86-64 asm中,禁用了优化功能,它只是加载了一些未初始化的堆栈内存作为.)
The fact that your code doesn't always crash tells us that in ICC's code-gen it happens to be pointing somewhere valid, probably somewhere on the stack. With a larger file, we presumably get a buffer overflow that overwrites a local variable and/or a return address and ends up in an infinite loop. It's pretty amazing that this managed to still not crash given that it happened by accident. (In the x86-64 asm from ICC with optimization disabled, it just loads some uninitialized stack memory as an arg for Somestruct_ctor
.)
或者它可能是指向stdio数据结构的指针,该指针是在main
之前的stdio初始化中遗留下来的.也许让fillDataFromFile
遍历FILE *stdout
指向的所有数据(例如),使它处于锁定"状态,所以您的单线程被困在等待其他解锁互斥锁的东西. 如果您知道asm,则可能单步执行printf
内的无限循环或死锁"并准确了解发生了什么.
Or maybe its a pointer to a stdio data structure, left over from init of stdio before main
. Perhaps having fillDataFromFile
scribble all over the data that FILE *stdout
points to (for example) left it in a "locked" state, so your single thread is stuck waiting for something else to unlock a mutex. If you know asm, it might be interesting to single-step the infinite loop or "deadlock" inside printf
and see exactly what happened.
如果使用gcc -O3
进行编译,则编译器会将fillDataFromFile
的寄存器清零(在内联Somestruct_ctor
之后),因此它将传递NULL指针.假设该函数取消了指针的引用,那可能总是会崩溃.
If you compile with gcc -O3
, the compiler zeros a register as an arg for fillDataFromFile
(after inlining Somestruct_ctor
), so it's passing a NULL pointer. That would presumably crash always, assuming the function dereferences the pointer.
clang选择保留未初始化的rdi
(x86-64系统V中第一个通过conventino的arg-pass寄存器),因此当main
调用fillDataFromFile
时,它仍然保持argc
.那也会可靠地崩溃.
clang chooses to leave rdi
(the first arg-passing register in the x86-64 System V calling conventino) uninitialized, so it still holds argc
when main
calls fillDataFromFile
. That would also reliably crash.
所有主要的x86编译器(gcc,clang,MSVC,ICC)都有此警告,但默认情况下并非在所有编译器中它们都处于启用状态(仅在MSVC中).可能是因为在某些情况下,有时编译器无法确定var是否未初始化.在这种情况下,可以100%肯定使用了未初始化的代码,但是如果init和use位于不同的if()
块中,则编译器可能无法证明使用仅在init发生的情况下发生.
All the major x86 compilers (gcc, clang, MSVC, ICC) have warnings for this, but they aren't on by default in all compilers (only in MSVC). Probably because there can be cases where the compiler isn't sure about a var being uninitialized if there's some conditional stuff. In this case it's 100% certain that it's definitely used uninitialized, but if the init and the use were inside different if()
blocks, the compiler might not be able to prove that the use only happened if the init happened.
使用clang和gcc时,通常应使用-Wall
并使所有警告静音.
With clang and gcc, you should usually use -Wall
and silence all the warnings.
使用ICC,-diag-enable:warn
更靠近gcc -Wall
. (ICC的-Wall
不会启用此非常重要的警告.不要误以为您已使用icc -Wall
启用了所有重要的警告.)
With ICC, -diag-enable:warn
is closer to gcc -Wall
. (ICC's -Wall
doesn't enable this very important warning. Don't be fooled into thinking you've enabled all important warnings with icc -Wall
.)
# from icc -diag-enable:warn on your code
<source>(21): warning #592: variable "myObject" is used before its value is set
myObject = Somestruct_ctor(myObject);
^
如何打开icc/icpc警告?有一些信息.与gcc相比,icc的-Wall
极小.因此,-Wall -Wextra
可能对icc很有用.建议将-w2
或-w3
作为可能有用的警告级别.
how to turn on icc/icpc warnings? has some info. It ways that icc's -Wall
is very minimalisitc compared to gcc's. So maybe -Wall -Wextra
would be useful with icc. It recommends -w2
or -w3
as potentially-useful warning levels.
在这种情况下,C语通常具有最好的警告:
Clang usually has the nicest warnings, in this case:
<source>:21:30: warning: variable 'myObject' is uninitialized when used here [-Wuninitialized]
myObject = Somestruct_ctor(myObject);
^~~~~~~~
<source>:19:18: note: initialize the variable 'myObject' to silence this warning
void * myObject;
^
= NULL
1 warning generated.
通过编译您的源文件在Godbolt编译器浏览器上(修复后语法错误:在结构后缺少分号,并且将Struct
关键字大写.)-xc
告诉Godbolt上的C ++编译器将其编译为C.
I got the above outputs by compiling your source on the Godbolt compiler explorer (after fixing the syntax errors: missing semicolon after the struct, and the capitalization of the Struct
keyword.) -xc
tells the C++ compilers on Godbolt to compile as C.
事实证明,您无需启用icc和gcc的优化即可注意到此错误.某些警告仅在启用优化的情况下起作用,在这种情况下,编译器将对程序逻辑进行更多分析,并且可以注意到更多警告,但即使在-O0
处,它们也会跟踪未初始化的内容.
It turns out that you don't need to enable optimization for icc and gcc to notice this error. Some warnings only work with optimization enabled, where the compiler does more analysis of program logic and can notice more, but they track uninitialized even at -O0
.
// C
int main(void){
struct Somestruct myObject; // automatic storage for the object value
Somestruct_ctor(&myObject); // pass a pointer to that storage
}
该对象需要居住在某个地方.我们可以通过自动(本地),静态(static
本地或全局)或动态存储(malloc
)为其留出空间.
The object needs to live somewhere. We can get space for it with automatic (a local), static (a static
local, or a global), or dynamic storage (malloc
).
如果struct Somestruct
具有在struct/class定义中声明的C ++默认构造函数,则自动存储+调用构造函数等效于C ++.
Automatic storage + calling the constructor is equivalent to C++ like this, if struct Somestruct
has a C++ default constructor declared in the struct/class definition.
// C++
int main(void){
Somestruct myObject; // calls the default constructor, if there is one
// destructor will be called at some point when myObject goes out of scope
}
这篇关于Return语句未在C中执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!