手绘知识点——指针运算&变量的内存分配原理

九月的最后一天,首先祝我们的祖国生日快乐,让我们继续砥砺前行……

来到了指针系列的第三篇,我们来说说指针的运算以及变量在内存中的存储问题,重点在于后者。

首先看一下指针的算术运算:

int a = 1,*pa=&a,*pa1=&a;
double b=3.1415,*pb=&b;

printf("before pa++,pb++:\n");
printf("&a=%#x,pa=%#x,pa1=%#x,&b=%#x,pb=%#x\n", &a,pa,pa1,&b,pb);

pa++; pb++;
printf("after pa++,pb++:\n");
printf("&a=%#x,pa=%#x,pa1=%#x,&b=%#x,pb=%#x\n", &a, pa, pa1, &b, pb);
if (pa==pa1)
     {
	printf("pa和pa1指向同一数据\n");
     }
else
     {
	printf("pa和pa1指向不同数据\n");
     }

我们定义了整型变量a以及指向它的两个指针pa和pa1,浮点型变量b以及指向它的指针pb,建议大家以后定义指针用这种形式,避免我上次犯的错误,误将pb定义成了int *型;

看结果:

在指针++前后做对比可以发现,整型指针pa++后值增加了4,而浮点型指针pb增加了8,这正是对应数据类型所占的内存大小,反映到图上大概是这样的:

因为一个整型变量a占据4个字节,而内存的分配基本单位为1个字节,所以pa++后pa应前进4个字节指向如上图所示位置,假如pa++后pa前进1个字节,那pa将把其后的四个字节当作一个整体,其中前三个字节属于变量a,最后一个字节并不知道是个啥,所以最终的输出也就很难保证了。最后的pa和pa1比较的是他们的值,即保存的地址,由于二者所保存的地址不一样,所以输出为pa和pa1指向不同的数据。

第二部分我们说一下变量在内存中的分配问题,其实这个东西在前两篇都已经提到了,我们这次再深入一些~

int g=0,x = 1, y = 2, z = 3;
int *m = &z;
for (int i = 0; i<20; i++) {
	printf("%d, ", *(m + i));
}
printf("\n");

int g1=0,x1 = 4,  y1= 5, z1 = 6;
int *m1 = &z1;
for (int i1 = 0; i1<20; i1++) {
	printf("%d, ", *(m1 + i1));
}
printf("\n");

int  g2=0,x2= 7, y2 = 8, z2 = 9;
int *m2 = &z2;
for (int i2 = 0; i2<20; i2++) {
	printf("%d, ", *(m2 + i2));
}
printf("\n");

printf("m=%#x,m1=%#x,m2=%#x\n", m, m1, m2);
printf("*(m2 -3) = %#x,*(m2 -2) = %d,*(m2 -1) = %d\n",*(m2 - 3), *(m2 - 2), *(m2 - 1));

看着又是好大一坨,其实都是一种形式,这一块我做了多次试验,不断地添加东西,所以现在看着有些杂乱,咱们还是长话短说,从上述代码大家应该能看到,我们连着定义了很多变量,然后使用for循环输出,一开始的时候只有xyz三个变量,循环输出8个值,感觉不是很明显,之后又加到循环输出12个值,此时可以看出一些端倪:

当然这时候还没有写最后一条输出语句~大家可以先看最后的m、m1和m2,地址依次减小,从整体上符合我们之前讨论过的地址的分配原则(变量入栈,后进先出,所以后定义的变量先分配地址),然后再看上边输出的三行数据,依然是满足“后定义先分配”的原则,先输出z,之后依次是y和x,只不过这中间还有些其他数据,而且多次实验证明这个“其他数据”还是固定的-858993460,而且出现的很有规律,都在两个连续定义的变量中间出现两次,而这个值正是debug模式下编译器对未初始化的变量赋的默认值~~~

