top
Loading...
水滴石穿C語言之指針步進辨析
基本解釋

通過上一篇的分析,我們已經很清楚地知道:指針不是一個簡單的類型,它是一個本身和所指向物相復合的類型。指針的算術運算(如步進)與指針所指向物的類型密切相關。

問題:指針步進 & 步進單位

下面的代碼中打印出的結果是幾?

int arContext[5] ={0,1,2,3,4}, i, *pAr;
pAr = arContext;
printf ("%d", *(pAr + 3 * sizeof (int)));

答案與分析:

這段代碼沒有正確答案,因為這段代碼是錯的,printf將打出無法預測的內存區的值,其中的原因如下:

在C語言中,指針總是按照它所指向的對象的大小步進。在上面的例子中,pAr是指向整數類型變量的指針,一個整數是4個字節(默認CPU字長是32位),pAr + 1就指向下一個整數,也就是指針后移4個字節,而不是說將地址只移動一個字節。

因為C語言編譯器知道每個指針的類型,因此對指針的運算是會自動把所指類型的Size考慮進去的。

pAr + 3 * sizeof (int) = pAr + 3 * 4 = pAr + 12 ,因此pAr指向了數組的第13個整數元素。而數組本身才5個元素,pAr早已經超出了界限,所指向的地方當然就是無人可知道的東西了,具體指向什么東西,各種不同的編譯器互不相同。總之,肯定不能打印出我們想要的值就是了。

指針不是一個簡單的類型,它是一個和指針所指物的類型相復合的類型。因此,它的算術運算與指針所指物的類型密切相關,在C++語言中也是同樣。

再比如下面的例子:

int a[8];
int* p = a;
int* q = p + 3;
p++;

指針的加減并不是指針本身的二進制表示加減,要記住,指針是一個元素的地址,它每加一次,就指向下一個元素。所以:

int* q = p + 3;

q指向從p開始的第三個整數。

p++;

p指向下一個整數。

問題:指針步進 & 步進單位轉換

我有一個char *類型的指針,恰好指向了一個int類型的值,我想讓這個指針跳過int指向下一個char,下面的代碼可以達到這個目的嗎?

((int *)p)++;

答案與分析:

可以。

首先我們要清楚C語言中左值和右值的概念,C語言中左值是指可以放在“=”左側,即可以被賦值,右值是可以放在“=”的右邊,即可以賦給其它變量的值。++是單目操作符,它將一個變量的值加1然后再賦給這個變量,因此它需要的操作數應該既可以放在“=”號的左邊,也可以放在“=”的右邊。原則上講,類型強制轉換的結果是右值而不是左值。所以,(int *)p的結果在這個表達式中是++的右值,而++的左值依舊是p,而不是(int *)p。

這個問題的核心正是告訴我們類型強制轉換的結果是右值而不是左值。

另外,我們可以使用一個簡單的辦法達到相同的目的:

p += sizeof(int);

p是char *類型的指針,它的步進長度是1,加上一個整數所占的長度,就是跳過了一個整數所占的空間。

所以,有時候,ULONG *p; 想要增加8個字節,可以作如下強制轉換:

(ULONG *)((UCHAR *)p+8

問題:指針步進 & void 指針

為什么我對void *類型的指針進行運算,編譯器會報告如下錯誤?

error C2036: 'void *' : unknown size

答案與分析:

在C語言中,所有的指針遠算,例如+、—、*、/,都是將它所指向的對象的尺寸考慮進取的。例如‘char*’類型的指針加1,就是地址向后移動一個字節;而‘int*’類型指針加1,就是移動4個字節。但是,對于‘void*’型的指針呢?‘void *’指針在C標準中被規定可以強制轉換成任何類型的指針而不會丟失數據,它的大小具體的編譯器各不相同,也就是說,編譯器也不知道void到底有多大,因此,無法對‘void*’類型的指針進行算術運算。

作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