C語言指針——指針的運算
說指針之前,先說兩個比較不相關的東西,也當做是一種知識的補充。看下面的代碼:
#include
int main(void)
{
char *s = "Hello world.";
printf(s);
}
運行這段代碼,會出現錯誤嗎?如果出現錯誤,是什麼錯誤?
分析:printf一種常規的用法就是printf("Please input a data:");這種用法相信很多人都使用過,但是將這種用法分析一下,這樣的用法printf函數接收到的參數是一個常量字符串的首地址,然後就可以輸出這個字符串,所以上面的代碼也是將字符串的首地址(存放於字符型指針s中)傳遞給了printf,所以這段代碼的運行不會出現任何問題,甚至編譯的時候連警告都不會出現。再看下面的一段代碼:
#include
int main(void)
{
char *s = "Hello world.";
printf(s, 1, 2, 3);
}
請問這段代碼是否正確,如果不正確,錯誤出在哪裡?
分析:要分析這個問題也還是比較容易的,printf函數和scanf函數是C語言中典型的兩個變長參數函數,printf也不知道自己會接收到多少個參數,它唯一知道的就是如果在輸入的字符串說明符中有多少個輸出控制符(%d等)就會到後面去解析多少個參數,如果解析不到的話這種情況下才會報錯。但是如果前面並沒有說明需要參數,而後面跟了參數的話,這個它是不會理會的,所以這段代碼編譯和運行都不會出現問題,還是跟上面一樣,編譯的時候甚至警告都不會出現。
現在開始言歸正傳說說指針,這次就簡單的說說指針的加減運算,請看下面一段代碼:
#include
int main(void)
{
int a[10];
printf("%p %p ", &a[0]+1, &a[1]);
}
請問程序輸出的兩個值一樣嗎?如果不一樣,兩者相差多少?
分析:這個問題需要對指針的運算理解比較深入才能答對,正確的答案是兩者輸出是一樣的,可能很多人碰到這個問題的時候會想,&a[0]之後得到的是第0個單元的地址,這個地址和&a[1]也就是第1個單元的地址應該是相差sizeof(int)個字節,所以兩者輸出應該相差3。但是這樣的算法忽略了指針的加減運算規則,指針或者說地址的運算往往依賴於指針或者地址的類型,比如我們常常會用int *p=a; printf("%d ", *p++);這種用法,通過p++就可以指向下一個int類型的單元!這就說明p裡面的值被p++這條語句增加了4,這一層的運算程序員是看不見的,後面實質進行的運算相當於p=p+1*sizeof(int);這裡也是一樣,int型的地址加1,並不是地址值簡單的加1,而是加了sizeof(int)個1,所以兩者輸出是一樣的。這個問題可以定義一個字符型的變量s,然後定義一個int類型的指針p,使用p=&s來給p賦值,這個時候編譯器會給出諸如:不能用char
*賦值給int *指針的錯誤或者警告,這個也證明對某種類型的變量進行取地址操作,得到的數據類型是該數據類型指針。再看一個更極端的例子:
#include
int main(void)
{
printf("%d ", (int *)0x04-(int *)0x00);
}
這個程序的輸出是多少呢?
程序運行之後輸出了1,這個當然是在32位機上運行的結果,這個例子可以有助於理解上面的分析過程。