返回语句不会在c中执行

Nat*_*lly 3 c return intel icc

所以,我有一个奇怪的案例,并不能完全弄清楚我做错了什么.这是场景:

我写了一个创建器函数,它应该返回一个指向函数的指针.为了用数据填充结构,我读了一个文本文件.根据我用作输入的文本文件,错误会发生或不会发生.(对于具有~4000行的文本文件而言,错误发生,对于具有~200的文件,如果这会产生差异,则会发生错误).奇怪的是代码执行到return语句之前.但它不会返回但只是挂起.没有错误,没有编译器警告(英特尔编译器).我想知道是否有人经历过类似的事情或有什么可能出错的想法.

下面的代码被简化以说明问题.实际的代码有点复杂,因为我使用Schreiner的方法来玩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;
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 10

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寄存器)未初始化,因此argcmain调用时仍然保持不变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
<source>(21): warning #592: variable "myObject" is used before its value is set
    myObject = Somestruct_ctor(myObject);
                               ^
Run Code Online (Sandbox Code Playgroud)

如何启用icc/icpc警告?有一些信息.-Wall与gcc相比,icc的方式非常简约.所以也许-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.
Run Code Online (Sandbox Code Playgroud)

我通过在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
}
Run Code Online (Sandbox Code Playgroud)

对象需要住在某个地方.我们可以通过自动(本地),静态(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
}
Run Code Online (Sandbox Code Playgroud)