使用sprintf时奇怪的警告。
对于以下代码:
https://godbolt.org/z/wcgf9hes3
#include <stdio.h>
int main() {
char temp_buffer[8];
double val = 25.3;
sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
printf("%s", temp_buffer);
}
我在GCC中得到警告11.3带有-wall
标志:
<source>:8:29: warning: field precision specifier '.*' expects argument of type 'int', but argument 3 has type 'long unsigned int' [-Wformat=]
8 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
| ~~^~ ~~~~~~~~~~~~~~~~~~~
| | |
| int long unsigned int
<source>:8:27: warning: '%.*g' directive writing between 1 and 310 bytes into a region of size 8 [-Wformat-overflow=]
8 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
| ^~~~
<source>:8:26: note: assuming directive output of 12 bytes
8 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
| ^~~~~~
<source>:8:5: note: 'sprintf' output between 2 and 311 bytes into a destination of size 8
8 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
实际上,目标缓冲区的大小太小,无法存储给定尺寸参数的值,但是警告'Sprintf'输出在2到311个字节之间是什么进入大小8
的目的地? 311字节值从何而来?
如果我将小数位置的数量命名为int
, ie (int)sizeof(temp_buffer)
潜在的溢出数字急剧下降:
'sprintf' output between 2 and 16 bytes into a destination of size 8
For the following code:
https://godbolt.org/z/WcGf9hEs3
#include <stdio.h>
int main() {
char temp_buffer[8];
double val = 25.3;
sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
printf("%s", temp_buffer);
}
I get the warnings in gcc 11.3 with -Wall
flag:
<source>:8:29: warning: field precision specifier '.*' expects argument of type 'int', but argument 3 has type 'long unsigned int' [-Wformat=]
8 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
| ~~^~ ~~~~~~~~~~~~~~~~~~~
| | |
| int long unsigned int
<source>:8:27: warning: '%.*g' directive writing between 1 and 310 bytes into a region of size 8 [-Wformat-overflow=]
8 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
| ^~~~
<source>:8:26: note: assuming directive output of 12 bytes
8 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
| ^~~~~~
<source>:8:5: note: 'sprintf' output between 2 and 311 bytes into a destination of size 8
8 | sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), val);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In fact the size of the destination buffer is too small to store the value given the size argument, but what's with the warning 'sprintf' output between 2 and 311 bytes into a destination of size 8
? Where does that 311 bytes value come from?
If I cast the number of the decimal places to int
, i.e. (int)sizeof(temp_buffer)
the potential overflow numbers drop dramatically:
'sprintf' output between 2 and 16 bytes into a destination of size 8
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
代码中有多个问题:
sprintf
期望int
*
plote持有人的值,然后您通过size_t
可能具有不同的大小和表示形式。传递
sizeof(temp_buffer)
是一个错误,编译器似乎对实际参数值感到困惑,并且对精确值或要转换的数字没有特别的假设。然而,当他们记录输出时,它们似乎是错误的,可以是2到311个字节:25.3
,最接近IEEE 754数字的确切表示为25.30000000000000000000000000000710542735760100100110018718711241124242424242675788125 。
printf(“%。1000G”,-0x1.ffffffffffffp+1023)
具有310个字符,因此需要311个字节,这似乎是2的原因,到311字节
。%。*g
转换实际上可以产生超过311个字节:printf(“%。1000G”,-5E -324)
在MacOS和Linux上都产生758个字符。当您施放
sizeof(temp_buffer)
为(int)
时,编译器确定精度是8
(非琐事优化),并确定输出可以小于2
字节(一个数字和无效终结者),但不超过16(-
,digit,。
> 7个小数,, - , 程序员!
关于这种潜在的不确定行为的 在最坏的情况下,很难在所有情况下生产尽可能多的小数,并且可能需要多次尝试或复杂的后处理。
There are multiple issues in the code:
sprintf
expects anint
value for the*
place holder and you pass asize_t
which may have a different size and representation.Passing
sizeof(temp_buffer)
is an error, the compiler seems confused about the actual argument values and makes no particular assumption about the precision value or the number to convert. Yet they seem mistaken when they document the output can be 2 to 311 bytes:25.3
, the exact representation of the closest IEEE 754 number is25.300000000000000710542735760100185871124267578125
, requiring 52 bytes.printf("%.1000g", -0x1.fffffffffffffp+1023)
has 310 characters, thus requiring 311 bytes, which seems to be the reason for the2 to 311 bytes
.%.*g
conversion can actually produce more than 311 bytes:printf("%.1000g", -5e-324)
produces 758 characters on both macOS and linux.When you cast
sizeof(temp_buffer)
as(int)
, the compiler determines that the precision is8
(a non trivial optimisation) and determines that the output can be as small as2
bytes (a single digit and a null terminator) but no longer than 16 (-
, a digit,.
, 7 decimals,e
,-
and as many as 3 exponent digits plus a null terminator. That is still potentially too much for an 8 byte array.Good job for warning the programmer about this potential undefined behavior!
Use
snprintf()
, a larger array and pass(int)(sizeof(temp_buffer) - 9)
as the precision to get as many decimals as will fit in the worst case. It is difficult to produce as many decimals as will fit in all cases and may require multiple tries or complex postprocessing.编译器很困惑。
“%
。编译器正在做出错误的预测,即打印
-dbl_max
将使用310 + 1个字符,期望>%g
切换到Extreme Value的美“%g”
,有限的输出。如果它不切换到指数符号,那么极端输出将就像“%”。*f”,(int)0
。输出
*1
Compiler is confused.
"%.*g", 8
may output 15*1 + 1 (for the null character) characters, but not 311.I suspect the compiler is making the wrong prediction that printing
-DBL_MAX
would use 310 + 1 characters, expect for the fact that%g
switches to exponential notation for extreme values - that is the beauty of"%g"
, limited output. If it did not switch to exponential notation then that extreme output would be like"%.*f", (int) 0
.Output
*1
基本上,它试图告诉您的是,最终有可能获得比存储它们的空间更多的数字。如果Val真的很大,会发生什么?
Basically what it's trying to tell you is that it's possible to end up with more digits than there is space to store them. What happens if val is a really big number?