c-为什么我没有得到分段错误



我有

x=(int *)malloc(sizeof(int)*(1));

但是我仍然能够读取CCD_ 1或CCD_。

我怎样才能获得这些价值观?访问这些内存时不应该出现分段错误吗?

基本前提是Sourav Ghosh的答案:访问malloc返回的内存超过您要求的大小是未定义的行为,因此一致性实现几乎可以做任何事情,包括愉快地返回奇异值

但是给定一个";正常的";在主流操作系统上的实现;正常的";机器(gcc/MSVC/clang、Linux/Windows/macOS、x86/ARM)为什么有时会出现分段错误(或访问违规),有时却不会?

几乎每";常规的";当通过指针1读/写时,C实现不执行任何类型的内存检查;这些加载/存储通常被直接转换为相应的机器代码,该机器代码访问给定位置的存储器而不太考虑"内存"的大小;抽象C机";对象。

然而,在这些机器上,CPU不会直接访问PC的物理存储器(RAM),而是引入了转换层(MMU)2;每当你的程序试图访问一个地址时,MMU就会检查是否有任何东西被映射到那里,以及你的进程是否有权在那里进行写入。如果这些检查中的任何一个失败3,则会出现分段错误,进程将被终止。这就是为什么未初始化和NULL指针值通常会给出不错的segfault:虚拟地址空间开头的一些内存被保留为未映射,只是为了发现NULL的解引用,通常,如果你随机将一个飞镖扔到32位地址空间(或者更好的是,64位地址空间),你最有可能找到从未映射到任何东西的内存区域。

尽管它很好,MMU无法捕获所有的内存错误,原因有几个。

首先,内存映射的粒度与大多数";普通的";拨款;在PC上,内存页(可以映射并具有保护属性的最小内存单元)的大小通常为4KB。当然,这里有一个折衷方案:非常小的页面本身需要大量内存(因为每个页面都有一个目标物理地址和保护属性,这些属性必须存储在某个地方),并减慢MMU的操作3。因此,如果您访问内存超出";逻辑";边界,但仍在同一内存页内,MMU无法帮助您:就硬件而言,您仍在访问有效内存。

此外,即使你离开了分配的最后一页,也可能是后面的页面是";有效的";就硬件而言;事实上,对于从所谓的堆(malloc&friends)获得的内存来说,这是非常常见的。

这源于这样一个事实,即malloc,对于较小的分配,不要求OS";新的";存储器块(理论上可以被分配以在两端保持保护页);相反,C运行时中的分配器以大的顺序块向操作系统请求内存,并在逻辑上将它们划分在较小的区域中(通常保存在某种链表中),这些区域在malloc上分发并由free返回。

现在,当你在程序中超出了请求内存的边界时,你可能不会得到任何错误:

  • 您正在使用的内存块不在页面边界附近,因此您的越界读取不会触发访问冲突;

  • 即使它位于页面的末尾,后面的页面仍然被映射,因为它仍然属于堆;它可能是给进程中其他代码的内存(因此您正在读取代码中某个不相关部分的数据),也可能是空闲内存区域(因此您在读取块的前一个所有者在freed-it时留下的任何垃圾),或者是分配器用来保存其记账数据的区域(因此,您正在读取此类数据的一部分)。

    在所有这些情况下;自由块";首先,即使在那里进行编写,也不会出现分段错误,但可能会损坏不相关的数据或堆的数据结构(这通常会导致稍后崩溃,因为分配器发现其数据不一致)。


票据

  1. 尽管现代编译器提供了特殊的插入指令的构建来捕获其中的一些错误;gcc和clang尤其提供了所谓的";地址消毒剂">
  2. 这允许引入透明分页(在物理内存可用性较低的情况下交换到未被积极使用的磁盘内存区域),最重要的是,内存保护和地址空间分离(当用户模式进程运行时,它"看到"一个完整的虚拟地址空间,只包含他的东西,而不包含来自其他进程或内核的任何东西)
  3. 操作系统被通知进程试图访问已交换的内存,这并不是故意设置的故障
  4. 考虑到每次访问内存都需要通过MMU,映射必须非常快,因此最常用的页面映射都保存在缓存中;如果将页面设置得很小,并且缓存可以容纳同样多的条目,那么缓存覆盖的内存范围就会更小

否,访问无效内存是未定义的行为,分段故障是UB的众多副作用之一。不能保证。

也就是说,

  • 在使用返回的指针之前,始终通过对照x[20]1检查返回的指针来检查malloc()是否成功
  • 请看这个:我是否投射malloc的结果

相关内容

  • 没有找到相关文章