我有下面的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没有发出警告,按照上面链接的未定义行为的标准定义,这是非常好的。
要知道为什么在某些情况下有两种不同的警告,而在其他情况下只有一种是编译器特有的,我认为试图找出原因是推测性的。
关于警告中发出的溢出值,我感到很好奇,并提出了一个问题,答案是明确的:
发布于 2022-05-01 06:37:04
有人能帮我弄明白怎么回事并解决警告吗?
使用sprintf(temp_buffer, "%.*g", precision, **data);,precision在指数显示的意义上控制数字的数量。
假设sprintf(temp_buffer, "%.*g", 5, 1234567e10);会形成"1.2346e+16“。
与其给出缓冲区大小的进动,不如考虑其他字符,如符号、小数点、'e‘和指数(对于普通double,可能是4个字符,如"-300")和空字符。在本例中为8。
#define G_OVERHEAD 8
sprintf(temp_buffer, "%#.*g", (int) sizeof temp_buffer - G_OVERHEAD, **data);这段代码风险很大,因为G_OVERHEAD可能更大。考虑确定最坏情况的启动代码,如以下所示。
int g_overhead = snprintf(0, 0, "%#.*g", 2, -DBL_TRUE_MIN) + 1 /*\0*/ - 2 /*Precision*/;
// Common result: 8 for -4.9e-324https://stackoverflow.com/questions/72057715
复制相似问题