![](/img/trans.png)
[英]sprintf writing a blank string when used with %*.s format specifier
[英]Strange warning when calling sprintf with .* width specifier
對於以下代碼:
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);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
事實上,目標緩沖區的大小太小,無法存儲給定 size 參數的值,但是'sprintf' output between 2 and 311 bytes into a destination of size 8
什么? 那 311 字節的值從何而來?
如果我將小數位數轉換為int
,即(int)sizeof(temp_buffer)
,則潛在的溢出數會急劇下降:
'sprintf' output between 2 and 16 bytes into a destination of size 8
代碼中有多個問題:
sprintf
期望*
占位符的int
值,並且您傳遞一個size_t
,它可能具有不同的大小和表示形式。 傳遞sizeof(temp_buffer)
是一個錯誤,編譯器似乎對實際參數值感到困惑,並且對精度值或要轉換的數字沒有特別的假設。 然而,當他們記錄輸出可能是 2 到 311 個字節時,他們似乎弄錯了:
25.3
,最接近的 IEEE 754 數字的精確表示是25.300000000000000710542735760100185871124267578125
,需要 52 個字節。printf("%.1000g", -0x1.fffffffffffffp+1023)
輸出的最大數字有 310 個字符,因此需要 311 個字節,這似乎是2 to 311 bytes
的原因。%.*g
轉換實際上可以產生超過 311 個字節: printf("%.1000g", -5e-324)
在 macOS 和 linux 上產生 758 個字符。 當您將sizeof(temp_buffer)
為(int)
時,編譯器確定精度為8
(非平凡優化)並確定輸出可以小至2
個字節(單個數字和空終止符)但不再超過 16 ( -
,一個數字, .
,7 個小數, e
, -
以及多達 3 個指數數字加上一個空終止符。對於 8 字節數組來說,這仍然可能太多了。
很好地警告程序員這種潛在的未定義行為!
使用snprintf()
,一個更大的數組並傳遞(int)(sizeof(temp_buffer) - 9)
作為精度,以獲得盡可能多的小數,以適應最壞的情況。 很難產生適合所有情況的盡可能多的小數,並且可能需要多次嘗試或復雜的后處理。
那個 311 字節的值是從哪里來的?
編譯器很困惑。
"%.*g", 8
可能輸出 15 個*1 + 1(對於空字符)字符,但不能輸出 311。
我懷疑編譯器做出了錯誤的預測,即打印-DBL_MAX
將使用 310 + 1 個字符,期望%g
切換到極值的指數表示法 - 這就是"%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
基本上,它試圖告訴你的是,最終可能會得到比存儲空間更多的數字。 如果 val 是一個非常大的數字會怎樣?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.