對于以下代碼:
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
uj5u.com熱心網友回復:
代碼中有多個問題:
sprintf期望占位符的int值,并且您傳遞的 a可能具有不同的大小和表示形式。*size_t- 該呼叫可能具有未定義的行為,因為您請求的精度是目標陣列的長度,這可能會產生超過所述長度的輸出。
傳遞sizeof(temp_buffer)是一個錯誤,編譯器似乎對實際引數值感到困惑,并且對精度值或要轉換的數字沒有特別的假設。然而,當他們記錄輸出可能是 2 到 311 個位元組時,他們似乎弄錯了:
- 對于 value
25.3,最接近的 IEEE 754 數字的精確表示是25.300000000000000710542735760100185871124267578125,需要 52 個位元組。 - 最大數字 via 的輸出
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)作為精度傳遞,以獲得盡可能多的小數,以適應最壞的情況。很難產生適合所有情況的盡可能多的小數,并且可能需要多次嘗試或復雜的后處理。
uj5u.com熱心網友回復:
那個 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
uj5u.com熱心網友回復:
基本上,它試圖告訴你的是,最終可能會得到比存盤空間更多的數字。如果 val 是一個非常大的數字會怎樣?
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/468817.html
