首先给所有读者提醒到,指针的知识,所有的代码都是在DEV-C++平台下完成。DEV-C++平台有一个好处就是定义的变量在再次编译的时候地址不会改变,对于我们很多理解有很大的帮助。
指针常量
我们首先提出两个概念:
指针是有类型的地址常量
对变量取地址,取出的地址就是一个指针,且是常量指针
(以上概念会在完整看完指针博客之后理解更加深刻)
第一点我们已经在指针(上)博客里面进行说明,下面我们将会使用另一种方法进行验证,也对于为什么在取地址的时候必须加上类型,因为类型决定我们从当前位置能够访问的字节数的范围。
我们对变量取地址:
char a;
short b;
int c;
double d;
printf("%x\t%d\n", &a,&a);
printf("%x\t%d\n", &b,&b);
printf("%x\t%d\n", &c,&c);
printf("%x\t%d\n", &d,&d);
运行结果为:
那么在初始化之后,对于变量进行取地址和解引用就可以得到变量的值:
对于变量使用取地址符号&取到了变量的地址,再对取到的地址进行解引用,也就是取值 * 操作,得到了变量的数据,可以理解为,先找到变量地址,再通过变量的地址访问到变量所保存的数据。
char a = 1;
short b = 2;
int c = 10;
double d = 123.45;
printf("%x\t%d\n", &a,&a);
printf("%x\t%d\n", &b,&b);
printf("%x\t%d\n", &c,&c);
printf("%x\t%d\n", &d,&d);
printf("a = %d\n",*(&a));
printf("b = %d\n",*(&b));
printf("c = %d\n",*(&c));
printf("d = %f\n",*(&d));
运行结果为:
那么既然我们之前说到了指针就是地址,现在我们知道了a,b,c,d的地址,那我们能不能直接通过16进制的地址访问到变量呢?
我们通过获得的16进制数据取地址获得指针指向的数据
char a = 1;
short b = 2;
int c = 10;
double d = 123.45;
printf("%x\t%d\n", &a,&a);
printf("%x\t%d\n", &b,&b);
printf("%x\t%d\n", &c,&c);
printf("%x\t%d\n", &d,&d);
printf("a = %d\n",*(&a));
printf("b = %d\n",*(&b));
printf("c = %d\n",*(&c));
printf("d = %f\n",*(&d));
printf("a = %d\n",*(0x62fe1f));
printf("b = %d\n",*(0x62fe1c));
printf("c = %d\n",*(0x62fe18));
printf("d = %f\n",*(0x62fe10));
以上两种方式是否都能打印出来变量的值呢?
我们在编译的时候直接出现错误:
说明是不允许的。
但是我们换一种方式:
把其中:
printf("a = %d\n",*(0x62fe1f));
printf("b = %d\n",*(0x62fe1c));
printf("c = %d\n",*(0x62fe18));
printf("d = %f\n",*(0x62fe10));
换成:
printf("a = %d\n",*((char*)0x62fe1f));
printf("b = %d\n",*((short*)0x62fe1c));
printf("c = %d\n",*((int*)0x62fe18));
printf("d = %f\n",*((double*)0x62fe10));
打印结果为:
我们通过以上方式证明:①指针等价于地址,②说明十六进制地址的时候必须要说明类型。
同时也就证明了我们刚开始提到的第一点:
指针是有类型的地址常量
接下来我们通过图解进行解释:
如果我们只是给了一个地址,那么我们从这个地址到底取到多长,我们是没有办法确定的,这也就是出错的原因。
其他类型变量证明类似。
常量指针不是一个单纯的地址,而是有类型的。(重点理解)
那么我们在这里就类型展开讨论:
在这里我们提到一个小端序和大端序的问题:
小端序就是内存中的小地址存储的是小数据,大地址存储的是大数据。
大端序就是内存中小地址存储的的是大数据,大地之存储的是小数据。
例如:
int data = 0x12345678;
那么在内存中的存储为:
也就是说,比较小的78存在最低为,最大的12存在了最高位,这就是我们的小端序,我们的计算机存储方式就是这种方式,手机的内存存储方式恰好相反。
接下来我们通过代码进行验证:
int data = 0x12345678;
printf("%x\n",&data);
printf("%x\n",*((char*)0x62fe1c));//起始地址相同
printf("%x\n",*((short*)0x62fe1c));//类型决定大小
printf("%x\n",*((int*)0x62fe1c));//同样的地址类型不同获取的长度也不同
当我们确定变量data的地址之后,我们可以通过类型确定我们访问的字节数。
例如我们通过char访问一个字节,short访问两个字节,并且打印。
打印结果为:
我们通过图解方式进行理解:
以上也就证明了打印出来:
78
5678
的原因
所以我们最终对于指针的结论就是:指针是一个有类型的地址。
地址我们已经早都知道了,32位机能够放出的地址,类型决定了从这个地址开始的寻址能力(也就是从一个地址开始能够寻址多少个字节的地址)
那么我们讲到指针常量,也就是说指针常量的地址是不允许被赋值的:
char a;
short b;
int c;
double d;
&a = 0x12345678;
出现错误:不允许a的地址被改变
所以我们接下来学习指针变量。
指针变量
声明一个指针变量保存:①地址数据,②类型
我们在指针上已经介绍了指针的定义和简单使用,在我们学习了指针常量之后,我们重新分析以下定义的方式:
Type * pointerName
那么我们在这里的解析就是:
表示是一个指针变量
Type表示该变量的内存放的地址的寻址能力
之前我们说指针==地址
那么他们的大小肯定的都是一样的了,我们在指针(上)的博客中已经给大家说到了,可能很多人忘记了,我们在这里对比打印,证明他们的大小是一样的。
char a;
short b;
int c;
double d;
char*aa;
short*bb;
int*cc;
double*dd;
printf("sizeof(&a) = %d\n", sizeof(&a));
printf("sizeof(&b) = %d\n", sizeof(&b));
printf("sizeof(&c) = %d\n", sizeof(&c));
printf("sizeof(&d) = %d\n", sizeof(&d));
printf("sizeof(char *) = %d\n", sizeof(aa));
printf("sizeof(short *) = %d\n", sizeof(bb));
printf("sizeof(int *) = %d\n", sizeof(cc));
printf("sizeof(double *) = %d\n", sizeof(dd));
这里我们在64位平台下验证,64位平台下,每个指针占8个字节
打印结果为:
之前我们在访问地址指向的变量时候是这样访问的:
char a = 1;
short b = 2;
int c = 3;
double d = 5.6;
printf("%d\t",*(&a));
printf("%d\t",*(&b));
printf("%d\t",*(&c));
printf("%f\t",*(&d));
打印结果为:
那么在定义了指针变量之后,我们可以换一种方式访问变量
char a = 1;
short b = 2;
int c = 3;
double d = 5.6;
char*aa = &a;
short*bb = &b;
int*cc = &c;
double*dd = &d;
printf("%d\t",*(&a));
printf("%d\t",*(&b));
printf("%d\t",*(&c));
printf("%f\n",*(&d));
printf("%d\t",*aa);
printf("%d\t",*bb);
printf("%d\t",*cc);
printf("%f\n",*dd);
打印结果为:
我们可以看出两种方式打印出来完全相同。
那么接下来我们要区分一下不同位置的的作用:
我们在定义的时候使用的 知识声明变量是指针变量,例如:int * p;
printf("%d\t",aa); 这里的表示取值,也就是我们理解的解引用。
那么之后我们就可以这样使用:
例如:
int data = 0x12345678;
int *pa = &data;
printf("%x\n",&data);
printf("%x\n",*(int *)pa);
printf("%x\n",*(short*)pa);
printf("%x\n",*(char*)pa);
printf("%x\n",*(int *)0x62fe14);
printf("%x\n",*(short*)0x62fe14);
printf("%x\n",*(char*)0x62fe14);
打印结果为:
那么我们就可以不需要知道变量的地址,直接进行访问和操作。
并且在这里要注意的是,我们这篇博客是在devc++的环境下进行测试的。如果在其他平台,每次编译的时候变量的地址都会变,所以是没有办法通过直接访问具体的地址来访问到变量的。