我有下面的C++代码,它发出如下所示的编译警告。
案例1:
char temp_buffer[80];
double **data;
....
sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), **data);
汇编警告:
extension.C:1031:41: warning: field precision specifier '.*' expects argument of type 'int', but argument 3 has type 'long unsigned int' [-Wformat=]
1031 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), **data);
| ~~^~ ~~~~~~~~~~~~~~~~~~~
| | |
| int long unsigned int
我正在通过将sizeof
类型转换为int来修复警告,如下所示。
案例2:
char temp_buffer[80];
double **data;
...
sprintf(temp_buffer, "%.*g", (int)sizeof(temp_buffer), **data);
但现在我收到了以下警告:
extension/extension.C:1031:39: warning: \u2018%.*g\u2019 directive writing between 1 and 87 bytes into a region of size 80 [-Wformat-overflow=]
1031 | sprintf(temp_buffer, "%.*g", (int)sizeof(temp_buffer), **data);
| ^~~~
extension/extension.C:1031:38: note: assuming directive output of 86 bytes
1031 | sprintf(temp_buffer, "%.*g", (int)sizeof(temp_buffer), **data);
| ^~~~~~
In file included from /usr/include/stdio.h:873,
from /opt/python/python-3.9/include/python3.9/Python.h:25,
from /codemill/dhamodha/projects/base/src/lib/despython/desnumpyinit.h:19,
from extension/extension.C:8:
/usr/include/bits/stdio2.h:36:34: note: \u2018__builtin___sprintf_chk\u2019 output between 2 and 88 bytes into a destination of size 80
36 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37 | __bos (__s), __fmt, __va_arg_pack ());
我的假设是,“*”在精度上内部强制long unsigned int
到int
。但是如果是这样的话,如果我引入sizeof
类型转换,那么新的警告就不会发生了。
有人能帮我弄明白怎么回事并解决警告吗?
案例3:
假设我将temp_buffer
修改为大小为8
,那么我会看到两个警告:
extension/extension.C:1033:20: warning: \u2018%.*g\u2019 directive writing between 1 and 310 bytes into a region of size 8 [-Wformat-overflow=]
1033 | sprintf(temp_b, "%.*g", sizeof(temp_buffer), **data);
| ^~~~
extension/extension.C:1033:19: note: assuming directive output of 12 bytes
1033 | sprintf(temp_b, "%.*g", sizeof(temp_buffer), **data);
| ^~~~~~
In file included from /usr/include/stdio.h:873,
from /opt/python/python-3.9/include/python3.9/Python.h:25,
from /codemill/dhamodha/projects/base/src/lib/despython/desnumpyinit.h:19,
from extension/extension.C:8:
/usr/include/bits/stdio2.h:36:34: note: \u2018__builtin___sprintf_chk\u2019 output between 2 and 311 bytes into a destination of size 8
36 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37 | __bos (__s), __fmt, __va_arg_pack ());
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
发布于 2022-04-29 12:19:06
您可以在使用前指定宽度:
int width = sizeof(temp_buffer);
sprintf(temp_buffer, "%.*g", width, **data);
这一警告将消失,但这并不能解决根本问题。如果您想避免潜在的缓冲区溢出,一个合理的选项是使用正确的大小:
sprintf(temp_buffer, "%.*g", (int)(sizeof(temp_buffer)) - 8, **data);
在您的原始代码中,您的意思是您希望小数位数是缓冲区的大小,这就没有留下符号、科学符号和nul字节的空间。您需要传递一个足够小的十进制部件大小,以便所有这些都能够满足目标缓冲区的要求。
尽管编译器提到了7个字节,但我相信8个字节更合适,2个用于符号,1个用于点,1个用于e
,3个用于指数,1个用于nul字节。
虽然我认为有一个足够大到有最长可能表示的缓冲区可能更安全,但考虑到双可能高达758字节的最长可能表示(尽管并非所有编译器都是这样),一个可以占用800个字节的缓冲区将能够处理这个问题,假设上面的字节计数与gcc 11.3的编译一致。
因此,正确的方法是使用安全得多的snprintf
,它允许将最大缓冲区大小作为参数传递:
snprintf(buffer, sizeof buffer, "%.*g", (int)sizeof buffer - 8, **data);
对于不同的用例,该行为未定义。没有强制转换,因为您没有将正确的参数类型传递给...
参数第三参数 (在大多数情况下,unsigned long
的大小大于int
),因为编译器没有被授权发出任何警告。它确实可以被认为是礼貌的,例如,clang
没有发出警告,按照上面链接的未定义行为的标准定义,这是非常好的。
要知道为什么在某些情况下有两种不同的警告,而在其他情况下只有一种是编译器特有的,我认为试图找出原因是推测性的。
关于警告中发出的溢出值,我感到很好奇,并提出了一个问题,答案是明确的:
https://stackoverflow.com/questions/72057715
复制相似问题