并且大家可以看到,m、m1和m2之间是有关系的,两两之差都为60,即60个内存单元(字节),而一个int型变量占据4个字节,每两个变量之间还有两个默认值共占8个字节,从上述代码可以看到我们定义了四个变量x-y-z-m-i,这样每一部分共占15字节大小(大家可以理解成每个变量后跟两个整型的默认值),正是每一部分起始地址之差~~

怎么样,是不是感觉已经乱了,这一块真的是好说不好写啊,我们还是直接看图吧:

为了方便我直接将上图中的一小块写成4字节,这样看起来比较直观,大家可以发现每一部分的样式是相同的,从m2开始依次存储,其中XX代表默认值-858993460;(以下这一部分实在惭愧,大家可以直接跳到最后看总结)

这里边最大的亮点就是我们上图标出的m1,由于这个值是以十进制输出的,并且每次运行都会改变,所以我立即就想到了这是不是某个地址,因为我们定义的这些变量在不同批次的运行时所分配的内存空间都不一样,果不其然,十进制转十六进制,就是下一个部分的起始地址,而大家需要注意的是这个地址是在每一部分开始前的内存空间中,基于这样的原理我们可以假设最开始分配空间的m2的地址在其后方三个整型存储空间大小的位置,于是我们使用上述代码的最后一行依次输出:

不出所料,m2-3所指向的内存空间的值正是m2的值,这里需要说明的是(m2 - 3)是个指针,大家要分清指针的值和指针所指向数据的值的区别,即m2 - 3和 *(m2 - 3)的不同,对应于上边的手工画图,前者为0x73f91c,后者为0x73f928

为了更加直观我们可以看一下上述代码最终的输出效果,看看每一部分输出20次并着增加一个变量g之后是怎么样的结果:

不难看出,变量的输出顺序和我们想象的一样,并且这些空间可以理解成是在一定基础上连续的,比如倒数第二行,这是第三部分的内容,当输出到后边出现了“6”,而这正是第二部分开始输出的内容,这样三部分内容就连起来了~~~

这其中还有一个问题就是第一行的输出并不按套路出牌,i的值20没有正常输出,而是显示的11795016,再往后也是一塌糊涂,并且每次运行所得结果都在变化,由此可以推想这一块也是系统随机分配的~

当然这一块内容细说起来还是挺多的,大家可以尝试修改数据类型为double,看看会有什么变化,那个默认值-858993460会以怎样的形态出现,自己动手运行一下,会有惊喜。。

大概就这些了,又是相当啰嗦,总结一下吧:

1. 指针的算术运算所移动的单位量取决于所指向的数据类型,这也体现了定义指针时的类型要和所指数据类型一致的重要性;

2. 连续定义的变量之间地址是不连续的(通常情况是这样,如果有例外还请大家不吝赐教),中间会有一些辅助性的数据(通常为两个四字节的默认值);并且越先定义的变量分配的地址越靠后,这其中需要注意的是比如我们定义int a=1,b=2,c=3;   int d=4;那么d的地址是最先分配的,其次是c、b、a~

ok,现在说一下刚刚发现的一个问题,就在刚才我试着运行上边定义的abcd,结果跟想象的不一样,本以为按照前边的输出,d的地址应该是靠后的,木有想到其地址是最先分配的,此刻突然想到前边提到的第一行中的变量i没有按套路出牌输出20,因为这里根本就不是第一部分的变量i!!!!大家看一下我们定义变量的顺序,按照先定义后分配的原则应该是先分配给i地址,之后是指针m,再往后是zyx,这个原则不管是在全局还是局部都是成立的,也就是说我的手工画图中的那个变量i不是第三部分的而是第二部分的!!!这么重要而浅显的一点我竟然没有意识到。。。总之就是我们定义的这三部分正确的打开方式应该是从变量i开始,依次存储的是i-m-z-y-x。。

好了,就这样吧,老了老了,脑子是真的反应迟钝,让我缓缓,各位午安……

如果文章对您有一点点帮助,还请打赏一二,您的鼓励将是我前进的不竭动力

 

 

公众号为“非著名IT表演艺术家”,比较中二的名字,就是灵光一闪,然后这个名字就冒出来了……


 

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

beyond_LH

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值