指针学完之后,对指针是否深刻理解,以下是一些关于指针的经典例题,通过例题可以回顾一下指针的相关内容:
代码示例1:
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));//2,5
return 0;
}
代码详解1:
首先,&a表示的是整个数组的地址,其类型是int*[5],
(&a+1)表示跳过整个数组的地址,(int *)(&a + 1)
,强制类型转换为(int*)
类型,int *ptr = (int *)(&a + 1);
就可用下图来表示:
对于(ptr-1)
的位置上进行解引用即*(ptr - 1),
就可得到最后的结果为:5
同样,a
表示的是首元素的地址,(a+1)
表示指向第2个元素的地址,*(a+1)
表示对(a+1)
进行解引用,最后得到的结果为2
。
代码示例2:
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);//0x100014
printf("%p\n", (unsigned long)p + 0x1);//0x100001
printf("%p\n", (unsigned int*)p + 0x1);//0x100004
return 0;
}
代码详解2:
重点考察:指针+1
.
上面的例子中p是一个结构体指针类型,则p的类型是struct Test*
,0x100000
是十六进制,为一个整形类型,要将0x100000
强制类型转换为struct Test*
类型。
- 1
p + 0x1
:结构体指针+1,实际上是加了一个1 x 结构体的大小
,结构体Test类型的变量大小是20个字节,即结构体指针+1x20-->0x100000+1x20
,此处为十六进制(0x)的表示形式所以要将20转化为十六进制即14
,(4 x 16^0 + 1 x 16^1)
,所以最后所的结果为0x100014
。 - 2
(unsigned long)p + 0x1)
:强制类型转换为整形,即(unsigned long)p + 0x1)
就是加了一个整形1
,所得结果为:0x100001
。
- 3
(unsigned int*)p + 0x1)
:强制类型转换为整形指针,整形指针+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);// 4,2000000
return 0;
}
代码详解3:
VS2013为小端存储模式:即低位存储在低地址处,高位存储在高地址处。
&a
表示的是整个数组的地址,(&a+1)
表示跳过整个数组的地址,(int *)(&a + 1)
,强制类型转换为(int*)
类型,则ptr
指向的位置可用下图表示:
ptr1[-1]-->*(ptr+(-1))-->*(ptr-1)
,由于其是小端存储模式,则它解引用之后的输出顺序应该为:00 00 00 04
,所以ptr1[-1]
最后的输出结果为:4
a
表示首元素的地址,(int)a
表示强制转换成 int
型,((int)a+1)
表示地址值加1,就是向后偏移一个地址,则ptr2
指向的位置就如上图所示。对ptr2
解引用就是从ptr2
向后数4个地址即为一个整形的地址(上图蓝圈圈出的地方),然后进行输出,即为02 00 00 00-->2000000
。
代码示例4:
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]); //1
return 0;
}
代码详解4:
重点:**逗号表达式表示的结果实际上取决于逗号表达式里面最后一个值表示的结果,**即(0,1)-->(1),(2,3)-->(3),(4,5)-->(5)
,所以a
数组里面放的值实际上为{1,3,5};
数组里面的其他位置就放的是0
,放置效果如下图所示:
a[0]
表示第一行的数组名,这里即没有出现&a[0]
,也没有sizeof(a)
,所以a[0]
就表示数组的首元素地址,p[0]-->*(p+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]);
//FFFFFFC,-4
return 0;
}
代码详解5:
a[5][5]
是一个5行5列的数组,其可用下图形式表示,第一行到第五行的数组名可用a[0]---a[4]
表示,&a[4][2]
很容易就可以从数组中找出,如上图所指处。int(*p)[4] :
p是一个指针,指向的是一个数组,数组里面有4个元素,数组的类型为int
类型,p+1
跳过4个整形元素,则p+4
就是跳过16个整形元素,如上图所画处。
p[4][4]-->*(*(p+4)+2)
,&p[4][2] - &a[4][2]
小的减去大的,两者之间相差4个元素,则得到的是-4
。
10000000 00000000 00000000 00000100 //-4原码
11111111 11111111 11111111 11111011 //反码
11111111 11111111 11111111 11111100 //补码 (在内存中的存贮方式)
以%d的形式打印,打印出的为原码(-4)。
以%p的形式打印,即以地址的形式打印就没有原码,反码,补码的概念,则它会直接以补码的形式打印出来,即:F F F F F F F C
。
代码示例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)); //10,5
return 0;
}
代码详解6:
&aa
取出的是整个二维数组的地址,(&aa + 1)
跳过整个二维数组,(int *)(&aa + 1)
强制转换为int*
类型,将其赋值给ptr1
,ptr1-1
向前移动一个字节,然后对其进行解引用后指向的元素就为10
。
aa
是个数组名,表示的是第一行的地址,aa+1
代表第2行的地址,对其进行解引用,相当于拿到了第2行(得到了第2行的数组名),(*(aa + 1))-->a[1]
,则ptr2
指向的位置如上图所示,就可轻易推断出*(ptr2 - 1)
的结果。
代码示例7:
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa); //at
return 0;
}
代码详解7:
char *a[]
指针数组,a
表示首元素的地址,a
里面的每个类型都是char*
类型,将a
赋给pa
,pa
里面的每个类型都是char**
类型,pa
指向的是首元素的地址,pa++
,向后移动一个单位字节,对其进行解引用得到最终结果:at
。
代码示例8:
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);
return 0;
}
代码详解8:
char *c[]
指针数组,c
表示首元素的地址,c
里面的每个类型都是char*
类型,
cp
中共有4个元素,每个元素都是char**
类型,则它对应于数组c
中得位置如下图绿色箭头所指处,将cp
赋值给cpp
,其为char***
类型。
cpp
为一个指针,它指向的是cp
中第一个元素的位置,++cpp
向下移动一个元素(指向c+2位置),*(++cpp)
对其进行解引用,找到了cp
数组中c+2
位置的元素,**++cpp
再对其进行解引用,找到了数组c
中对应位置的元素,即为POIN
。如下图绿线所描述路径。
++cpp
再向下移动一个元素(指向c+1位置),*++cpp
解引用的到c+1
里面的元素,c+1
对用的是c
中的第二个元素, --*++cpp
:cpp
里面的元素减1
,即变为c
,对应数组 c
中得第一个元素, *--*++cpp
:对其进行解引用,拿到数组c
中对应位置的元素,*--*++cpp+3
:从元素所指位置向后移动3个元素位置,最终所的结果为:ER
。如下图棕线所描述路径。
*cpp[-2]-->*(cpp-2)
:由c
所指的位置向上移动2个位置,即指向(c+3)
所在位置,其对应的是数组c
中得第4个元素的位置,*cpp[-2]+3
:从元素所指位置向后移动4个元素位置,最终所的结果为:ST
。
cpp[-1][-1]+1
可写为*(*cpp-1)-1)+1
,cpp-1
:向下移动一个位置,指向(c+2)
所在位置,其对应的是数组c
中得第3个元素的位置,(*cpp-1)-1)
:表示从数组c
中对应位置向上移动一个元素位置,其对应的是数组c
中得第2个元素的位置,*(*cpp-1)-1)+1
:对数组c
中得第2个位置处进行解引用,并从元素所指位置向后移动1个元素位置,输出为字符串形式,则最终所的结果为:EW
。
以上。如有不足之处,还望指正。