使用sprintf时奇怪的警告。

发布于 2025-01-25 03:56:14 字数 1749 浏览 2 评论 0原文

对于以下代码:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

§普罗旺斯的薰衣草 2025-02-01 03:56:14

代码中有多个问题:

  • 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 an int value for the * place holder and you pass a size_t which may have a different size and representation.
  • the call may have undefined behavior because you request a precision that is the length of the destination array, which may produce output that exceeds said length.

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:

  • for the value 25.3, the exact representation of the closest IEEE 754 number is 25.300000000000000710542735760100185871124267578125, requiring 52 bytes.
  • the output for the largest number via printf("%.1000g", -0x1.fffffffffffffp+1023) has 310 characters, thus requiring 311 bytes, which seems to be the reason for the 2 to 311 bytes.
  • yet a %.*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 is 8 (a non trivial optimisation) and determines that the output can be as small as 2 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.

忆梦 2025-02-01 03:56:14

311字节值来自哪里?

编译器很困惑。

“%

。编译器正在做出错误的预测,即打印-dbl_max将使用310 + 1个字符,期望>%g切换到Extreme Value 的美“%g”,有限的输出。如果它不切换到指数符号,那么极端输出将就像“%”。*f”,(int)0

int main(void) {
  char buffer[1000];
  char temp_buffer[8];
  int len = sprintf(buffer, "%.*g", (int) sizeof(temp_buffer), -DBL_MAX);
  printf("%d <%s>\n", len, buffer);
  len = sprintf(buffer, "%.*f", (int) 0, -DBL_MAX);
  printf("%d <%s>\n", len, buffer);
}

输出

15 <-1.7976931e+308>
310 <-179769313486231570814527423731704356798070565449239578069709514447683386590106403234437238318580897337933920052621128987133479958537240295444402082043505598186819583097828779632178833278408753356733764414284292236482024122876814115690851853178733231033249466834451356736736928870307247739983885979597249860971>

*1

1 1 1   8-1   1 1  3 --> 15
- d . ddddddd e - eee

Where does that 311 bytes value comes from?

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.

int main(void) {
  char buffer[1000];
  char temp_buffer[8];
  int len = sprintf(buffer, "%.*g", (int) sizeof(temp_buffer), -DBL_MAX);
  printf("%d <%s>\n", len, buffer);
  len = sprintf(buffer, "%.*f", (int) 0, -DBL_MAX);
  printf("%d <%s>\n", len, buffer);
}

Output

15 <-1.7976931e+308>
310 <-179769313486231570814527423731704356798070565449239578069709514447683386590106403234437238318580897337933920052621128987133479958537240295444402082043505598186819583097828779632178833278408753356733764414284292236482024122876814115690851853178733231033249466834451356736736928870307247739983885979597249860971>

*1

1 1 1   8-1   1 1  3 --> 15
- d . ddddddd e - eee
狼亦尘 2025-02-01 03:56:14

基本上,它试图告诉您的是,最终有可能获得比存储它们的空间更多的数字。如果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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文