C語言類型轉換知多少?
學C的都知道類型轉換,可是,到底哪些類型轉換是安全的,那些是有危險的?雖然類型統一規劃好是好的程序開發的基礎。但是有些時候避免不了類型轉換的時候,這時候我們需要:
1. 瞭解哪些類型轉換有危險(危險的都是顯式類型轉換?)。
2. 瞭解那些轉換會增加代碼量。
3. 如何通過顯式的轉換來減少轉換次數,提高效率。
類型轉換的方式:
|
短的轉長的 |
等長的之間 |
長的轉短的 |
有符號轉無符號 |
符號擴展到長的 負數值變含義了 |
存儲的二進制不變 負數值變含義了 |
截斷 |
無符號轉有符號 |
值不變 |
大於負數範圍的值變負 |
截斷 |
有符號之間 |
值不變 |
N/A |
截斷 |
無符號之間 |
值不變 |
N/A |
截斷 |
1. double 和整數之間的轉換是直接將小數點後截掉進行轉換,反之,將整數賦值給浮點數的時候,受浮點數表達能力的限制,可能會出現整數部分不完全一樣的情況,這些轉換都需要代碼實現。
2. 無論短轉長,還是長轉短,這些轉換都需要代碼實現。
測試當中,還發現有這個現象:
short a =-100;
unsignedshort b;
printf("a=%d\n",a);
printf("a=0x%x\n",a);
b =a;
printf("b=%d\n",b);
printf("b=0x%x\n",b);
結果如下:
a=-100
a=0xffffff9c
b=65436
b=0xff9c
爲什麼a和b的十六進制數打印的不一樣呢?我們看看真正是怎麼處理的:
movw $-100,-2(%rbp) // a = -100;
movswl -2(%rbp),%eax // 準備參數,因爲類型是short,所以做一次隱式轉換,相當於(int)a; 由於是有符號數,所以符號擴展了。
movl %eax,%esi
movl $.LC0,%edi
movl $0,%eax
call printf // printf("a=%d\n",(int)a);
movswl -2(%rbp),%eax
movl %eax,%esi
movl $.LC1,%edi
movl $0,%eax
call printf
movzwl -2(%rbp),%eax // 寄存器eax = a; 這裏無論怎麼擴展,下一步取得都只是低32位,所以不會有任何影響。
movw %ax,-4(%rbp) // b = 寄存器ax(低32位);
movzwl -4(%rbp),%eax // 準備參數,因爲類型是short,所以做一次隱式轉換,相當於(unsigned int)a;
movl %eax,%esi
movl $.LC2,%edi
movl $0,%eax
call printf //printf("b=%d\n", (unsigned int)b);
movzwl -4(%rbp),%eax
movl %eax,%esi
movl $.LC3,%edi
movl $0,%eax
call printf
從上面的代碼可以看出,確實printf的輸入參數,小於int的類型是要轉換爲相應的int,然後調用printf進行打印的,這也是C語言函數處理入參的默認行爲。
隱式轉換規則:
就是不需要明確指定,編譯器會生成相關的代碼進行轉換:
1. 關於表達式運算
首先:不同類型數運算的時候,先要轉換爲相同的,轉換規則是表達能力小的轉換爲表達能力大的,即:短的轉換爲長的。
其次:整形計算的最小單位是int,如果是小於int的數字,先要轉換爲int。浮點數計算的單位都是double,所以所有的參與浮點運算的任何一方只要是浮點數,大家都會轉換爲double進行運算。這麼做的原因是:保證精度。
2. 關於賦值
賦值是強制按照左邊的類型進行轉換,有可能是顯式的截斷,參見上表。