return语句未在c

时间:2019-01-03 03:07:26

标签: c return intel icc

因此,我有一个奇怪的案例,无法完全弄清楚我做错了什么。这是场景:

我写了一个创建函数,应该返回一个指向函数的指针。为了用数据填充结构,我读取了一个文本文件。根据我用作输入的文本文件,是发生错误还是不发生错误。 (该错误发生在具有〜4000行的文本文件中,而不是具有〜200行的文件,如果有所不同的话)。奇怪的是,代码一直执行到return语句之前。但是它然后不返回而只是挂起。没有错误,没有编译器警告(intel编译器)。我想知道是否有人经历过类似的事情或知道什么地方可能出问题了。

下面的代码被简化以说明该问题。由于我使用Schreiner的Approach来处理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;
}

1 个答案:

答案 0 :(得分:10)

void * myObject;未初始化,并且未指向有效存储。读取其值(将其作为arg按值传递给Somestruct_ctor(myObject))是不确定的行为。

>

您的代码并不总是崩溃的事实告诉我们,在ICC的代码生成中,它恰好指向有效的某个地方,可能是堆栈中的某个地方。对于更大的文件,我们可能会出现缓冲区溢出,该溢出会覆盖局部变量和/或返回地址并最终陷入无限循环。 令人惊奇的是,由于意外发生,它仍然没有崩溃。(在ICC的x86-64 asm中,禁用了优化功能,它只是将一些未初始化的堆栈内存作为{{ 1}}。)

或者它是指向{@ {1}}之前的stdio初始化遗留的stdio数据结构的指针。也许让Somestruct_ctor指向main所指向的所有数据进行乱写(例如),使其处于“锁定”状态,因此您的单线程被困在等待其他解锁互斥量的操作。 如果您知道asm,那么在fillDataFromFile内单步执行无限循环或“死锁”并准确了解发生了什么可能很有趣。


如果使用FILE *stdout进行编译,则编译器会将printf的arg寄存器清零(在内联gcc -O3之后),因此它将传递NULL指针。假设该函数取消了指针的引用,那总是会崩溃。

clang选择保留未初始化的fillDataFromFile(x86-64 System V中调用conventino的第一个arg-pass寄存器),因此当Somestruct_ctor调用{{1时,它仍然保持rdi }}。那也会可靠地崩溃。


您忘记启用编译器警告。

所有主要的x86编译器(gcc,clang,MSVC,ICC)都有此警告,但默认情况下并非在所有编译器中它们都处于启用状态(仅在MSVC中)。可能是因为在某些情况下,有时编译器无法确定var是否未初始化。在这种情况下,可以100%确信绝对使用了未初始化的代码,但是如果init和使用位于不同的argc块中,则编译器可能无法证明使用仅在init发生的情况下发生。

使用clang和gcc时,通常应使用main并使所有警告静音。

使用ICC,fillDataFromFile更接近if() 。 (ICC的-Wall不会启用此非常重要的警告。请不要误以为您已使用-diag-enable:warn启用了所有重要的警告。)

gcc -Wall

how to turn on icc/icpc warnings?有一些信息。与gcc相比,icc的-Wall极小。因此,也许icc -Wall对于icc很有用。它建议使用 # 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); ^ -Wall作为可能有用的警告级别。


在这种情况下,C语通常具有最好的警告:

-Wall -Wextra

我通过编译您的源代码on the Godbolt compiler explorer得到了上述输出(修复了语法错误之后:结构体后缺少分号,并且将-w2关键字大写。)-w3告诉Godbolt上的C ++编译器可以编译为C。

事实证明,您无需为icc和gcc启用优化即可注意到此错误。某些警告仅在启用优化的情况下起作用,在这种情况下,编译器会对程序逻辑进行更多分析,并且可以注意到更多警告,但是即使在<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. ,它们也会跟踪未初始化的内容。


更有意义的构造函数代码:

Struct

该对象需要居住在某个地方。我们可以通过自动(本地),静态(-xc本地或全局)或动态存储(-O0)来获得空间。

如果// C int main(void){ struct Somestruct myObject; // automatic storage for the object value Somestruct_ctor(&myObject); // pass a pointer to that storage } 具有在struct / class定义中声明的C ++默认构造函数,则自动存储+调用构造函数等效于C ++。

static