在minStackCreate() 之后,在minStackPush() 中遇到obj->leading += sizeof(int); 时,obj->leading 立即指向在minStackCreate() 中分配的内存块MinStack *stack = malloc(sizeof(MinStack)); 当*(obj->leading) = x; 尝试将整数分配给地址时, 未定义的行为被调用。
内存使用/错误检查
一个简单的内存使用/错误检查详细说明了问题。
$ valgrind ./bin/minstack
==22103== Memcheck, a memory error detector
==22103== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==22103== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==22103== Command: ./bin/minstack
==22103==
Current Stack Size: 1
Stack Size:1 Last Operation: pushed -2
0:-2
==22103== Invalid write of size 4
==22103== at 0x4006A6: minStackPush (minstack.c:36)
==22103== by 0x4007C9: main (minstack.c:62)
==22103== Address 0x51f40b0 is 12 bytes after a block of size 4 free'd
==22103== at 0x4C3023F: realloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==22103== by 0x40068F: minStackPush (minstack.c:35)
==22103== by 0x4007C9: main (minstack.c:62)
==22103== Block was alloc'd at
==22103== at 0x4C2E01F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==22103== by 0x4005D0: minStackCreate (minstack.c:18)
==22103== by 0x400792: main (minstack.c:59)
==22103==
Current Stack Size: 2
Stack Size:2 Last Operation: pushed 0
0:-2
==22103== Invalid read of size 4
==22103== at 0x400753: print_state (minstack.c:50)
==22103== by 0x4007DA: main (minstack.c:63)
==22103== Address 0x51f4540 is 8 bytes after a block of size 8 alloc'd
==22103== at 0x4C3023F: realloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==22103== by 0x40068F: minStackPush (minstack.c:35)
==22103== by 0x4007C9: main (minstack.c:62)
==22103==
1:0
==22103== Invalid write of size 4
==22103== at 0x4006A6: minStackPush (minstack.c:36)
==22103== by 0x4007EB: main (minstack.c:64)
==22103== Address 0x51f40c0 is 16 bytes after a block of size 16 in arena "client"
==22103==
==22103== Invalid read of size 4
==22103== at 0x4006DE: minStackPush (minstack.c:40)
==22103== by 0x4007EB: main (minstack.c:64)
==22103== Address 0x51f40c0 is 16 bytes after a block of size 16 in arena "client"
==22103==
X is -3, so clearly obj->top_ptr = -3
Current Stack Size: 3
Stack Size:3 Last Operation: pushed -3
0:-2
==22103== Invalid read of size 4
==22103== at 0x400753: print_state (minstack.c:50)
==22103== by 0x4007FC: main (minstack.c:65)
==22103== Address 0x51f4590 is 4 bytes after a block of size 12 alloc'd
==22103== at 0x4C3023F: realloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==22103== by 0x40068F: minStackPush (minstack.c:35)
==22103== by 0x4007EB: main (minstack.c:64)
==22103==
1:0
2:0
==22103== Invalid write of size 4
==22103== at 0x4006A6: minStackPush (minstack.c:36)
==22103== by 0x40080D: main (minstack.c:66)
==22103== Address 0x51f40d0 is 32 bytes before a block of size 1,024 in arena "client"
==22103==
==22103==
==22103== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==22103== Access not within mapped region at address 0x1051F40C4
==22103== at 0x4006DE: minStackPush (minstack.c:40)
==22103== by 0x40080D: main (minstack.c:66)
==22103== If you believe this happened as a result of a stack
==22103== overflow in your program's main thread (unlikely but
==22103== possible), you can try to increase the size of the
==22103== main thread stack using the --main-stacksize= flag.
==22103== The main thread stack size used in this run was 8388608.
==22103==
==22103== HEAP SUMMARY:
==22103== in use at exit: 48 bytes in 2 blocks
==22103== total heap usage: 6 allocs, 4 frees, 1,096 bytes allocated
==22103==
==22103== LEAK SUMMARY:
==22103== definitely lost: 0 bytes in 0 blocks
==22103== indirectly lost: 0 bytes in 0 blocks
==22103== possibly lost: 0 bytes in 0 blocks
==22103== still reachable: 48 bytes in 2 blocks
==22103== suppressed: 0 bytes in 0 blocks
==22103== Rerun with --leak-check=full to see details of leaked memory
==22103==
==22103== For counts of detected and suppressed errors, rerun with: -v
==22103== ERROR SUMMARY: 7 errors from 6 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
在调用未定义行为时还有两个问题。
if (x == -3){ // for debug purposes
printf("X is -3, so clearly obj->top_ptr = %d\n", obj->top_of_stack_ptr);
}
您试图用%d 打印一个整数指针。如果要打印指针地址,请使用 %p 并将指针转换为 void,例如使用:
printf("X is -3, so clearly obj->top_ptr = %p\n", (void*)obj->top_of_stack_ptr);
如果你确实想输出整数值,你需要:
printf("X is -3, so clearly obj->top_ptr = %d\n", *obj->top_of_stack_ptr);
你有类似的问题:
int value = **((stack->stack) + sizeof(int) * i);
不清楚你是如何编译的,因为这会导致:
minstack.c:49:17: error: invalid type argument of unary ‘*’ (have ‘int’)
int value = **((stack->stack) + sizeof(int) * i);
你有太多的一级间接。要修复,您需要:
int value = *((stack->stack) + sizeof(int) * i);
您将需要解决这些问题。
始终在启用警告的情况下进行编译,并且不要接受代码,直到它在没有警告的情况下进行编译。要启用警告,请将-Wall -Wextra -pedantic 添加到您的gcc/clang 编译字符串(也可以考虑添加-Wshadow 以警告阴影变量)。对于 VS(Windows 上为cl.exe),请使用/W3。所有其他编译器将具有类似的选项。阅读并理解每个警告——然后去修复它。他们将识别任何问题,以及它们发生的确切线路。通过聆听编译器告诉您的内容,您可以学到很多东西。
如果您在进一步解决这些问题时遇到问题,请在下方发表评论,我们很乐意为您提供进一步帮助。
修复索引
清除上面指出的错误后,您的下一个最大问题是:
int value = *((stack->stack) + sizeof(int) * i);
您的价值在:
int value = stack->stack[i];
通过包含(address) + (4 * i),您的索引远远超出了尝试打印该值的分配存储空间。
分配逻辑
您的分配逻辑过于复杂。在MinStack *minStackCreate() 中,您正在创建一个MinStack 对象,因此只需为此进行分配。你还不需要为stack->stack分配,你没有它的价值,所以只需初始化stack->stack = NULL;,例如
MinStack *minStackCreate()
{
MinStack *stack = malloc(sizeof(MinStack));
stack->current_stack_size = 0;
stack->stack = NULL;
return stack;
}
现在在int *minStackPush(MinStack *obj, int x) 中,您不需要if ... then ... 条件来处理添加第一个值或后续值。 stack->stack 初始化为 NULL,所以只需调用 realloc() 为一个额外的整数添加空间并适当地更新指针,例如
int *minStackPush(MinStack *obj, int x)
{
/* always realloc using temporary pointer to prevent memory leak on failure */
void *tmp = realloc (obj->stack, (obj->current_stack_size + 1) * sizeof *obj->stack);
if (!tmp) {
perror ("realloc-obj->stack");
return NULL;
}
obj->stack = tmp;
obj->leading = obj->stack + obj->current_stack_size;
obj->top_of_stack_ptr = obj->leading;
*obj->leading = x;
obj->current_stack_size++;
return obj->stack;
}
(注意:我已将返回类型更改为int*。您在函数内分配内存,因此您需要一种方法来确定成功/失败。这不是“如果”的问题重新分配将失败,这是重新分配失败的“时间”问题。这与您的回报无关(例如int或int*等...)。事实是您可以确定是否分配成功或失败很重要。选择一个方便合理的返回类型,你可以在调用函数的地方合并返回检查)
您的printf() 造成了无穷无尽的问题。如上所述,您试图访问超出分配范围的内存。你有一个对象stack,并且该对象包含一个指向已分配整数值块的指针(例如stack->block——用于避免与stack->stack中的stack混淆的块)。只需将 block 索引为数组,例如stack->block[i]。当您需要在分配的块中索引一个整数时,只需像使用数组一样进行索引,例如
void print_state(MinStack *stack, char *op)
{
printf ("Stack Size:%d Last Operation: %s\n",
stack->current_stack_size, op);
for (int i = 0; i < stack->current_stack_size; i++)
printf("%d:%d\n", i, stack->stack[i]);
}
把精炼的逻辑放在一起,你会:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int current_stack_size;
int *stack; // base of stack
int *leading; // extreme top, to place new int
int *top_of_stack_ptr; // base of top, to retrieve
} MinStack;
MinStack *minStackCreate()
{
MinStack *stack = malloc(sizeof(MinStack));
stack->current_stack_size = 0;
stack->stack = NULL;
return stack;
}
int *minStackPush(MinStack *obj, int x)
{
/* always realloc using temporary pointer to prevent memory leak on failure */
void *tmp = realloc (obj->stack, (obj->current_stack_size + 1) * sizeof *obj->stack);
if (!tmp) {
perror ("realloc-obj->stack");
return NULL;
}
obj->stack = tmp;
obj->leading = obj->stack + obj->current_stack_size;
obj->top_of_stack_ptr = obj->leading;
*obj->leading = x;
obj->current_stack_size++;
return obj->stack;
}
void print_state(MinStack *stack, char *op)
{
printf ("Stack Size:%d Last Operation: %s\n",
stack->current_stack_size, op);
for (int i = 0; i < stack->current_stack_size; i++)
printf("%d:%d\n", i, stack->stack[i]);
}
int main(void)
{
MinStack *stack = minStackCreate();
for (int i = -5; i < 6; i++) {
char buf[32];
minStackPush (stack, i);
sprintf (buf, "pushed: %d", i);
print_state (stack, buf);
}
free (stack->stack);
free (stack);
}
(注意:您的 top_of_stack_ptr 和 leading 执行相同的功能。您不需要单独的指针来指向应该添加下一个 int 的位置,因为这没什么但是基于current_stack_size 的简单偏移量。所以删除一个或另一个。就个人而言,我会放弃leading,因为top_of_stack_ptr 是你真正想要的)
使用/输出示例
$ ./bin/minstack_fixed
Stack Size:1 Last Operation: pushed: -5
0:-5
Stack Size:2 Last Operation: pushed: -4
0:-5
1:-4
Stack Size:3 Last Operation: pushed: -3
0:-5
1:-4
2:-3
Stack Size:4 Last Operation: pushed: -2
<...snip...>
Stack Size:11 Last Operation: pushed: 5
0:-5
1:-4
2:-3
3:-2
4:-1
5:0
6:1
7:2
8:3
9:4
10:5
内存使用/错误检查
$ valgrind ./bin/minstack_fixed
==29254== Memcheck, a memory error detector
==29254== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==29254== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==29254== Command: ./bin/minstack_fixed
==29254==
Stack Size:1 Last Operation: pushed: -5
0:-5
Stack Size:2 Last Operation: pushed: -4
0:-5
1:-4
Stack Size:3 Last Operation: pushed: -3
0:-5
1:-4
2:-3
<...snip...>
Stack Size:11 Last Operation: pushed: 5
0:-5
1:-4
2:-3
3:-2
4:-1
5:0
6:1
7:2
8:3
9:4
10:5
==29254==
==29254== HEAP SUMMARY:
==29254== in use at exit: 0 bytes in 0 blocks
==29254== total heap usage: 13 allocs, 13 frees, 1,320 bytes allocated
==29254==
==29254== All heap blocks were freed -- no leaks are possible
==29254==
==29254== For counts of detected and suppressed errors, rerun with: -v
==29254== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
查看一下,如果您还有其他问题,请告诉我。