[問題] printf & 型態轉換

看板C_and_CPP作者 (vvrr)時間9年前 (2016/02/15 14:51), 9年前編輯推噓11(11053)
留言64則, 9人參與, 最新討論串1/2 (看更多)
開發平台(Platform): (Ex: VC++, GCC, Linux, ...) GCC 問題(Question): printf的結果會根據型態的不同而改變 餵入的資料(Input): int a = 5000; char b = (char)a; printf("b = %x\n", b); 預期的正確結果(Expected Output): b = 88 (5000 = 0x1388) 錯誤結果(Wrong Output): b = ffffff88 補充說明(Supplement): 嘗試了一些a的初始值和結果,有點不太明白為什麼會變成這樣,整理如下: int a = 5000; --> b = ffffff88 int a = 0x1234; --> b = 34 int a = 50; --> b = 32 // 這個很正常 char a = 50; --> b = 32 // 這個很正常 char a = 0x7F; --> b = 7f char a = 0x80; --> b = ffffff80 char a = 0x81; --> b = ffffff81 主要是前兩個,為什麼一個會印出ffffff,一個就不會? -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 60.250.31.103 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1455519100.A.9CF.html

02/15 15:05, , 1F
char的範圍是?
02/15 15:05, 1F

02/15 15:17, , 2F
為什麼不是0x000088而是0xffff88 overflow是直接用ff
02/15 15:17, 2F

02/15 15:17, , 3F
頂替嗎
02/15 15:17, 3F

02/15 15:20, , 4F
char* b = (char*)&a; 這樣比較理想
02/15 15:20, 4F

02/15 15:23, , 5F
結果不是b = ffffff88嗎,怎麼下一行又變b = ffffff34
02/15 15:23, 5F
※ 編輯: vvrr (60.250.31.103), 02/15/2016 15:37:04

02/15 15:37, , 6F
是88沒錯@@ 抱歉
02/15 15:37, 6F

02/15 15:42, , 7F
char = 0x7F ,0x80 , 0x81 各試看看吧
02/15 15:42, 7F

02/15 15:43, , 8F
char就,一般的char. 應該是-128~127
02/15 15:43, 8F

02/15 15:44, , 9F
2F,我也不知道為什麼是ff,gcc印出來的..
02/15 15:44, 9F
※ 編輯: vvrr (60.250.31.103), 02/15/2016 15:46:25

02/15 15:45, , 10F
因為你 printf 要求的輸入不是 char 你卻給他 char 吧…
02/15 15:45, 10F

02/15 15:47, , 11F
它需要一個比 char 更大的型態
02/15 15:47, 11F

02/15 15:47, , 12F
像你這裡輸出的結果是 4*8=32bit 的型態
02/15 15:47, 12F

02/15 15:48, , 13F
如果前面都會被補成ffff就還好,但是有些就不會
02/15 15:48, 13F
※ 編輯: vvrr (60.250.31.103), 02/15/2016 15:49:09

02/15 15:49, , 14F
int i = 5000;
02/15 15:49, 14F

02/15 15:49, , 15F
char c = (char)i;i = c;printf("%p\n", i);
02/15 15:49, 15F

02/15 15:50, , 16F
@vvrr因為你不能知道你讀超過b之外的值是多少啊
02/15 15:50, 16F

02/15 15:52, , 17F
如果b前面的變數剛好都是0那讀出來當然就不會有f 反之
02/15 15:52, 17F

02/15 15:52, , 18F
亦然
02/15 15:52, 18F

02/15 15:53, , 19F
這裡沒有 overflow 只有 overbound
02/15 15:53, 19F

02/15 16:04, , 20F
FFFFFF不是補上去的吧......看了我都快吐血了
02/15 16:04, 20F

02/15 16:07, , 21F
神人來說一下char跟int的負數怎麼表示吧
02/15 16:07, 21F

02/15 16:11, , 22F
char跟int的-1各為FF跟FFFFFFFF
02/15 16:11, 22F

02/15 16:11, , 23F
-2呢,FE跟FFFFFFFE
02/15 16:11, 23F

02/15 16:13, , 24F
所以char的0x88跟int的0xffffff88是等值的
02/15 16:13, 24F

02/15 16:15, , 25F
在一些程式中,輸入0x80000000可能造成bug
02/15 16:15, 25F

02/15 16:15, , 26F
很多遊戲的洗錢BUG就是這麼來的
02/15 16:15, 26F

02/15 16:19, , 27F
關鍵字: sign extension
02/15 16:19, 27F

02/15 16:21, , 28F
(unsigned char)才對
02/15 16:21, 28F

