1. 程式人生 > >C語言:指標練習題(2)

C語言:指標練習題(2)

練習5:

int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("a_ptr=%#p,a_ptr=%#p\n", &a[4][2], &p[4][2]);
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}

輸出結果:ff ff ff fc;-4

分析:

練習6:

int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int *ptr1 = (int*)(&aa + 1);
	int *ptr2 = (int*)(*(aa + 1));
	printf("%d %d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

輸出結果:10 ; 5

分析:&aa,取的是二維陣列的地址,加1,跳過整個陣列,指向了10的後面,賦給ptr1,相當於ptr1指向了10的後面,ptr1-1再解引用,整形指標減1,向前挪動4個位元組,解引用後拿出10;陣列名是首元素地址,但是aa是二維陣列,所以首元素地址就是第一行的地址,加1向後跳過了一行,也就是指向了第二行的地址,第二行的地址解引用,就是aa[1],也是第二行的陣列名,它又相當於第二行首元素的地址,也就是讓ptr2指向了6,ptr2-1,向前挪動一個整形,解引用,訪問的就是5.

練習7:

(*(void(*)())0)();

怎麼理解這句程式碼?

分析:我們希望0是某個函式的地址,所以把它強制型別轉換為函式的指標型別,這個時候它就是個函式地址了,在前面加個*,表示解引用操作,找到這個函式,後面去呼叫它,呼叫的時候,因為函式無參,所以不用傳引數。這是分析過程,那麼具體如何描述呢?

答:呼叫0地址處的函式,呼叫的函式的引數是無參,返回型別為void,還要解釋的話,可以這樣說:把0強制型別轉換為函式的指標型別,解引用,去找到這個函式,並呼叫該函式,調的函式沒有引數,返回型別為void。

void(*signal(int, void(*)(int)))(int);

怎麼理解這句程式碼呢?

分析:分析這種複雜的程式碼,首先,我們得找到一個名字或者一個符號開始,這個程式碼呢,就從signal開始,這是函式,而且是函式宣告,不是函式呼叫,為什麼這樣說呢?呼叫函式有傳參,但是這裡面沒傳參,有的人可能會說傳的是int,但是傳int時,也只能傳1,2,3,或者其他整形數字,不可能會傳int過去,所以這絕對不是一個函式的呼叫,而是一個函式的宣告,它的引數為int和函式指標,返回型別為函式指標。

 void (*signal(int,void(*)(int)))(int);

signal(int,void(*)(int))就是函式名和函式引數,去掉signal(int,void(*)(int))之後,剩下的就是函式的返回型別void (*)(int),所以它返回的是一個函式指標,該指標指向函式的引數是int,返回型別為void。

不過,我們理解的的寫法應該是:

void(*)(int) signal(int,void(*)(int));

但是實際上並不這樣寫。這個程式碼太複雜,我們寫的程式碼應該是讓別人讀得懂的,所以可以使用 typedef 把它簡化一下:

typedef void(*ptr_t)(int);
ptr_t signal(int,ptr_t);

分析了這麼多,這段程式碼具體應該這樣解釋:

signal是一個函式宣告,函式的引數為int和一個函式指標,該函式指標指向的函式引數為int,返回型別為void;signal函式的返回型別也為函式指標,該函式指標指向的函式引數為int,返回型別為void。  

練習8:

#include <stdio.h>
int main()
{
	char *a[] = { "work", "at", "alibba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

輸出結果:"at"

分析: