C语言指针(中)(指针常量)(指针变量)(小端存储)(深度理解指针操作内存)

首先给所有读者提醒到,指针的知识,所有的代码都是在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个字节
打印结果为:
64位平台测试
之前我们在访问地址指向的变量时候是这样访问的:

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++的环境下进行测试的。如果在其他平台,每次编译的时候变量的地址都会变,所以是没有办法通过直接访问具体的地址来访问到变量的。

之后我们可以定义一个指针变量把地取出来的址存放起来,并且通过地址来操纵内存。并且我们强调单纯的说指针只是一个地址是没有意义的,指针必须有类型,才能确定在地址之后的寻址内存大小及就是寻址能力。

发布了84 篇原创文章 · 获赞 71 · 访问量 9087

猜你喜欢

转载自blog.csdn.net/qq_43648751/article/details/102785972