這篇筆記說的是關於c/c++中的類型轉換的東西,重點是與指標有關的類型。
1、強制類型轉換
看一下這個c代碼:
int a = 0x123456; //0x是十六進位的標記法
char c = (char)a;
printf("%d/n", c);
在我的編譯環境裡輸出的是什麼呢? 是86,怎麼得來的呢?(我的編譯環境見“概述”)
int a的記憶體如下(左邊是低地址的內容,右邊是高地址的內容):0x56 0x34 0x12 0x00
"是不是搞反了?" 有人奇怪(正如我當年~~~~~~),這個問題還得詳細討論,但不是在這裡,是在別的文章,上google搜一下“little endian”,看一下相關的知識,然後再回來看啊。(我用的是intel x86系列的cpu,所以是little endian序的)
(little endian序的內容就是:低位元組資料放入低地址,高位元組資料放入高地址,比如上面的56是低位元組,所以放入低地址)
現在轉換到char,char在我的環境裡佔一位元組,(char)a的意思就是,取a中的第一個位元組出來,就是0x56了(5 * 16 + 6 = 86)
這就是c語言中基本類型的強制類型轉換。
(不知道上面的方法給了你什麼啟發,比如下面的問題:編寫一個c程式,判斷你用的機子是little endian還是big endian,你想到思路了嗎?呵呵)
那麼,如果是自訂類型的類型轉換會如何呢?
比如:
struct A
{
int a;
};
struct B
{
char c;
};
struct A aA;
aA.a = 0x123456;
printf( "%d/n", ( (struct B)aA ).c);
很遺憾,這個是通不過編譯的,為什麼呢?
原因可能如下(是我猜測的,如有不正確之處,各位指點一下,謝謝):
基本類型是編譯器定義的,所以編譯器裡面也實現了關於基本類型的轉換函式,比如(int)a,實際上是在編譯期間,在編譯器的內部調用了相關的轉換函式,所以就編譯通過。
但是我們自訂的類型,編譯器裡面沒有相關的轉換函式,所以就轉換不了。
為了彌補這個缺陷,c++中提供了一種轉換運算子的重載,只要重載轉換運算子(),就可以實現自訂類型之間的類型轉換了。(關於轉換運算子多載的用法,去google吧,我以前也沒用過,只是知道有這個東西而已,嗚嗚~)
2、關於指標的類型轉換的含義
那麼,到底有沒有辦法能實現以上的功能呢?能~!用指標類型轉換就能!
下面還是從基本類型開始:
int a = 0x123456;
int *pa = &a;
char *ca = (char *)pa;
printf("%d/n", *ca);
答案呢?還是86,呵呵~(原理和上面的一樣,這裡就不重複了)
所以我個人懷疑,基本類型的運算子的強制轉換操作在編譯器內部還是通過指標來實現的(個人猜測,如有不正確之處,請多多指教,謝謝)
所有的指標變數的大小都是固定的,都是4位元組(再次注意這一系列的筆記的所有例子有一個大的前提,就是我的編譯環境,請看筆記之“概述”)。
那麼,指標變數定義時候,前面的類型說明什麼呢?既然int *pInt; 和char *pChar;中,pInt和pChar的大小都是4位元組,那麼,int和char在這裡的作用又是幹嘛的?
休息一下,下面請看“指標類型轉換的基本原理”。
...(唱個“對面的女孩看過來”先)
......
指標定義的時候,前面的型別宣告,說明的是指標所指向變數。具體一點,是所指向的變數在記憶體的大小(sizeof()可以算出來),進一步可以推算出,該指標變數一次操作說取的記憶體單位。
比如
int a = 0x123456;
int *pInt = &a;
那個int *就說明了pInt指向的類型是一個整形,這意味著:pInt每次操作時候,以4位元組為單位,就是說每次4位元組4位元組的取記憶體;所以pInt指向的是a的地址(裡面從低到高放著:0x56, 0x34,0x12, 0x00...),讀取4位元組,再注意到這是little endian序,*pInt就是0x00123456了。
如果是 short *pShort = (short*)&a;呢?
(shrot *)&a說明了,取a的地址,放到pShort變數中去,而pShort的型別宣告是short,也就是說每次是以記憶體的2位元組為單位,是2位元組2位元組的取資料,每次pShort+1其實是在記憶體裡面向高處移動2位元組。
*pShort = 多少呢? 是0x3456(過程和上面的相似,這裡不重複了)
那麼對於自訂類型的指標轉換可以嘛?下面試試例子吧。
struct A
{
int a;
};
struct B
{
char c;
};
struct A aA;
struct B *pB;
aA.a = 0x123456;
pB = (struct B*)&aA;
printf("%d/n", pB->c);
編譯通過...串連通過...執行結果是86,Oh,Yeah!大功告成,親親嘴兒...(誰拍我的臉~)
轉換的過程這裡就不重複了。
所以說一般的類型轉換是有限的,但指標的類型轉換卻是無限的~(你可以這樣稱呼它:駭客的寵兒~~)
好了,這篇筆記到這裡就差不多結束了,下面再記下幾個問題:
1、是我以前在某公司的面試題目,問的是expr的值
char str[] = "abcdefghigklmn";
char expr = *(char*)( (int*)(str + 3) + 1 );
2、是我在工作當中遇到的一個問題的簡化。
char str[] = "abcdefg";
short *pStr = (short *)str;
printf("%d/n", *pStr);
問輸出什麼?
這個背景是要建立一個雜湊表,其索引是字串的頭兩個位元組,所以就有了這個東東~
PS.
本來我想寫簡練一點的,但是沒辦法,水平就這個樣子,以後在寫的過程中我會慢慢的改正一些風格的,希望各位能做一些評論,謝謝。
本筆記其實就說了一件事,指標定義的類型說明的作用就是:該變數指向的記憶體空間的大小和一次操作的單位。