指针本质论

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


你可能感兴趣的:(c)