作者:广告公司英子 | 来源:互联网 | 2022-11-22 20:20
1> Peter Cordes..: void * myObject;
是未初始化的,并没有指向有效的存储空间. 读取它的值(将值作为arg传递给Somestruct_ctor(myObject)
)是未定义的行为.
您的代码并不总是崩溃这一事实告诉我们,在ICC的代码中,它恰好指向某个有效的位置,可能位于堆栈的某个位置.对于更大的文件,我们可能会得到一个缓冲区溢出,它会覆盖局部变量和/或返回地址,最终会进入无限循环. 令人惊讶的是,由于它是偶然发生的,所以仍然没有崩溃. (在禁用优化的ICC的x86-64 asm中,它只是将一些未初始化的堆栈内存加载为arg for Somestruct_ctor
.)
或者它可能是指向stdio数据结构的指针,之前是从stdio的init遗留下来的main
.也许已经fillDataFromFile
遍布FILE *stdout
指向(例如)使其处于"锁定"状态的数据,因此您的单线程卡在等待其他东西解锁互斥锁. 如果你知道asm,那么在内部单步执行无限循环或"死锁" printf
并查看究竟发生了什么可能会很有趣.
如果你编译gcc -O3
,编译器将一个寄存器作为一个arg用于fillDataFromFile
(在内联之后Somestruct_ctor
),所以它传递一个NULL指针.假设函数取消引用指针,那么可能会崩溃.
clang选择离开rdi
(x86-64 System V中调用conventino的第一个arg-passing寄存器)未初始化,因此argc
在main
调用时仍然保持不变fillDataFromFile
.这也可能会崩溃.
您忘记启用编译器警告.
所有主要的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
(21): warning #592: variable "myObject" is used before its value is set
myObject = Somestruct_ctor(myObject);
^
如何启用icc/icpc警告?有一些信息.-Wall
与gcc相比,icc的方式非常简约 .所以也许-Wall -Wextra
对icc有用.它建议-w2
或-w3
作为潜在有用的警告级别.
Clang通常有最好的警告,在这种情况下:
:21:30: warning: variable 'myObject' is uninitialized when used here [-Wuninitialized]
myObject = Somestruct_ctor(myObject);
^~~~~~~~
:19:18: note: initialize the variable 'myObject' to silence this warning
void * myObject;
^
= NULL
1 warning generated.
我通过在Godbolt编译器资源管理器上编译你的源代码来获得上述输出(在修复语法错误之后:在结构之后缺少分号,以及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
}