02/15 16:21, , 29F
一個道具10萬塊錢好了,買21475個,變成0x80003FE0
02/15 16:21, 29F

02/15 16:22, , 30F
買了道具後系統還要付給你21億
02/15 16:22, 30F

02/15 16:36, , 31F
「所以char的0x88跟int的0xffffff88是等值的」
02/15 16:36, 31F

02/15 16:36, , 32F
所以printf會先把後面的數字轉成int嗎?
02/15 16:36, 32F

02/15 16:37, , 33F
^^^^^^^^^^ 後面的char型態的b
02/15 16:37, 33F

02/15 16:40, , 34F
%x Unsigned hexadecimal integer
02/15 16:40, 34F

02/15 18:21, , 35F
unsigned char b = (char)a; 改成
02/15 18:21, 35F

02/15 22:26, , 36F
>vvrr 16:36 是, 不過不是 printf 轉的
02/15 22:26, 36F

02/15 22:26, , 37F
而是因為 printf 屬於可變參數函式, 不到 int 等級的整數
02/15 22:26, 37F

02/15 22:27, , 38F
規定要轉成 int 再傳進去, 所以在那時就已經轉了
02/15 22:27, 38F

02/15 22:27, , 39F
也因為規定轉成 int, 所以會轉成一個有號整數
02/15 22:27, 39F

02/15 22:28, , 40F
這才用上了我上面講的 sign extension
02/15 22:28, 40F

02/15 22:29, , 41F
概念上就是如 stupid0319 講的, 0x88 (等於十進位 -120)
02/15 22:29, 41F

02/15 22:29, , 42F
會變成 int 的 -120 (0xffffff88)
02/15 22:29, 42F

02/15 22:29, , 43F
那因為二進位觀點來看就是最高位的正負號位元往前補滿
02/15 22:29, 43F

02/15 22:30, , 44F
所以要說「ffffff 是補上去的」技術上來說也沒有錯就是了
02/15 22:30, 44F

02/16 01:00, , 45F
其實可以開小算盤的程式設計師模式
02/16 01:00, 45F

02/16 01:02, , 46F
第一步計算5000+128
02/16 01:02, 46F

02/16 01:03, , 47F
第二步 將第一步結果 mod 256
02/16 01:03, 47F

02/16 01:04, , 48F
第三步 將第二步的結果再減去減去128
02/16 01:04, 48F

02/16 01:07, , 49F
小算盤的輸出結果是-120
02/16 01:07, 49F

02/16 01:10, , 50F
單看-120可能看不出端倪,請看小算盤下面顯示一堆1 0那欄
02/16 01:10, 50F

02/16 03:30, , 51F
要講型態轉換的話, 這樣操作: (1) 左下角選 dword, 10 進位
02/16 03:30, 51F

02/16 03:31, , 52F
然後輸入 5000; (2) 左下角點選 byte; 這等同於轉型成 char
02/16 03:31, 52F

02/16 03:31, , 53F
你會看到它變成了 -120 了
02/16 03:31, 53F

02/16 03:31, , 54F
(3) 根據我上面說的, 傳進 printf 前會再轉成 int
02/16 03:31, 54F

02/16 03:32, , 55F
所以再點回 dword, 你會看到數值還是 -120
02/16 03:32, 55F

02/16 03:32, , 56F
但下面的二進位顯示部份前面卻是全部補了 1 進去
02/16 03:32, 56F

02/16 03:33, , 57F
(4) 輸出成 %x, 所以點選 16 進位, 就看到 ffffff88 出來了
02/16 03:33, 57F

02/16 03:34, , 58F
你把你實驗的值代換掉上面的 5000, 觀察下面二進位顯示
02/16 03:34, 58F

02/16 03:34, , 59F
就會知道為什麼有些數會這樣變有些數會那樣變
02/16 03:34, 59F

02/16 10:58, , 60F
謝謝大家<(_ _)>
02/16 10:58, 60F

02/16 10:59, , 61F
1. int a 轉成 char b的時候,不論正負只留最後1個byte
02/16 10:59, 61F

02/16 11:00, , 62F
2. char b傳進printf前會根據b此時代表數值轉成signed int
02/16 11:00, 62F

02/16 11:01, , 63F
3. printf實際上印出來的都是int.有些只看到1byte的只是前面
02/16 11:01, 63F

02/16 11:02, , 64F
都是0(而且我沒有叫printf印出來) 大概是這樣沒錯吧
02/16 11:02, 64F
文章代碼(AID): #1MmNLydF (C_and_CPP)
文章代碼(AID): #1MmNLydF (C_and_CPP)