首页 > 解决方案 > 使用 ICC 编译的代码使用 -O2,警告 strtok 和使用 -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并且程序因分段错误而崩溃:

<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

但是当我尝试使用-O2or-O3开关时,根本没有任何警告,而且我的代码可以正常工作而不会崩溃!

我正在使用 Intel 2019 编译器和 Linux 操作系统。

如果有人可以帮助我解决这个问题,我真的很感激。

标签: cicc

解决方案


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

这个错误恰好在没有通知的情况下通过,只是因为您显式使用了 C89 模式,并且因为 C89 允许隐式函数声明(假设未声明的函数返回一个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");
}

ICC 报告一起编译-std=c89-O1

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

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

如果编译带有-O2警告就没了!但这不符合C89 3.3.16.1 Simple assignment规定

约束

应满足下列条件之一:[42]

  • 左边的操作数有合格或不合格的算术类型,右边有算术类型;

  • 左操作数具有与右操作数兼容的结构或联合类型的限定或非限定版本;

  • 两个操作数都是指向兼容类型的限定或非限定版本的指针,并且左边指向的类型具有右边指向的类型的所有限定符;

  • 一个操作数是指向对象或不完整类型的指针,另一个是指向 void 的限定或非限定版本的指针,左侧指向的类型具有右侧指向的类型的所有限定符;或者

  • 左边的操作数是一个指针,右边是一个空指针常量。

5 个子弹中没有一个是匹配的。由于违反了约束,因此兼容的编译器必须发出诊断消息,而 ICC 不会这样做


但是,如果您使用了该-std=c11模式,即使在-O2级别上,编译器也会输出真正罪魁祸首的诊断信息:

<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我的程序摘录,您仍然不会收到任何警告

主要内容是:

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

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

  • 因此,始终使用其他编译器进行开发,并且 ICC 仅用于测试代码构建(如果它明显更快)。


推荐阅读