【 在 sqsl 的大作中提到: 】
: 如下一段程序:
:
: #include <stdlib.h>
: ...................
有两个不懂的地方请教一下:
(1)开辟的是40个字节的空间,为什么程序允许b可以大于20也不报错?
(2)当b<=12时,a是40,当b>=13时,a是72,这是为什么呢?
求指点,谢谢
1) 楼主勇于实践是值得鼓励的, 但是, 这里有点逻辑不严谨。
为什么程序允许b可以大于20也不报错? 其实, 严谨的说法是:
为什么b可以20这次也不报错?
你试验的是一次, 只能得出一次结论,事实上, 你使用各种各样的平台,
编译器是有可能得到不同的结果。
没有做错误检查, 这个主要的原因是性能考虑,毕竟,一块内存它仅仅是一块内存,
A) 对于CPU来说,它没必要做语义上的区分, 而这种区分有更高层来处理, 比如,
到了操作系统层面, 可以利用CPU提供的接口,把内存做安全等级区分,intel的x86架构, 就可以
划分出环0, 到环3,多个级别。如果, 读写跨越了等级权限, 则会触发异常,一个简单的方法就是
你把指针赋值一个不是特别大的数值, 然后去访问,大概率会触发保护异常。
B)对于程序逻辑的语义, 在由程序员和编译器来负责。而像堆栈这种由系统负责管理的内存资源,
那么, 最终还是要由操作系统负责分配管理, 因此, 最终的实现还是会到操作系统运行时库负责。
到这个时候, 不同的硬件架构, 不同的操作系统, 有可能表现出,不一样的行为,就不奇怪了。
比如还是x86的CPU, 虽然, 它把分段内存分为代码段, 和数据段, 但是,实际上一致是可以混合使用的, 也就是代码段里面依然可以写入数据, 写入的数据依然可以做代码译码, 数据段依然也可以写入代码编码作为数据, 而且, 也可以加载这些动态代码数据, 作为CPU指令让CPU执行。
这极大增加了程序的灵活性, 这种动态特性, 也让程序放手做很多想做的事情, 比如动态指令替换,
动态升级, 更容易的调试器实现, 甚至可以实现远程的热补丁
当然, 坏处也不少, 比如各种臭名昭著的漏洞。各种不得不打的安全补丁导致的各种复杂问题。
其实, 我相信, 中国确实应该可以做出,更简洁更安全架构的CPU,这一点,反倒没那么多兼容包袱
(2)当b<=12时,a是40,当b>=13时,a是72,这是为什么呢?
越界后输出的这个72, 其实, 只是一种情况的结果, 实际内存分配, 并不是要几个字节给几个字节,
其实,也是按块对齐的。所以, 你分配40个字节, 操作系统实际分配的块可能是64字节或者128字节。
实际的字节, 可以确定的是, 你请求的字节, 还有记录和管理这次分配,需要等级的信息数据。因为
malloc是在堆上分配的, 因此,一般堆运行时库会需要记录等级信息, 这部分信息放在哪里? 实际上这个也是实现相关的, 一般简单的, 就是放在分配的数据块头部或者尾部, 把这些头尾占用刨去, 剩下的空间,才是能被程序使用的空间。
一般, 如果,程序是debug版本的, 方便跟踪越界和读写, 调试版本的运行时库还会做一些特殊处理,
比如, 分配的内存做特殊的初始值写入, 并对使用内存的首位添加栅栏, 来跟踪是不是有写入越界破坏了栅栏。
所以, 不必纠结是不是72, 编译个release版, 换个版本的编译器, 操作系统可能什么都变了。
--
FROM 115.171.244.*