C:指针经典题目详解

求下面的题目在win32环境下的运行结果

1.
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int *ptr = (int *)(&a + 1);
	printf( "%d,%d", *(a + 1), *(ptr - 1));
	system("pause");
	return 0;
}

首先我们看到,a是一个存放五个整形元素的数组,&a这个操作得到的是一个指向整个数组a的数组指针,它+1后跨越了整个数组,来到了5的后面,此时这个指针类型是int(*)[5],每走一步跨越20个字节,紧接着将它强制类型转换成int*指针依然指向的是5后面的那块空间,但步长缩短为一个整形(四个字节),将这个地址赋给同为int*的ptr;ptr-1向后来到了存放5的空间,解引用后拿到了5;a+1则是数组首元素地址+1来到了第二个元素的地址,解引用后拿到2;
所以这道题的运行结果是:

2 5
2.
 struct Test
 {
 	int Num;
 	char *pcName;
 	short sDate;
 	char cha[2];
 	short sBa[4];
 }*p;
 //假设p 的值为0x100000。 如下表表达式的值分别为多少?
 int main()
 {
 	p = (struct Test*)0x100000;
 	printf("%x\n", p + 0x1);//0x100014
 	printf("%x\n", (unsigned long)p + 0x1);//0x100001
 	printf("%x\n", (unsigned int*)p + 0x1);//0x100004
 	system("pause");
 	return 0;
 }

首先我们需要求出一个Struct Test类型变量所占空间大小:int(4)+char*(4)+short(2)+char(1)*2+short(2)*4 = 20byte;
第一个输出语句:0x1换算成十进制就是1,因为p指向的元素大小是20个字节,所以+1后地址就加了20个字节,变成了0x100014;
第二个输出语句:将p强制类型转化为一个无符号长整形,此时p只是单纯的一个数组,加1那就是真+1了,所以结果是0x100001;
第三个输出语句:将p强制类型转换成无符号整形指针类型,这时加+1就是给地址加4个字节,所以输出就是0x100004

3.
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int *ptr1 = (int *)(&a + 1);
	int *ptr2 = (int *)((int)a + 1);
	printf( "%x,%x", ptr1[-1], *ptr2);
	system("pause");
	return 0;
}

这里的a依然是一个存放了4个整形元素的数组,和第一题的操作一样,ptr1是一个步长为四个字节,存放a数组后面那块空间的地址,ptr[-1]就等价于*(ptr-1),拿到了4,而ptr2的操作是将a首元素地址强制类型转换为整形,再+1(整数运算哦,不是地址),再强制类型转换为int*(这意味着较a的首元素地址向后移动了1个字节)附给ptr2,这里解引用时就可要小心了:
因为笔者的电脑是小端存储,数据的低位储存在内存空间的低地址位,高位存储再内存空间的高地址位
C:指针经典题目详解_第1张图片
从图上我们就可以看到*ptr2的值应该是0x2000000;
所以最终的输出结果为:

4 2000000
4.
#include 
int main(int argc, char * argv[])
{
	int a[3][2] = { (0,1),(2,3),(4,5) };
	int *p;
	p = a[0];
	printf( "%d", p[0]);
	//0
	system("pause");
	return 0;
}

不知道你注意了到没有,a[3][2]里好像有点不对劲,初始化应该是{{0,1},{2,3},{4,5}}啊,对,这个(0,1)其实是逗号表达式,只输出最后一个逗号后面的结果,所以,正确的画风应该是这样的{1,3,5},
又因为在二维数组中a[0]是一个数组名,所以给p赋的是第零行首元素的地址,所以最终结果是1;

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

这里我们看到a是一个5行5列的二维数组,p是一个指向存放4个整形字节的数组指针,所以p = a,就是将a的第0行地址付给了p,虽然存放的地址相同,但p与a[0]数组指针的步长并不相等,所以,我们在计算&p[4][2]-&a[4][2]时先将他们化成如下形式

&(*(*(p+4)+2) )-&(*(*(a+4)+2))

这表明是p数组指针先加4再访问第五组中的第2个元素,所以共跨越了*4+2=18个整形元素,a数组指针也是先加4再访问第5组的第二个元素,因为a数组指针的类型是int*[5],所以共跨越了4*5+2=22个整形元素,因为a和p都是从同一地址出发,所以p的最终地址和a的最终地址是连续的,相减得到的是有正负的元素个数,是-4;而-4作为地址输出的是无符号十六进制位-4在内存中存放的是补码:
11111111 11111111 11111111 11111100
转化成无符号十六进制就是0xFFFFFFFC
所以最终输出的结果是:

0xFFFFFFFC -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));

	system("pause");
	return 0;
}

首先&aa拿到的是整个二维数组的地址,+1后直接来到数组之后的那块空间,在强制类型转化成步长为4字节的整形数组赋给ptr1,-1
后就向前移动一个整形(4byte)指向了10的地址,解引用后拿到数字10;
接下来,aa+1代表的是二维数组首元素(整个a[0]行)+1,来到a[1]
行,此时解引用正相当于拿到了a[1]行首元素的地址赋给了ptr2,-1后来到了5的位置,解引用后得到5;
所以输出结果为:

10 5
7.
int main()
{
	char *a[] = {"work","at","alibaba"};
	char**pa = a;
	pa++;
	printf("%s\n", *pa);
	system("pause");
	return 0;
}

首先a是一个字符指针数组,里面分别存放了指向"work"首字符的指针,指向"at"首字符的指针,指向"alibaba"首字符的指针;char**pa = a则是将a数组的首元素地址(存放了指向“work”首字符的指针)传给了pa,pa++后来到了a的第二个元素,也就是存放指向"at"首字符的指针的空间,进行一次解引用后拿到了这个指针,%s直接输出这个指针指针指向的字符串;所以最终结果是:

at
8.
#include 

int main()
{
	char *c[] = {"ENTER","NEW","POINT","FIRST"};
	char**cp[] = {c+3,c+2,c+1,c};
	char***cpp = cp;
	printf("%s\n", **++cpp);
	printf("%s\n", *--*++cpp+3);
	printf("%s\n", *cpp[-2]+3);
	printf("%s\n", cpp[-1][-1]+1);

	//POINT ER ST EW-4
	system("pause");
	return 0;
}

要想顺利的做对这道题,我们需要先画图,看清楚在内存空间上c,cp,cpp有怎样的联系:
C:指针经典题目详解_第2张图片
在对c,cp,cpp进行初始化,根据上面的题目,我们不难画出这样的图。
第一句printf语句中cpp第一次++后指向cp1的位置,解引用后,拿到了c2的位置,再次解引用后得到了POINT的首字符地址,输出POINT;
第二句printf语句中cpp第二次++后指向cp2的位置,解引用后拿到c1的位置,–后(红线)指向了c0的位置,解引用后拿到ENTER首字符地址+3后指向E输出ER;
第三句printf语句中cpp[-2]等价于*(cpp-2)拿到cp0储存的c3地址,解引用拿到FRISR首字符地址+3后指向S,输出ST;
第四句printf语句中cpp[-1]等价于*(cpp-1)拿到cp1储存的c2地址,
c2[-1]拿到c1中储存的NEW首字符地址,+1指向E输出EW;
所以最后输出结果为:

POINT 
ER
ST
EW

你可能感兴趣的:(C/C++)