sprintf精度打字导致汇编警告
我有以下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 ());
我的假设是在内部的精确性启动长期无符号的int
to
int 。但是,如果我引入
sizeof
键入键盘,则不会发生新的警告。
有人可以帮助我了解发生了什么并解决警告吗?
案例3:
说我修改了 temp_buffer
size 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 ());
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以在使用前分配宽度:
警告会消失,但是这不会解决基本问题。如果要避免潜在的缓冲区溢出,一个合理的选择是使用正确的尺寸:
在原始代码中,您要说的是您想要小数位的数量是缓冲区的大小,而没有空间对于标志,科学符号和nul字节。您需要将十进制零件尺寸的尺寸足够小,以便所有这些零件尺寸都可以适合目标缓冲区。
即使编译器提到7个字节,我认为8个字节是最好的,其中2个字节为2,其中1个字节为1,为1个字节,1个字节,1个字节为1个字节,
e
为1个字节,指数为3,为指数为1,nul字节为1。尽管我认为具有足够大的缓冲区足以具有最长可能的表示形式,但鉴于双可以多达758个字节(尽管所有编译器可能并非如此),假设上述字节计数与编译一致,可以使用800个字节的缓冲区可以解决此问题使用 GCC 11.3 。
这一切都说明了这一点的正确方法是使用更安全的
snprintf
,它允许您将最大缓冲区大小传递给参数:对于不同的用例,行为是不确定的没有演员,因为您不通过正确的参数类型到
...
参数 assprintf第三参数
(在大多数情况下,
无符号长
的大小大于int
),因此,不要求编译器发出任何警告。它确实可以被视为礼貌,clang
,例如发出警告,根据上述未定义行为的标准定义,这一事实是完全良好的。要知道为什么在某些情况下有两个不同的警告,而在其他情况下,只有一个是特定于编译器的问题,我认为试图弄清楚为什么这样做是很投机的。
关于警告中发出的溢出值,我发现它很好奇,并问了一个问题,答案是阐明的:
You could assign the width before use:
The warning would go away, that however would not solve the underlying problem. If you want to avoid potential buffer overflow, a reasonable option would be to use the correct size:
In your original code what you're saying is that you want the number of decimal places to be the size of the buffer, that leaves no space for the signs, scientific notation and the nul byte. You need to pass a decimal part size small enough so that all of those can fit the the destination buffer.
Even though the compiler mentions 7 bytes I believe 8 bytes would be prefereable, 2 for the signs, 1 for the dot, 1 for the
e
, 3 for the exponent and 1 for the nul byte.Though I think having a buffer large enough to have the longest possible representation could be safer, given that the longest possible representation of a double can be as much as 758 bytes (though this may not be the case for all compilers), a buffer that can take 800 bytes would be able to deal with this, assuming the above byte count, consistent with the compilation with gcc 11.3.
That all being said the correct way to go about this is to use the much safer
snprintf
which allows you to pass the maximum buffer size as argument:As for your different use cases, the behavior is undefined without the cast because you do not pass the correct type of arguments to a
...
parameter as issprintf
third parameter (in most cases the size of anunsigned long
is larger thanint
), as such the compiler is not mandated to issue any warnings. The fact that it does can be considered a courtesy,clang
for example issues no warnings, which is perfectly fine as per the standard definition of undefined behavior linked above.To know the specific reason why in some cases there are the two different warnings and in others only one is a matter that is specific to the compiler, I consider it to be speculative to try to figure out why this is so.
Regarding the overflow values issued in the warnings, I found it curious and asked a question regarding it, the answers are elucidative:
使用
sprintf(temp_buffer,“%。
考虑
sprintf(temp_buffer,“%。*g”,5,1234567E10);
将形成“ 1.2346e+16”。而不是给出缓冲区大小的进动,不如说明其他字符,例如符号,小数点,“ e”和endent(对于常见
double
),可能是4个字符,例如“ -300 ”)和 null字符。在这种情况下8。此代码有风险,因为
g_overhead
可能更大。考虑确定最坏情况的启动代码,例如以下情况。With
sprintf(temp_buffer, "%.*g", precision, **data);
,precision
controls the number of digits in the significand of exponential display.Consider
sprintf(temp_buffer, "%.*g", 5, 1234567e10);
would form "1.2346e+16".Rather than give a precession that is the size of the buffer, account for other characters like the sign, decimal point, 'e', and exponent (for common
double
, might be 4 characters like "-300") and the null character. In this case 8.This code is risky as
G_OVERHEAD
may be larger. Consider startup code that determines worst case, such as the following.