C 在手动队列中推送太多的项目——为什么这不是分段错误?
我推了7个项目,而我只为5个项目malloc'd,但我没有看到任何故障发生。我错过了什么?我认为这会导致指针移动超过数组边界,并且C 在手动队列中推送太多的项目——为什么这不是分段错误?,c,segmentation-fault,C,Segmentation Fault,我推了7个项目,而我只为5个项目malloc'd,但我没有看到任何故障发生。我错过了什么?我认为这会导致指针移动超过数组边界,并且 #include "stdlib.h" #include "stdio.h" #include "assert.h" typedef struct { int *space; int size; int *start; int *end; } queue_t; typedef char BOOL; #define TRUE 1
#include "stdlib.h"
#include "stdio.h"
#include "assert.h"
typedef struct {
int *space;
int size;
int *start;
int *end;
} queue_t;
typedef char BOOL;
#define TRUE 1
#define FALSE 0
void queue_print(queue_t *queue) {
for (int *cur = queue->start; cur < queue->end; cur++) {
printf("%i,", *cur);
}
printf("\n");
}
void queue_init(queue_t **queue, int size) {
(*queue) = (queue_t*) malloc(sizeof(queue_t));
(*queue)->space = (int*) malloc(sizeof(int) * size);
(*queue)->size = size;
(*queue)->start = (*queue)->space;
(*queue)->end = (*queue)->space;
}
void queue_push(queue_t *queue, int elem) {
*(queue->end) = elem;
queue->end++;
}
int queue_pop(queue_t *queue) {
int ret = *(queue->start);
queue->start++;
return ret;
}
int main(int argc, char const *argv[])
{
queue_t *queue;
queue_init(&queue, 5);
queue_print(queue);
queue_push(queue, 1);
queue_print(queue);
queue_push(queue, 2);
queue_print(queue);
queue_push(queue, 3);
queue_print(queue);
queue_push(queue, 4);
queue_print(queue);
queue_push(queue, 5);
queue_print(queue);
queue_push(queue, 6);
queue_print(queue);
queue_push(queue, 7);
queue_print(queue);
printf("%i\n", queue->size);
queue->space[123] = 4;
return 0;
}
C不会检查越界访问,但您的程序会破坏堆,将来再次调用
malloc()
时,您可能会遇到segfault。您通过调用队列
分配空间来容纳5个整数。如果在gdb中运行代码,并检查queue->start
位置的内存,您将看到如下内容:
(gdb) p *queue
(gdb) x/8 0x602450
0x602450: 0 0 0 0
0x602460: 0 0 134049 0
(gdb) x/8 0x602450
0x602450: 1 2 3 4
0x602460: 5 6 7 0
您将看到起始成员的内存地址。在“检查内存”命令中使用该地址,如下所示:
(gdb) p *queue
(gdb) x/8 0x602450
0x602450: 0 0 0 0
0x602460: 0 0 134049 0
(gdb) x/8 0x602450
0x602450: 1 2 3 4
0x602460: 5 6 7 0
gdb将打印如下内容:
(gdb) p *queue
(gdb) x/8 0x602450
0x602450: 0 0 0 0
0x602460: 0 0 134049 0
(gdb) x/8 0x602450
0x602450: 1 2 3 4
0x602460: 5 6 7 0
第5字节之后的所有内容都属于未分配或先前分配的内容。在第7次调用queue_push并再次检查内存后,您会看到如下内容:
(gdb) p *queue
(gdb) x/8 0x602450
0x602450: 0 0 0 0
0x602460: 0 0 134049 0
(gdb) x/8 0x602450
0x602450: 1 2 3 4
0x602460: 5 6 7 0
因此,运行时不会阻止您覆盖未分配或以前分配的内存。但是,您可能写了一些重要的东西,这些东西可能会给运行时或流程中的其他线程带来问题。这很有趣。你愿意解释一下吗,或者指出一些我可以用来研究这个案例的信息来源?谢谢!这可能会有帮助:可能会导致。更改了答案以解决概率方面的问题。那么,您很可能在下一次malloc中遇到问题。:-)只需在主队列中复制您的代码,并尝试创建另一个队列。叮当作响的静态分析器警告我第二个队列中可能存在内存泄漏。非常好的解释!所以现在我明白发生了什么。但是,如果我可以在数组末尾之外写入,那么什么是segfault呢?我曾经说过:“有四种常见错误会导致分段错误:取消引用NULL、取消引用未初始化的指针、取消引用已释放(或删除,在C++中)或超出范围(在函数中声明数组的情况下)的指针,以及注销数组的结尾。”我想,注销数组的结尾并不总是导致分段错误。这取决于你写什么。一些内存管理器会自动检测到它并抛出运行时错误。此外,当您释放内存时,它很可能会抛出错误。因此,可以在
main
的return
之前添加这两行:free(queue->space);免费(排队)代码>是的。这会直接从内存分配器抛出一个错误。这有多有趣?TKS!SegFault与异常不同:当您违反规则时,它们不会发生,只有当破坏无法恢复时才会发生。您可能会写入超过数组末尾的内容,但幸运的是,该数组位于数组的开头附近,因此有“空间”来破坏其余内存,而不是立即分段出错。没错,malloc()
完全有可能返回指向堆中任何位置的指针。我想的是一个具体的情况,但页面与主要问题实际上并不相关,你似乎已经理解了这一点。