指针本质论 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