使用 ICC 编译的代码使用 -O2,关于 strtok 的警告和使用 -O1 的崩溃

Code compiled with ICC works with -O2, warning about strtok and a crash with -O1

我有一个文本文件,每行包含三列数据。前两个数字是整数,最后一个是双精度数,即

1 2 3.45
4 42 3.45
... and so forth...

我正在使用以下 C 代码从文件中读取仅第一行

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    long int m, n;
    double val;
    FILE* f = fopen("input.txt", "r");
    char line[1024]; 
    char* pch;   
    fgets(line, sizeof(line), f);
    pch = strtok(line, " \t"); //** warning
    n = strtol(pch, NULL,10);
    pch = strtok(NULL, " \t");  //** warning
    m = strtol(pch, NULL,10);
    pch = strtok(NULL, " \t"); //** warning
    val = strtod(pch, NULL);
    ...
}

但是,当我尝试使用 -std=c89 -Wall -Wextra -O1 开关编译代码时,每个 strtok 都会收到以下 warning 消息并且程序崩溃分段错误:

<source>(9): warning #556: a value of type "int" cannot be assigned to an entity
of type "char *"

      pch = strtok(line, " \t");
          ^
...
Compiler returned: 0

但是当我尝试 -O2-O3 开关时,根本没有任何警告,我的代码也没有崩溃!

我正在使用 Intel 2019 编译器和 Linux OS。

如果有人能帮助我解决这个问题,我将不胜感激。

我忘了插入 #include <string.h> 。谢谢大家!

原因是您忘记了 #include <string.h> 定义 strtok 的原型!

这个 bug 碰巧在没有通知的情况下通过了,只是因为你明确地使用了 C89 模式 因为 C89 允许隐式函数声明(其中未声明的函数被假定为 return 一个 int) 因为英特尔 C 编译器有问题!


我已将代码简化为:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    FILE* f = fopen("foo", "r");
    char line[1024]; 
    char* pch;   
    fgets(line, sizeof(line), f);
    pch = strtok(line, " \t");
}

compiled with -std=c89 and -O1 ICC 报告时

<source>(9): warning #556: a value of type "int" cannot be assigned to an entity 
of type "char *"

      pch = strtok(line, " \t");
          ^

如果compiled with -O2 the warning is gone! But this does not comply to C89 3.3.16.1 Simple assignment表示

Constraints

One of the following shall hold: [42]

  • the left operand has qualified or unqualified arithmetic type and the right has arithmetic type;

  • the left operand has a qualified or unqualified version of a structure or union type compatible with the type of the right;

  • both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;

  • one operand is a pointer to an object or incomplete type and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right; or

  • the left operand is a pointer and the right is a null pointer constant.

None 5 个项目符号匹配。由于违反了约束,兼容的编译器必须发出诊断消息,ICC 不会


但是,如果您使用了 -std=c11 模式,even on -O2 level 编译器将为真正的罪魁祸首输出诊断信息:

<source>(9): warning #266: function "strtok" declared implicitly

      pch = strtok(line, " \t");
            ^

Compiler returned: 0

在缺少 strtok 的现有声明的情况下,编译器使用 C89 规则进行隐式函数声明,并隐式假定该函数将被声明为

int strtok();

但这违反了 2011 修订版,该修订版不再具有 隐式函数声明,因此会输出一条诊断消息。


最后应该注意的是 ICC 诊断真的非常糟糕。如果您使用 -std=c89 -O2 -Wall -Wextra 作为我的程序摘录,您 still receive no warnings whatsoever!

主要要点是:

  • 永远不要使用 C89 模式。它是 30 岁。它与 Windows 2.1xMSDOS 4.0Mac 系统 6。你也不会使用它们。 请注意,即使是晚至 ICC 16.0.3 的版本似乎也默认为 C89 模式。

  • ICC 不是符合标准的 C 编译器。它在诊断方面实际上 不好 。它接受 -Wall 作为 "-Wno-more" 的同义词是不可接受的。

  • 因此始终使用其他编译器进行开发,ICC 仅用于测试代码构建,前提是它明显更快。