通过C教程
#include <stdio.h>
int main() {
short s = 10;
int i = *(int *)&s; // wonder about this
printf("%i", i);
return 0;
}
当我告诉C s
的地址是一个int时,它是否应该读取4个字节?
从s
的2个字节的最左侧开始。在这种情况下这不是非常危险,因为我不知道它是什么读取,因为短路只分配了2个字节?
如果尝试访问我没有分配/属于我的内存,这不应该崩溃吗?
答案 0 :(得分:5)
正如您所指出的那样,它将读取比实际分配的字节更多的字节,因此它从内存中读取一些垃圾值,而不是由您的变量分配。
实际上它很危险,它会打破 Strict Aliasing Rule [详细信息如下] 并导致未定义行为。
编译器应该给你这样的警告。
warning: dereferencing type-punned pointer will break strict-aliasing rules
当你的编译器发出警告时,你应该总是听你的编译器。
[详情]
严格别名是由C(或C ++)编译器做出的假设,即取消引用指向不同类型对象的指针永远不会引用相同的内存位置(即别名。)< / p>
规则的例外是char *,允许指向任何类型。
答案 1 :(得分:3)
首先,永远不要这样做。
至于它为什么不崩溃:因为s
是本地的,所以它被分配在堆栈上。如果short
和int
在您的体系结构中有不同的大小(这不是给定的),那么您可能最终会从内存中读取更多字节内存页面作为堆栈;所以并且不存在访问冲突(即使你会阅读垃圾)。
<强>可能。强>
答案 2 :(得分:1)
正如你所说,这是危险和不明确的行为。
它在32(或64)位平台上不崩溃的原因是大多数编译器为每个堆栈变量分配至少32位。这使得访问速度更快,但是例如8位处理器,你会得到高位的垃圾数据。
答案 3 :(得分:1)
不,它不会使你的程序崩溃,但它会在堆栈上读取其他变量(或可能是垃圾)的一部分。我不知道你从中获得了什么教程,但这种代码很可怕。
答案 4 :(得分:1)
首先,所有地址都具有相同的大小,如果你是64位架构,每个char *,short *或int *将有8个字节。 在&符号之前使用星号时,它将取消效果,因此*&amp; x在语义上等同于x。
答案 5 :(得分:1)
基本上你是正确的,因为你正在访问一个int *指针,这将获取4个字节而不是仅为's'存储保留的2个字节,结果内容将不会完美地反映出什么' s'真的意味着。
然而,这很可能不会崩溃,因为's'位于堆栈上,所以根据你的堆栈在这一点上的布局,你很可能会读取在'main'函数序言中推送的数据...
如果程序因读取内存访问无效而崩溃,则需要访问未映射的内存区域,这将在用户世界级别触发“分段错误”,而内核级别发生“页面错误”。通过'映射',我的意思是你在虚拟内存区域和物理内存区域之间有一个已知的映射(这种映射由操作系统处理)。这就是为什么如果你访问一个NULL指针,你会得到这样的异常,因为在userworld级别没有有效的映射。通常会通过调用类似malloc()的方式为您提供有效的映射(请注意,malloc()不是系统调用,而是管理虚拟内存块的智能包装器)。您的堆栈也不例外,因为它只是内存,但已经为您完成了一些预映射区域,因此当您在块中创建局部变量时,您不必担心它的内存位置,因为它已被处理在这种情况下,您访问的距离远远不足以达到非映射的内容。
现在让我们说你做那样的事情:
short s = 10;
int *i = (int *)&s;
*i = -1;
然后在这种情况下,您的程序更可能崩溃,因为在这种情况下您开始覆盖数据。根据您接触到的数据,如果您覆盖堆栈中推送的返回地址,可能会造成无害的程序错误行为,也可能会导致程序崩溃......数据损坏对我来说是最困难的(如果不是最难的话) )要处理的错误类别,因为它的效果会随着非确定性模式随机影响你的系统,并且可能在实际执行原始违规指令很久之后发生。
如果您想了解有关内部内存管理的更多信息,可能需要查看操作系统设计中的Virtual Memory Management。
希望它有所帮助,