我正在学习如何在C中使用指针和结构。当然,我试图故意破坏我的代码以进一步理解语言的工作原理。这里有一些测试代码可以正常工作:
#include <stdio.h>
#include <stdlib.h>
struct pair {
int x;
int y;
};
typedef struct pair pair;
void p_struct( pair ); //prototype
int main( int argc, char** argv ) {
pair *s_pair;
int size, i;
printf( "Enter the number of pair to make: " );
scanf( "%d", &size );
getchar();
printf( "\n" );
s_pair = (pair*)malloc( size * sizeof(pair) );
for( i = 0; i < size; i++ ) {
s_pair[i].x = i;
s_pair[i].y = i;
p_struct( s_pair[i] );
}
getchar();
return (EXIT_SUCCESS);
}
void p_struct( pair s_pair ) {
printf( "\n%d %d\n", s_pair.x, s_pair.y );
}
如前所述,就我所知,这段代码是有效的。
然后我决定修改一部分代码:for( i = 0; i < size + 3; i++ ) {
s_pair[i].x = i;
s_pair[i].y = i;
p_struct( s_pair[i] );
}
此修改未产生我预期会出现的seg错误错误。尽管我使用scanf函数为我的变量 size 赋值时超出了我明确设置的缓冲区,但仍打印了所有“对”。
正如我理解指针(如果我错了,请纠正我),当我调用时,堆中的内存管理器会保留一个大小为 size * sizeof(pair)的连续内存块。我的指针类型对 s_pair 的malloc函数。我做的是当我将for循环修改为条件 i&lt;时,我超过了最后分配的内存地址。尺寸+3 。
如果我正确地理解了这一点,那么我的指针是否超出了它的保留内存限制而恰好是明确的,因为没有任何相邻的权利被其他数据占用?溢出缓冲区时这是正常的行为吗?
要添加,当我使用 i&lt;的强制循环条件进行测试时,我确实收到了seg错误。大小+ 15 。问题是,它仍然打印输出。同样,根据我制作的p_struct函数,当屏幕上的 size = 10 时,它会打印对“0 0”以配对“24 24”。只有当程序到达底部的其中一个getchar()之后,程序才会被seg故障崩溃。我的程序怎么能将值分配给超过缓冲区的对,在屏幕上打印它们,然后当它到达getchar()时突然决定崩溃为seg故障?它似乎没有问题 i&lt;大小+ 3 (尽管它仍然是错误的)。
为了记录,我还使用常规指针数组测试了这种行为:
int size, i, *ptr;
scanf( "%d", &size );
ptr = (int*)malloc( size * sizeof(int) );
for( i = 0; i < size + 15; i++ )
ptr[i] = i;
这产生与上面完全相同的结果。在 i&lt;大小+ 3 seg故障似乎没有任何问题。
最后,我也测试了一个数组:
int i, array[10];
for( i = 0; i < 25; i++ )
array[i] = i;
对于条件 i&lt; 25 ,我得到了一个段错误。当我将其更改为 i&lt; 15 ,我没有收到任何段错误。
如果我没记错的话,指针数组和数组之间的唯一区别是分配给数组的内存位于堆栈而不是堆(不确定这一点)。考虑到这一点,并考虑到 i&lt; 15 当 array [10] 没有产生任何seg错误时,为什么 i&lt; 25 是一个问题? for循环期间,数组不在堆栈顶部吗?当它不关心60个额外字节时,为什么要关心100个额外字节?为什么不是那个数组缓冲区的上限一直到为整个堆栈保留的任意内存块的末尾?
希望所有这一切对于决定阅读一个稍微醉酒的男人的谣言都是有道理的。
答案 0 :(得分:2)
如果我正确地理解了这一点,那么我的指针是否超出了它的保留内存限制而恰好是明确的,因为没有任何相邻的权利被其他数据占用?
差不多。除非您没有“明确”,因为相邻的东西可能 被其他数据占用,而您的代码只是踩在那个内存上并更改了值。您可能永远不会注意到问题,或者您可能会在以后发现问题。无论哪种方式,它都是未定义的行为。
答案 1 :(得分:2)
欢迎来到C的辉煌世界!
内存分配函数(malloc
,calloc
,realloc
等)为您提供堆上的内存。当你打电话给其中一个并且你的程序没有足够的空间时,它会使system call获得更多。但它并没有以精确的增量执行此操作(它通常会在一定数量的整页增量中执行此操作)。当您索引数组的末尾(或甚至在数组开头之前)时,您仍然在程序的合法地址空间的范围内。只有当您离开程序所拥有的细分时,才能获得Segmentation Violation。
我强烈建议您使用Valgrind来检查您的程序,特别是如果您故意通过破坏内容来了解内存。除此之外,它还会在分配的任一侧存储金丝雀值,以帮助您确定何时访问界限并警告您有关双重释放和内存泄漏的信息。
答案 2 :(得分:1)
当您调用malloc时,可能会获得比您需要的内存更多的内存,因为内存是以公共块大小的倍数分配的。如果块大小为64字节并且您只需要10个字节,那么操作系统将为您提供64个字节,因此您仍然可以访问超出请求范围的内存,这是您的程序正在观察的行为。
答案 3 :(得分:1)
正如其他人所说,未定义的行为并不意味着您的程序在任何情况下都会崩溃。
这完全取决于你在哪里覆盖数据。
为了帮助您了解真正发生的事情,打印地址(例如printf("%p\n", s_pair);
或类似的东西)可能会有所帮助,并且可以将程序编译为可读的汇编程序助记符(例如{{1 }})