在ILP32中, char, short, int, long, long long, pointer分別占1, 2, 4, 4, 8, 4個字節,
在 LP64中, char, short, int, long, long long, pointer分別占1, 2, 4, 8, 8, 8個字節,
無論是在ILP32中還是LP64中, long long總是占8個字節,下面給出簡單的C代碼實現表征出整數的取值范圍先。
o foo.c
1 #include <stdio.h> 2 /** 3 * The size (n bytes) of basic types 4 * ================================= 5 * char short int long long long pointer 6 * ----- ---- ----- --- ---- --------- ------- 7 * LP64 1 2 4 8 8 8 8 * ILP32 1 2 4 4 8 4 9 */ 10 typedef char __s8; 11 typedef short __s16; 12 typedef int __s32; 13 typedef long long __s64; 14 typedef unsigned char __u8; 15 typedef unsigned short __u16; 16 typedef unsigned int __u32; 17 typedef unsigned long long __u64; 18 19 #define SMAX8 ((__s8 )(((__u8 )~0) >> 1)) 20 #define SMAX16 ((__s16)(((__u16)~0) >> 1)) 21 #define SMAX32 ((__s32)(((__u32)~0) >> 1)) 22 #define SMAX64 ((__s64)(((__u64)~0) >> 1)) 23 24 #define SMIN8 -SMAX8 25 #define SMIN16 -SMAX16 26 #define SMIN32 -SMAX32 27 #define SMIN64 -SMAX64 28 29 #define UMAX8 ((__u8 )~0) 30 #define UMAX16 ((__u16)~0) 31 #define UMAX32 ((__u32)~0) 32 #define UMAX64 ((__u64)~0) 33 34 #define UMIN8 ((__u8 )0) 35 #define UMIN16 ((__u16)0) 36 #define UMIN32 ((__u32)0) 37 #define UMIN64 ((__u64)0) 38 39 int main(int argc, char *argv[]) 40 { 41 __s8 smax8 = SMAX8; 42 __s16 smax16 = SMAX16; 43 __s32 smax32 = SMAX32; 44 __s64 smax64 = SMAX64; 45 __s8 smin8 = SMIN8; 46 __s16 smin16 = SMIN16; 47 __s32 smin32 = SMIN32; 48 __s64 smin64 = SMIN64; 49 printf("s64: [%llx, %llx]\t[%lld, %lld]\n", smin64, smax64, smin64, smax64); 50 printf("s32: [%x, %x]\t\t\t[%d, %d]\n", smin32, smax32, smin32, smax32); 51 printf("s16: [%x, %x]\t\t\t\t[%d, %d]\n", smin16, smax16, smin16, smax16); 52 printf("s8 : [%x, %x]\t\t\t\t[%d, %d]\n", smin8, smax8, smin8, smax8); 53 printf("\n"); 54 55 __u8 umax8 = UMAX8; 56 __u16 umax16 = UMAX16; 57 __u32 umax32 = UMAX32; 58 __u64 umax64 = UMAX64; 59 __u8 umin8 = UMIN8; 60 __u16 umin16 = UMIN16; 61 __u32 umin32 = UMIN32; 62 __u64 umin64 = UMIN64; 63 printf("u64: [%llx, %llx]\t\t\t[%lld, %llu]\n", umin64, umax64, umin64, umax64); 64 printf("u32: [%x, %x]\t\t\t\t[%d, %u]\n", umin32, umax32, umin32, umax32); 65 printf("u16: [%x, %x]\t\t\t\t\t[%d, %u]\n", umin16, umax16, umin16, umax16); 66 printf("u8 : [%x, %x]\t\t\t\t\t[%d, %u]\n", umin8, umax8, umin8, umax8); 67 68 return 0; 69 }
o 編譯並執行
$ gcc -g -Wall -m32 -o foo foo.c $ ./foo s64: [8000000000000001, 7fffffffffffffff] [-9223372036854775807, 9223372036854775807] s32: [80000001, 7fffffff] [-2147483647, 2147483647] s16: [ffff8001, 7fff] [-32767, 32767] s8 : [ffffff81, 7f] [-127, 127] u64: [0, ffffffffffffffff] [0, 18446744073709551615] u32: [0, ffffffff] [0, 4294967295] u16: [0, ffff] [0, 65535] u8 : [0, ff] [0, 255]
注意: 二進制數在計算機中一律以補碼表示。 這里簡單說說二進制編碼中的原碼,反碼以及補碼(注:移碼這里不談)以幫助理解上面的輸出。
1. 原碼的編碼規則
1.1 原碼即"原始編碼", 最高位為符號位,0表示整數,1表示負數;
1.2 +0和-0的原碼表示是不同的。在16位機器上,
+0 = 0000 0000 0000 0000b -0 = 1000 0000 0000 0000b
2. 反碼的編碼規則
2.1 正數的反碼等於其原碼;
2.2 負數的反碼是符號位不變,除符號外之外的其他位按位取反;
2.3 +0和-0的反碼表示也是不同的。在16位機器上,
+0 = 0111 1111 1111 1111b -0 = 1111 1111 1111 1111b
3. 補碼的編碼規則
3.1 正數的補碼等於原碼;
3.2 負數的補碼是符號位不變,除符號外之外的其他位按位取反,再給最低位加1;
3.3 +0和-0的補碼是唯一的,都是0。在16位機器上,
+0 = 0000 0000 0000 0000b ;= +0(反) -0 = 0000 0000 0000 0000b ;= -0(反)+1
4. 為什么要引入補碼?
4.1 無論是原碼,還是反碼,都無法解決0的的二義性問題。補碼的引入,解決了這一問題,也就是0的表示是唯一的;
4.2 讓符號位參與運算。因此,所有減法都可以用加法器實現。
o 因為編譯選項是-m32, 所以:
-127 的補碼表示是 0xffffff81 = (1111 1111 1111 1111 1111 1111 1000 0001b)
-32767 的補碼表示是 0xffff8001 = (1111 1111 1111 1111 1000 0000 0000 0001b)
-2147483647 的補碼表示是 0x80000001 = (1000 0000 0000 0000 0000 0000 0000 0001b)
-9223372036854775807 的補碼表示是0x8000000000000001 = (1000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0001b)
以-127為例,在32位機器上,其原碼、反碼和補碼可表示為:
o 1000 0000 0000 0000 0000 0000 0111 1111b ; 原碼
o 1111 1111 1111 1111 1111 1111 1000 0000b ; 反碼: 在原碼的基礎上, 符號位不變, 剩下31位按位取反
o 1111 1111 1111 1111 1111 1111 1000 0001b ; 補碼: 在反碼的基礎上, 給最低位加1
小結: 將常見的整數的取值范圍牢記於心,有利於在實際的程序設計中根據需求快速地確定變量(或結構體成員)的基本數據類型,寫出優質無錯的代碼。
- 對於占N個二進制位的有符號整數, 能表示的范圍是[- (2^N-1 - 1), +((2^N-1 - 1)], N=8, 16, 32, 64, ... (因為符號位占了一位,所以是N-1)
- 對於占N個二進制位的無符號整數, 能表示的范圍是[0, +((2^N - 1)], N=8, 16, 32, 64, ...
另外,在做算法設計的時候,將下面的表格(2的N次方)爛熟於心也有利於快速做出判斷。 例如,一個將每個32位無符號整數映射為布爾值的hash表可以將一台計算機的內存填滿。
2的N次方 | 准確值 | 近似值 | K/M/G/T...表示 |
7 | 128 | ||
8 | 256 | ||
10 | 1024 | 千(Thousand) | 1K |
16 | 65, 536 | 64K | |
20 | 1, 048, 576 | 百萬(Million) | 1M |
30 | 1, 073, 741, 824 | 十億(Billion) | 1G |
32 | 4, 294, 967, 296 | 4G | |
40 | 1, 099, 511, 627, 776 | 萬億(Trillion) | 1T |
1K : 2^10 : 千 1M : 2^20 : 百萬 (Million) ; 千千 1G : 2^30 : 十億 (Billion) ; 千百萬 1T : 2^40 : 萬億 (Trillion); 千十億 1E : 2^50 1Z : 2^60 256: 2^8 64K: 2^16 4G : 2^32
4Z : 2^64