標籤:style blog color io 使用 ar 資料 sp div
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引入了null 指標即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 wronga=*((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 + 1a 表示數組的起始地址,(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 d2 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 0010111116 s1.n = 600; 17 s2 = *((struct stu2 *)&s1);18 printf("%d, %d, \n", s2.c1, s2.c2);19 int a = 559; //10 0010111120 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, 4747, 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語言 指標之間的 強制轉換 就是 其指標所指內容之間的 強制轉換。一個位元組 一個位元組之間的轉換。多出的部分截取掉。
指標強轉和void*