Re: [問題] 關於printf的問題

看板C_and_CPP作者 (好人超)時間13年前 (2011/11/07 05:48), 編輯推噓20(20036)
留言56則, 18人參與, 最新討論串2/2 (看更多)
※ 引述《zebraseven (Die walkuere)》之銘言: : 開發平台(Platform): (Ex: VC++, GCC, Linux, ...) : Windows NT : 問題(Question): : 如果在一個程式裡 : 只打上一行printf("%d %d", 4*atan(1), sin(4*atan(1))); : 我的執行結果會很奇怪,如下: : 1413754136 1074340347 : 但是如果把第一個 %d 改成了 %g : printf("%g %d", 4*atan(1), sin(4*atan(1))); : 答案竟然變成了我想要的 : 3.14159 0 : 第一個答案怎樣我不管, : 我的問題是第二個,它的輸出型態都是 %d : 但為什麼第一次跟第二次的執行結果不一樣呢?? : 特別說一下,我沒有宣告任何的暫存變數, : 程式碼就僅僅只有這兩行和標頭檔而已... : 這問題實在讓在下百思不得其解... : 有勞板上的大大們解惑!! 首先,要知道 4*atan(1) 這個結果是怎麼存在電腦記憶體裡面的 寫成二進位的話,它實際上是長這個樣子: 0100000000001001001000011111101101010100010001000010110100011000 約 3.14159 如果不知道為什麼,請找IEEE 754 0100000000001001001000011111101101010100010001000010110100011000 這一長串數字,如果從中間切成兩組32bit的數字: 01000000000010010010000111111011 = 1074340347 01010100010001000010110100011000 = 1413754136 有沒有覺得很眼熟?那就是你一開始印出來的東西了 printf("%d %d", 4*atan(1), sin(4*atan(1))); 這行的動作是這樣的: (1) printf 第一次看到 %d, 它知道 %d 代表該印出 32 位元的整數 因此它從記憶體裡面拿出32元位長度的資料,並且印出來 這個時候拿到的是3.14159這個數字的後半段,1413754136 (2) printf 第二次看到 %d, 它知道 %d 代表該印出 32 位元的整數 因此它從記憶體裡面拿出32元位長度的資料,並且印出來 這個時候拿到的是3.14159這個數字的前半段,1074340347 (3) 兩個%d都印完了,任務結束,sin(4*atan(1))的結果被無視了 printf("%g %d", 4*atan(1), sin(4*atan(1))); 在我電腦上的執行結果是 3.14159 856972295,而不是你所說的 3.14159 0 改成這樣之後,就變成了 (1) printf 第一次看到 %g, 它知道 %g 代表該印出 64 位元的浮點數 因此它從記憶體裡面拿出64元位長度的資料,並且印出來 這個時候拿到的是完整的3.14159這個數字 (2) printf 第二次看到 %d, 它知道 %d 代表該印出 32 位元的整數 因此它從記憶體裡面拿出32元位長度的資料,並且印出來 這個時候拿到的是sin(4*atan(1))的後半的結果 856972295 (3) 兩個%g, %d都印完了,任務結束,sin(4*atan(1))另一半被無視 這就是為什麼您只是把第一個 %d 改成 %g 印出來的結果會導致第二個數不同 printf根據您所輸入的 %d 或 %g 決定從記憶體剩下的資料裡拿出多少出來印 至於為什麼在您的電腦上,可以跑出 0 這個數 這就是其他板友講的「未定義行為」了 根據不同的電腦、不同的printf實作 可能印出「856972295」或者「0」或者其他什麼莫名其妙的東西,都很正常 您可以把 sin(4*atan(1)) 換成別的東西,結果應該就不是那麼正確了 ---- 其實這篇文章是用結果去推斷過程的,畢竟我懶得挖printf原始碼XD 坦白說我不知道自己講的夠不夠正確,不過意思應該有點接近了 接下來就交給t大繼續補充 (傳棒子) -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 114.35.184.77 ※ 編輯: james732 來自: 114.35.184.77 (11/07 13:53)

11/07 14:07, , 1F
11/07 14:07, 1F

11/07 14:16, , 2F
PUSH
11/07 14:16, 2F

11/07 14:19, , 3F
好清楚
11/07 14:19, 3F

11/07 14:30, , 4F
超哥是個人才
11/07 14:30, 4F

11/07 14:30, , 5F
推 我前面推文說的要很大一篇就是這一篇...
11/07 14:30, 5F
其實我已經省略了很多我自己都搞不懂的細節...XD

11/07 14:33, , 6F
另外我沒記錯的話 傳的參數在記憶體的位置是實作規定
11/07 14:33, 6F

11/07 14:33, , 7F
沒有一定要在隔壁 只是大部份情形下都會是就是了...
11/07 14:33, 7F

11/07 15:09, , 8F
我也認同超哥是個人才(發一張人才卡給超哥)(∩_∩)
11/07 15:09, 8F
如果是正妹的話,可以喔 (啥)

11/07 15:15, , 9F
上次有一個正妹被超哥兇跑了.所以此時這沒正妹
11/07 15:15, 9F

11/07 15:16, , 10F
.....有這回事?
11/07 15:16, 10F

11/07 15:26, , 11F
正妹都找人代寫作業.別找了.超哥
11/07 15:26, 11F

11/07 15:30, , 12F
說的也是 (嘆)
11/07 15:30, 12F

11/07 16:23, , 13F
超哥給個推,另外,(拍拍)
11/07 16:23, 13F

