指针本质论
1.指针是什么? 和一般变量有什么区别?
指针就是地址,和一般变量没有本质区别,仅仅是它有自己的规则。
int a=100;
int *p=&a;
printf("%d\n",a); // 100
printf("%p\n",p); //0xbfa47858
a是一个变量名,类型是int,值是100,a有自己的地址&a
p是一个变量名,类型是int*,值是0xbfa47858,p有自己的地址&p
我们发现指针和普通变量没什么区别,那到底为什么我们觉得它难呢?
主要是指针是一种新的变量类型,它有自己的一套运算规则。
2.指针的运算规则
#1.指针的类型
定义不同的指针,它有自己的类型. 如,
int *p -> int 型指针
char *p -> char 型指针
int (*p)[2] -> int型的数组指针
void (*p)() -> 函数指针
...
果然很多,怪不得那么难.
#2.指针的强制类型转换
int *p;
char *q=(char*)p;
这是把int型指针转换成char型指针,和变量的强制类型转换一样,so easy...
#3.指针的加减规则
指针有自己一套加减规则。
int a=100;
int *p=&a;
printf("%d\n",a); // 100
printf("%p\n",p); //0xbfa47858
p=p+1; p是int型指针,sizeof(int)=4,所以p+1其实加的是一个int型变量的大小,所以p=0xbfa4785c
char *p;
p=0x77777777; //p是一个变量,可以给p直接赋值
p=p+1; p是char型指针,sizeof(char)=1,所以给p+1其实加的是一个char型变量的大小,所以p=0x77777778
....
我们发现,还是so easy...
#4. 指针的解引用,也就是*p
解引用是有步骤的
如 type *p; 求*p //type是类型
1.找到指针所存放的内存单元
2.从这个地址开始读取sizeof(type)个字节大小
3.把读取的内容按type类型解释,解释出来的就是*p
eg.
short a=256;
short *p=&a;
printf("%d\n",*p); //256,so easy...
1.p所对应的就是a的地址
2.a->00000001 00000000,读取2个字节,所以读取到的就是00000001 00000000
3.把00000001 00000000解释成为short类型,明显是256
char *q=(char*)p;
printf("%d\n",*q); //0, maybe not so easy....
1.p所对应的就是a的地址
2.a->00000001 00000000,读取1个字节(sizeof(char)),所以读取到的就是00000000(小段字节序)
3.把00000000解释成为char类型,明显是0
如果上面的都懂了,那么下面的就不是难事
我要读取a的高地址,怎么写?
char *q=(char*)p+1; //(char*)p指向a的地地址,+1,因为是char型指针,所以加一个字节,指向a的高地址
printf("%d\n",*q); // 读取sizeof(char)个字节,即00000001,所以是输出1
short a=256;
char *p=&a;
printf("%d\n",*(short*)p);
p指向a的低地址,将p强制转换为short型制作,所以读取sizeof(short)个字节,所以输出256
short a=256;
char *p=&a;
printf("%d\n",*((char*)(short*)p+1)); //输出结果为1,so easy...
再来点有挑战的吧!
short a=256;
char *p=&a;
printf("%d\n",*(short*)(*(int*)&p)); //256
如果这道题目做对了,那么指针也就被你俘虏了。
关键就是这是为什么是int*,主要是指针所占空间是4个字节(32位主机)
#5.那么数组指针和指针数组到底怎么回事?
数组指针是指针,指针数组是数组,就这区别
数组指针只存在与多维数组吗?,下面将带给你不一样的认识
我们知道数组名所代表的就是数组首地址,那么对数组名取地址得到的是什么?
一维数组:
int a[2]={1,2};
(gdb) p a
$5 = {1, 2}
(gdb) p &a[0]
$6 = (int *) 0xbffff0c0 //&a[0]是普通int型指针
(gdb) p &a[0]+1
$7 = (int *) 0xbffff0c4 //所以+1加的是sizeof(int)
(gdb) p &a
$8 = (int (*)[2]) 0xbffff0c0 //一维数组名的地址其实是个数组指针,这个就有点像sizeof(a)返回的是整个数组大小
(gdb) p &a+1
$9 = (int (*)[2]) 0xbffff0c8 //所以+1加的是2*sizeof(int)
(gdb) p a+1
$10 = (int *) 0xbffff0c4 //我们发现当数组名用于表达式时自动转换为数组首地址
二维数组:
int a[2][2]={1,2,3,4};
(gdb) p a
$1 = {{1, 2}, {3, 4}}
(gdb) p &a[0]
$2 = (int (*)[2]) 0xbffff0b8 //因为是二维数组,所以&a[0]是数组指针
(gdb) p &a[0]+1
$3 = (int (*)[2]) 0xbffff0c0 //所以+1,加的是2*sizeof(int)
(gdb) p &a
$4 = (int (*)[2][2]) 0xbffff0b8 //对数组名取地址代表的是整个数组的首地址,有点整体的意思
(gdb) p &a+1
$5 = (int (*)[2][2]) 0xbffff0c8 //所以+1加的是sizeof(a)
(gdb) p a==&a
$7 = 1
(gdb) p a==&a[0]
$8 = 1
(gdb) p a==&a[0][0]
$9 = 1
其实从值的角度来说,a,&a,&a[0],&a[0][0]都是数组首地址的值
int a[2][2]={1,2,3,4};
int (*p)[2]=a;
如果我要输出3:
printf("%d\n",**(p+1)); //3
这个p已经是指针了,为啥要**呢,难道这是指针的指针?
在一维数组中我们知道: *(a+i)=a[i],在二维数组中同样成立.
*(p+1)=a[1],只不过此时的a[1]代表的是第二行的首地址,所以会出现**的问题.
printf("%d\n",*(int*)(p+1)); // 3,这样写我想更清楚,易懂
我们来点难点的吧?
short a[2][2]={256,256,256,256};
short *q=a[1];
printf("%d\n",**(char(*)[2])q); // 0,你对了吗?
q指向的是a[1]首地址,即256,然后将256当作2列char型数组,00000001 00000000,
所以最后输出来的是低地址的值->0
再来一个更变态的吧!!
char a[2][4]={8,7,6,5,4,3,2,1};
short *q=a;
printf("%d\n",*(*(((short(*)[2])q)+1)+1)); //小段字节序算
答案是258,你做对了吗?
解释:
(short(*)[2])q -> 将q转换为short型的数组指针
*(((short(*)[2])q)+1) -> 此时指向的是4
*(*(((short(*)[2])q)+1)+1) -> 此时指向的是2,00000010 00000001,而00000010是低地址,
所以最后为00000001 00000010 -> 258
指针本质论
最新推荐文章于 2022-03-26 18:18:37 发布