c - 使用 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
但是当我尝试使用-O2
or-O3
开关时,根本没有任何警告,而且我的代码可以正常工作而不会崩溃!
我正在使用 Intel 2019 编译器和 Linux 操作系统。
如果有人可以帮助我解决这个问题,我真的很感激。
解决方案
原因是你忘记#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.1x和MSDOS 4.0和Mac System 6同龄。你也不会使用它们。请注意,即使是晚于 ICC 16.0.3 的版本似乎也默认为 C89 模式。
ICC 不是符合标准的 C 编译器。它实际上在诊断方面很糟糕。它接受
-Wall
作为“-Wno-more
”的同义词是不可接受的。因此,始终使用其他编译器进行开发,并且 ICC 仅用于测试代码构建(如果它明显更快)。
推荐阅读
- c++ - 为什么定义 C++ 结构的成员函数的顺序不重要?
- python - 如何快速比较两个文本文件并获得唯一行?
- nginx - nginx 根本不监听任何端口
- android-studio - 尝试保存时出现空指针异常
- android - Android Gradle Plugin 4.1.0 自动授予位置权限
- python - 将值加在一起并将它们分配给特定的键?
- android - Flutter剪贴板在android 10中返回null
- git - Git - 在跟踪旧远程时将子目录提取到新存储库
- javascript - Vue 3 - 延迟加载图像组件
- mysql - 幽灵/复制表比原始表大很多倍