11/07 16:51, , 14F
%g跟%lg有差嗎~~?
11/07 16:51, 14F

11/07 17:06, , 15F
lg 是日立唷XD
11/07 17:06, 15F

11/07 17:20, , 16F
日立是LG O___O?
11/07 17:20, 16F

11/07 17:27, , 17F
兩家公司合資的
11/07 17:27, 17F

11/07 18:40, , 18F
所以下次要印%lg可以印%hitachi嗎 ! (....我去反省)
11/07 18:40, 18F

11/07 19:15, , 19F
超哥超強 !!! 真的受教了... 這些都GOOGLE不到的呢 !!
11/07 19:15, 19F

11/07 19:15, , 20F
您真是程設板的樑柱 ~ :))
11/07 19:15, 20F

11/07 19:22, , 21F
這麼講太誇張了,板上的高手這麼多,會害他們笑出來 XD
11/07 19:22, 21F

11/07 19:36, , 22F
↖才德兼備
11/07 19:36, 22F

11/07 19:42, , 23F
↖才德兼備
11/07 19:42, 23F
.........

11/07 20:28, , 24F
大推超哥!!其實該說的都說完了,要查754,online 就可查.
11/07 20:28, 24F

11/07 20:28, , 25F
另 %g,%lg 在printf並無差別,只在scanf才有差別.
11/07 20:28, 25F

11/07 20:30, , 26F
其實亂碼在特定compiler常看到的可能就那幾個數字,如..
11/07 20:30, 26F

11/07 20:31, , 27F
0xCCCCCCCC , 寫到後來看到莫名奇妙的都知道有鬼。
11/07 20:31, 27F
0xCCCCCCCC 這個東西寫VC就會覺得超親切的啊...XD

11/07 21:22, , 28F
我補一下好了,上一篇 littleshan 曾提到這行為是 u.b.
11/07 21:22, 28F

11/07 21:22, , 29F
原因是在於printf抓變數出來時,通常還會考慮 padding
11/07 21:22, 29F

11/07 21:23, , 30F
問題,而 padding 實作更不用多說,各家不盡相同。
11/07 21:23, 30F

11/07 21:34, , 31F
padding???
11/07 21:34, 31F

11/07 23:50, , 32F
0xcc或0xcd印象中是VC debug build時用來檢查未使用變數
11/07 23:50, 32F

11/07 23:51, , 33F
或其他輔助除錯功能用的樣子....@_@"
11/07 23:51, 33F

11/08 01:10, , 34F
太強啦~
11/08 01:10, 34F

11/08 01:13, , 35F
%g跟%lg在printf沒差的話 我寫%g (看原文意思是會找64bi
11/08 01:13, 35F

11/08 01:13, , 36F
t參數)但是傳float 實際上printf會怎麼做啊~因為看起來
11/08 01:13, 36F

11/08 01:13, , 37F
沒有出錯的樣子呢 @@"
11/08 01:13, 37F

11/08 01:46, , 38F
要看堆疊的後面是甚麼吧 結果應該是不可預期的?
11/08 01:46, 38F

11/08 02:11, , 39F
@purincess:這問題和 %lf / %f 在 printf 會怎做是一樣
11/08 02:11, 39F

11/08 02:11, , 40F
但確實 %g / %lg, %f / %lf 在 printf 裡面是不分的。
11/08 02:11, 40F

11/08 02:12, , 41F
有興趣的話直接去 google printf.c 比較快。
11/08 02:12, 41F

11/08 02:24, , 42F
好~謝謝!!
11/08 02:24, 42F

11/08 11:09, , 43F
c 裡面有規定可變長度參數中float 會promote 成double
11/08 11:09, 43F

11/08 11:09, , 45F
更多有關 varargs 的常見問題
11/08 11:09, 45F

11/08 13:54, , 46F
我現在的暱稱就是 0xCCCCCCCC XD
11/08 13:54, 46F

11/08 13:55, , 47F
之所以會選這個值我猜是 0xCC 是 x86 asm 的 int 3 的原因
11/08 13:55, 47F

11/08 15:32, , 48F
write solid code ch3 有提到塞0xcc的兩個主要原因int 3 是
11/08 15:32, 48F

11/08 15:33, , 49F
其中一個 另一個是值夠大夠奇怪:D
11/08 15:33, 49F

11/08 15:37, , 50F
夠大夠奇怪的值會讓我想到死牛肉(0xDEADBEEF) XD
11/08 15:37, 50F

11/08 15:47, , 51F
11/08 15:47, 51F

11/08 16:25, , 52F
真沒想到cc也是個學問,推VictorTom,LPH66,akasan.
11/08 16:25, 52F
真的長知識了,原來 0xCC 也是有典故的 ※ 編輯: james732 來自: 140.117.171.40 (11/09 08:55)

11/09 09:04, , 53F
我比較好奇死牛肉的由來
11/09 09:04, 53F

11/09 09:19, , 54F
Hexspeak(16進制魔術數字)<--
11/09 09:19, 54F

11/09 09:49, , 55F
Linux system call 的 0xFEE1DEAD 超經典 lol
11/09 09:49, 55F

05/20 13:27, , 56F
推~
05/20 13:27, 56F
文章代碼(AID): #1Ejt4g9O (C_and_CPP)
討論串 (同標題文章)
本文引述了以下文章的的內容:
完整討論串 (本文為第 2 之 2 篇):
文章代碼(AID): #1Ejt4g9O (C_and_CPP)