C語言中,任何一個變量都必須占有一個地址,而這個地址空間內的0-1代碼就是這個變量的值。不同的數據類型占有的空間大小不一,但是他們都必須有個地址,而這個地址就是硬件訪問的依據,而名字只是提供給程序員的一種記住這個地址的方便一點的方法。但是,不同的變量在機器中都是0-1代碼,所以,我們不能簡單的通過檢查一個值的位來判斷它的類型。 例如,定義如下: int a; float b; double c; long double d; (假設它們所占的字節分別是4、8、8、10,而且連續存儲於某個地址空間,起始地址是100,則我們可以得到如下內存分布) a變量就是由以地址100開始到103結束的4個字節內存空間內的0-1代碼組成。b變量則是由以地址104開始到112結束的8個字節內存空間內的0-1代碼組成。而在機器中,這些內存都是連續的0-1代碼,機器並不知道100~103是整型而104~111是float型,所有這些類型都是編譯器告知的。當我們用a時,由於前面把a定義為int型,則編譯器知道從a的地址開始向后取4個字節再把它解釋成int型。那么(float)a,就是先按照int類型取出該數值,再將該數值按照int to float的規則轉換成float型。所以強制類型轉換就是按照某個變量的類型取出該變量的值,再按照***to***的規則進行強制轉轉換。如果是(類型名)常數,則是將該常數按照常數to類型 的規則進行強制轉換。 指針也是一個變量,它自己占據一個4個字節的地址空間(由於程序的尋址空間是2^32次方,即4GB,所以用4個字節表示指針就已經能指向任何程序能夠尋址到的空間了,所以指針的大小為4字節),他的值是另一個東西的地址,這個東西可以是普通變量,結構體,還可以是個函數等等。由於,指針的大小是4字節,所以,我們可以將指針強制轉換成int型或者其他類型。同樣,我們也可以將任何一個常數轉換成int型再賦值給指針。所有的指針所占的空間大小都是4字節,他們只是聲明的類型不同,他們的值都是地址指向某個東西,他們對於機器來說沒有本質差別,他們之間可以進行強制類型轉換。 指針 to 指針的強制類型轉換是指將指針所指的內容的類型由原先的類型轉換為后面的類型。 int a = 1; int *p = &a; float *p1 = (float*)p; 則p和p1的值都是&a,但是*p是將&a地址中的值按照int型變量進行解釋,而*p1則是將&a地址中的值按照float型變量進行解釋。 鑒於指針之間這種靈活的強制類型轉換的需求和出於簡化代碼的考慮,ANSI C引入了空指針即void*。void指針又名萬能指針,在現在的很多程序中,當參數不確定時就用萬能指針代替,這一類的指針在線程\進程函數里特別常見。 ANSI C規定,void指針可以復制給其他任意類型的指針,其他任意類型的指針也可以復制給void指針,他們之間復制不需要強制類型轉換。當然任何地址也可以復制給void型指針。我們在《網絡編程》中經常會看到accept(socket, (struct sockaddr *)&saddr_c, &lenth)之類的語句在&saddr_c之前需要增加代碼(struct sockaddr *)是因為當此函數被設計的時候ANSI C還沒有提出void*的概念。所有的地址統一用struct sockaddr類型標識,該函數的第二個參數也是指向struct sockaddr類型的指針,此處是強制類型轉換。 當然,在某些編譯器中不同類型的指針也可以進行直接賦值,但一般情況下會給出類型不匹配的警告。要求程序員顯示的給出指針強制類型轉換可以提醒程序員小心使用指針,對於明確程序目的具有一定的好處。 1、指針類型強制轉換: int m; int *pm = &m; char *cp = (char *)&m; pm指向一個整型,cp指向整型數的第一個字節 2、結構體之間的強制轉換 struct str1 a; struct str2 b; a=(struct str1) b; //this is wrong a=*((struct str1*)&b); //this is correct 3、關於一個程序的解釋 int main(void) { int a[4] = {1, 2, 3, 4}; int *ptr1=(int *)(&a+1); int *ptr2=(int *)((int)a+1); int *c = *(a + 1); printf("%x, %x,%x\n", ptr1[-1], *ptr2,*c); return 0; } 輸出分別為4 和2000000,2 式子&a+1表示的是指針加法運算,而不是普通的數值加法運算 vs2008下,其中a = 0x001bfc18 (&a + 1) = 0x001bfc28 而 a+1 = 0x001bfc1c &a + 1 的值取決於a的類型如果a申明int a; 則&a + 1 = 0xFFFF5704 = a + 1 如果 int a(ArryLen); 則&a + 1 = 0xFFFF5700 + 4 * ArryLen <> a + 1 a 表示數組的起始地址,(int ) a 表示將a的地址轉化為一個整形數,(int)a + 1 表示普通的數值加法運算,(int *)((int)a + 1)表示把(int )a + 1轉化為整型指針的地址。該地址指向數組a(0)的第一個字節(從0計數),因為是int型的 所以需要四個字節的解釋,所以結果是a(0)的后三個字節和a(1)的第一個字節組成的值,該值受大小端的影響。 *(a + 1) 此時的a已經是一個常指針了,這個表達式計算出a所指向元素后面的第2個元素的地址,然后對它解引用得到相應的值。這個表達式等價於 int last = a[1] 在貼 下面一段代碼: 復制代碼 #include <stdio.h> typedef struct stu1{ char chs[5]; }; typedef struct stu2{ char chs[4]; int n; }; int main(int argc, char const *argv[]){ struct stu1 s1; struct stu2 s2; s1.chs[0] = 'a'; s1.chs[1] = 'b'; s1.chs[2] = 'c'; s1.chs[3] = 'd'; s1.chs[4] = 'e'; s2 = *((struct stu2 *)&s1); printf("%c\n", s1.chs[3]); printf("%d\n", s2.n); return 0; } 復制代碼 結果輸出: 1 d 2 101 所有類型 都系統底層的本質都是一樣的 都是內存中的 0 1組成。基本強制類型轉換就是 把高出的 部分 位 截取掉。int型數值賦給char型變量時,只保留其最低8位,高位部分舍棄 在看一段代碼: 復制代碼 1 #include <stdio.h> 2 3 4 typedef struct stu1{ 5 int m; 6 int n; 7 }; 8 typedef struct stu2{ 9 char c1; 10 char c2; 11 }; 12 int main(int argc, char const *argv[]){ 13 struct stu1 s1; 14 struct stu2 s2; 15 s1.m = 815; //11 00101111 16 s1.n = 600; 17 s2 = *((struct stu2 *)&s1); 18 printf("%d, %d, \n", s2.c1, s2.c2); 19 int a = 559; //10 00101111 20 char c = (char)a; 21 printf("%d\n", c); 22 s1.m = 559; 23 s2 = *((struct stu2 *)&s1); 24 printf("%d, %d, \n", s2.c1, s2.c2); 25 return 0; 26 } 復制代碼 輸出結果: 47, 3, 47 47, 2, 上面代碼中 stu1結構體的大小為 4個字節 ,而stu2結構體的大小為 2個字節 ,所以stu1轉換為stu2的時候,只保留 前面2 個字節。s1.m 為 int 類型 s2.ch1 為 char 類型,前面說過 int型數值賦給char型變量時,只保留其最低8位,高位部分舍棄 我們可以看出 815 和 559 的 后(低)八位(一個字節) 都是一樣的 47。剩余一個字節(s1.m的高八位) 在賦值給 s2.ch2 815和 559的高八位 不一樣 所以輸出的結果也不一樣。 C語言 指針之間的 強制轉換 就是 其指針所指內容之間的 強制轉換。一個字節 一個字節之間的轉換。多出的部分截取掉。