c - gcc-11 不正确可能会在未初始化的情况下使用,这似乎很难避免
问题描述
我发现了一个特定的使用模式,它看起来完全没问题,以前没有编译器抱怨过。现在它使用 gcc-11 发出警告:下面是一个接近最小的示例。另存为t.c
并使用gcc-11 -O2 -Wall -c t.c
.
#include <stdlib.h>
#include <string.h>
extern void f(const char *s);
void
t(int len, char *d)
{ char tmp[500];
char *s, *o;
int i;
if ( len <= sizeof(tmp) )
s = tmp;
else if ( !(s=malloc(len)) )
return;
for(o=s,i=0; i < len; i++)
*o++ = d[i]+1;
f(s);
// i = strlen(s);
if ( s != tmp )
free(s);
}
编译结果:
gcc-11 -O2 -Wall -c t.c
t.c: In function ‘t’:
t.c:20:3: warning: ‘s’ may be used uninitialized [-Wmaybe-uninitialized]
20 | f(s);
| ^~~~
t.c:4:13: note: by argument 1 of type ‘const char *’ to ‘f’ declared here
4 | extern void f(const char *s);
| ^
t.c:20:3: warning: ‘tmp’ may be used uninitialized [-Wmaybe-uninitialized]
20 | f(s);
| ^~~~
t.c:4:13: note: by argument 1 of type ‘const char *’ to ‘f’ declared here
4 | extern void f(const char *s);
| ^
t.c:8:8: note: ‘tmp’ declared here
8 | { char tmp[500];
| ^~~
现在有一些观察
- 调用 strlen(s) 而不是 f(s) 不会导致警告。请注意,两者都接受
const char*
(签入/usr/include/string.h
) - 如果我删除 的
const
声明中的f()
,问题也会消失。 - 调用 using
f((const char*)s)
也无济于事。 - 像 in 一样初始化 s
char *s = NULL
也无济于事(无论如何我都不希望这样做,因为它隐藏了有关未初始化变量的正确警告)。
我了解到,声称某事是 GCC 错误通常被正确地证明是错误的。所以我首先在这里检查我缺少什么。
编辑由于我无法在评论中添加代码,因此这里是反驳部分声明的代码。这编译得很好:
extern void f(const char *__s);
void
t(int len, char *d)
{ char tmp[500];
char *s=tmp, *o;
int i;
for(o=s,i=0; i < len; i++)
*o++ = d[i]+1;
f(tmp);
}
解决方案
编译器是正确的,虽然英文措辞不完善。
假设len
总是积极的,解决方法是插入if (len <= 0) __builtin_unreachable();
函数。这告诉编译器len
始终为正,这意味着必须将一些数据写入调用s
之前指向的内存点。f
当编译器说“'s' may be used uninitialized”时,这并不意味着可以使用的值,s
而是它所指向的内容可以被使用,并且指向的内存没有被初始化。请注意,它s
被传递给一个接受 a 的函数const char *s
,这表明该函数不会修改数据s
点,因此希望它已经包含数据。C 标准并不严格要求这一点;只要未用 定义指向的内存const
,f
就可以将指针重新转换为其原始指针char *
并修改那里的数据,但参数声明的含义是它不会。
我们可以通过将函数的主体更改为:
char tmp[500];
f(tmp);
然后编译器抱怨“警告:'tmp'可能未初始化使用。” 很明显tmp
,传递给函数的 不是未初始化的;它将是数组的地址。所以编译器必须警告它是可能未初始化使用的数组的内容。
请注意,虽然从for(o=s,i=0; i < len; i++)
表面上开始的循环似乎初始化了指向的数据,但如果为零s
,它不会。len
并且由于s
被传递给f
without len
,f
因此无法知道其中没有任何内容s
(通过某些侧通道(例如使用外部对象)除外)。所以大概在每次调用f
中至少读取一些数据。s
这是一个较小的示例:
#include <stdlib.h>
extern void f(const char *s);
void t(int len)
{
char *s = malloc(len);
f(s);
free(s);
}
想必,len
总是积极的。要告诉 GCC,请在函数中插入这一行:
if (len <= 0) __builtin_unreachable();
这导致没有新的代码生成,但警告消失了。(实际上,生成的代码变小了,部分原因是编译器无需先测试就可以进入for
循环i < len
。)
推荐阅读
- c++ - 为什么需要双指针来更改 head 而不是链接列表中的其他地方
- android - Flutter Positioned Element 来flex吧,这个日志主要是什么?
- opencv - 如何在某些特定情况下修复 tesseract 无效输出?
- c# - c#和javascript正则表达式问题
- javascript - Angular 应用程序必须在新部署后清除缓存
- jquery - 将图像类复制到外部容器
- java - 检查 double/float 是否有超过 2 个十进制数
- linux - 如何在bash脚本中的IF函数中获取特定日期和时间
- aws-lambda - 通过回调从 lambda 获取响应并让 lambda 继续工作
- selenium - selenium ide从输入标签[值属性]中提取文本