嗯,我一直认为如果我调用malloc函数,我会分配特定的内存量,但是,我刚刚意识到,如果我写:
int* a = (int*)malloc(sizeof(int) * 2);
我可以为a[4]
或任何其他索引分配一个值,我认为在这种情况下,我只能为a[0]
或a[1]
分配。我有什么概念错误?
当您编写a[4]
时,它与编写*(a + 4)
相同。由于编译器不知道a
所指向的地址分配了多少内存,因此它会很乐意让您对内存进行寻址。
然而,位于那里的内存可能是任何东西——它可能是程序使用的另一个变量,堆栈的一部分,或者刚好超出程序的范围。以这种方式访问分配的空间之外可能(往好了说)会产生分段错误,或者(往坏了说)通过覆盖程序的其他部分而引入安全漏洞。
您是正确的,因为您只能安全地分配给a[0]
或a[1]
,但C编译器会允许您在该界限之外进行分配(因为它不知道任何不同)。
在您的示例中执行a[4]
是不安全的。
此外,最好不要投射malloc的结果-请参阅此答案
在C中,没有办法检查数组是否溢出。您可以继续写入数组之外的内容,直到写入到无效地址或只读页面等。
有一些可用的工具可以让您立即检测,例如当您越过阵列边界时。NAMD就是这样一个工具,它将数组边界之外的直接内存位置设置为只读。
当您访问只读内存时,它会给出SIGSEGV。因此您可以立即检测到数组溢出。
概念错误是相信C会保护你!C相信你知道自己在做什么。您只能真正使用索引0或1,但这不会阻止您使用4(操作系统可能会这样做)。
可以执行a[4]
的原因是C不执行任何边界检查。你可以通过数组的边界访问一个单元格,C会这样做
问题是,这是一种非常糟糕的做法,可能是一个安全漏洞。你不应该这样做,因为这可能会导致非常糟糕和不可预见的后果。
未定义的行为就是未定义的。它可能看起来像是在"工作",但事实并非如此。
扩展Keith的答案:您可以覆盖堆上的内存,因为C不进行编译时或运行时绑定检查。a[x]基本上将x * sizeof(x)
添加到指针"a"。指针a指向mallocated块的开头。