void * myObject; 未初始化,并且未指向有效存储。 读取其值(将其作为 arg 按值传递给 Somestruct_ctor(myObject))是未定义的行为。
您的代码并不总是崩溃的事实告诉我们,在 ICC 的代码生成中,它恰好指向某个有效的位置,可能是堆栈上的某个位置。对于较大的文件,我们可能会遇到缓冲区溢出,覆盖局部变量和/或返回地址并最终进入无限循环。 令人惊讶的是,由于它是偶然发生的,它仍然没有崩溃。(在禁用优化的 ICC 的 x86-64 asm 中,它只是加载一些未初始化的堆栈内存作为 @987654325 的 arg @.)
或者它可能是一个指向 stdio 数据结构的指针,在main 之前从 stdio 的 init 遗留下来。也许让fillDataFromFile 乱涂乱画FILE *stdout 指向的数据(例如)使其处于“锁定”状态,因此您的单线程被卡住,等待其他东西解锁互斥锁。 如果您了解 asm,单步执行printf 中的无限循环或“死锁”并查看究竟发生了什么可能会很有趣。
如果您使用gcc -O3 编译,编译器会将寄存器归零作为fillDataFromFile 的arg(在内联Somestruct_ctor 之后),因此它传递了一个NULL 指针。假设函数取消引用指针,这可能总是会崩溃。
clang 选择不初始化 rdi(x86-64 System V 调用 conventino 中的第一个 arg 传递寄存器),因此当 main 调用 fillDataFromFile 时它仍然保持 argc。这也会可靠地崩溃。
您忘记启用编译器警告。
所有主要的 x86 编译器(gcc、clang、MSVC、ICC)都对此有警告,但并非所有编译器都默认启用(仅在 MSVC 中)。可能是因为在某些情况下,如果有一些条件内容,编译器可能不确定 var 是否未初始化。在这种情况下,可以 100% 确定它肯定是在未初始化的情况下使用的,但如果 init 和 use 位于不同的 if() 块内,编译器可能无法证明仅在 init 发生时才发生使用。
使用 clang 和 gcc,您通常应该使用 -Wall 并将所有警告静音。
使用 ICC,-diag-enable:warn 更接近 gcc -Wall。 (ICC 的-Wall 没有启用这个非常重要的警告。不要误以为你已经用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);
^
how to turn on icc/icpc warnings? 有一些信息。与 gcc 相比,icc 的 -Wall 非常简单。所以也许-Wall -Wextra 对 icc 有用。它建议将-w2 或-w3 作为可能有用的警告级别。
Clang 通常有最好的警告,在这种情况下:
<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.
我通过编译您的源代码on the Godbolt compiler explorer 得到了上述输出(在修复语法错误后:结构后缺少分号,Struct 关键字的大写。)-xc 告诉 Godbolt 上的 C++ 编译器进行编译作为 C.
事实证明,您无需启用 icc 和 gcc 优化即可注意到此错误。一些警告仅在启用优化的情况下起作用,编译器对程序逻辑进行更多分析并且可以注意到更多,但即使在 -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)为其获取空间。
如果struct Somestruct在struct/class定义中声明了一个C++默认构造函数,那么自动存储+调用构造函数就相当于C++这样。
// 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
}