练习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"
分析: