数组元素即使在分配后也具有 0 值
Array element has 0 value even after assigning it
所以我正在用 c 实现堆栈。现在初始堆栈大小为 5,每次达到限制时我都会将其加倍。我将其中的值从 1 推入 20,然后将其一一弹出并打印出来。
这是输出: 20, 19, ..., 7, 0, 5, 4, 3, 2, 1
注意 0
而不是 6
这里是stack.c
:
#define INITIAL_CAPACITY 5
struct Stack
{
int size; // initial size = 5
int *data; // dynamically allocated array
int pointer; // position of the top element
};
struct Stack *create_stack()
{
int *array = calloc(INITIAL_CAPACITY, sizeof(int));
struct Stack *stack = malloc(sizeof(struct Stack));
if (array == NULL || stack == NULL)
{
printf("Memory allocation failed in create_stack");
exit(EXIT_FAILURE);
}
stack->size = INITIAL_CAPACITY;
stack->data = array;
stack->pointer = -1;
return stack;
}
static void resize(struct Stack *stack)
{
const int new_size = stack->size * 2;
stack->data = realloc(stack->data, (sizeof *stack->data) * new_size);
if (stack->data == NULL)
{
printf("Memory allocation failed in the resize function.\n");
exit(EXIT_FAILURE);
}
stack->size = new_size;
}
void push(struct Stack *stack, int element)
{
int *stack_data = stack->data; // --> I think the bug is here
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.");
resize(stack);
}
assert(stack->pointer < stack->size);
stack_data[stack->pointer] = element;
}
但是当我将推送功能更改为此时,一切正常并且输出正确:
void push(struct Stack *stack, int element)
{
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.");
resize(stack);
}
assert(stack->pointer < stack->size);
(stack->data)[stack->pointer] = element; // <--- changed line
}
现在输出正确吗?
发生了什么事?
这是问题所在:
void push(struct Stack *stack, int element)
{
int *stack_data = stack->data;
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.");
resize(stack);
// stack_data still points to old memory allocation
}
assert(stack->pointer < stack->size);
stack_data[stack->pointer] = element;
}
由于您在 resize()
中修改了 stack->data
,您保存的指针 stack_data
可能指向 resize()
之后的无效内存位置。您从未将新的 stack->data
分配给 stack_data
而您直接使用后者,这会导致内存访问冲突。
您的修复版本一直在使用 stack->data
,因此上述问题不再存在。
指针 stack->data
可能会被
更新
stack->data = realloc(stack->data, (sizeof *stack->data) * new_size);
在 resize
函数中。但是,它并没有反映到变量 stack_data
中,它仍然指向旧的和可能无效的地方。
要使该函数起作用,您必须在调用 resize
之后分配给 stack_data
。
void push(struct Stack *stack, int element)
{
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.");
resize(stack);
}
int *stack_data = stack->data; // move this after the call of resize()
assert(stack->pointer < stack->size);
stack_data[stack->pointer] = element;
}
当你调用函数 realloc
就像在这个语句中一样
stack->data = realloc(stack->data, (sizeof *stack->data) * new_size);
返回的指针不必与存储在用作函数参数的指针中的地址相同。
因此在函数push
的第一次实现中,指针stack_data
在其初始化后
int *stack_data = stack->data; // --> I think the bug is here
调用函数resize
后,不一定等于指针stack->data
的值
resize(stack);
因此这个语句
stack_data[stack->pointer] = element;
通常可以调用未定义的行为,因为可以使用指向已释放内存的指针 stack_data
。
在函数 push
的第二个实现中,使用了存储在数据成员 stack->data
中的重新分配内存的相同地址
(stack->data)[stack->pointer] = element;
所以第二个函数实现没有第一个函数实现中存在的错误。
注意push和resize函数不能发出任何消息。决定是否输出消息的是函数的调用者。所以该函数应该有一种方法来报告它们是否成功。
函数可以这样定义
static int resize(struct Stack *stack)
{
const int new_size = stack->size * 2;
int *tmp = realloc(stack->data, (sizeof *stack->data) * new_size);
int success = tmp != NULL;
if ( success )
{
stack->data = tmp;
stack->size = new_size;
}
return success;
}
int push(struct Stack *stack, int element)
{
stack->pointer = stack->pointer + 1;
int success = stack->pointer < stack->size;
if ( !success )
{
success = resize( stack );
}
if ( success ) stack->data[stack->pointer] = element;
return success;
}
问题出在您本地使用 stack_data
。请注意 realloc
完全可以 更改 整个内存区域。来自手册页:
If the area pointed to was moved, a free(ptr) is done.
当您调用 resize
(因此调用 realloc
)时,stack->data
的内存区域可能已更改位置。如果是这样,当您从 push
中的 resize
返回时,stack_data
不再指向有效的内存区域,实际上,访问 free
d 内存会调用 undefined行为。在那里使用本地真的没有意义,只需将 push
更改为
void push(struct Stack *stack, int element)
{
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.\n");
resize(stack);
// stack->data might point somewhere entirely different now.
}
assert(stack->pointer < stack->size);
// access stack->data directly, no need for a local
stack->data[stack->pointer] = element;
}
我创建了一个 mre that potentially illustrates this problem (took a few liberties with parts omitted in the OP). realloc
doesn't have to change the memory region, so you will only see your error if that happens: https://godbolt.org/z/4nEoM65Yc
所以我正在用 c 实现堆栈。现在初始堆栈大小为 5,每次达到限制时我都会将其加倍。我将其中的值从 1 推入 20,然后将其一一弹出并打印出来。
这是输出: 20, 19, ..., 7, 0, 5, 4, 3, 2, 1
注意 0
而不是 6
这里是stack.c
:
#define INITIAL_CAPACITY 5
struct Stack
{
int size; // initial size = 5
int *data; // dynamically allocated array
int pointer; // position of the top element
};
struct Stack *create_stack()
{
int *array = calloc(INITIAL_CAPACITY, sizeof(int));
struct Stack *stack = malloc(sizeof(struct Stack));
if (array == NULL || stack == NULL)
{
printf("Memory allocation failed in create_stack");
exit(EXIT_FAILURE);
}
stack->size = INITIAL_CAPACITY;
stack->data = array;
stack->pointer = -1;
return stack;
}
static void resize(struct Stack *stack)
{
const int new_size = stack->size * 2;
stack->data = realloc(stack->data, (sizeof *stack->data) * new_size);
if (stack->data == NULL)
{
printf("Memory allocation failed in the resize function.\n");
exit(EXIT_FAILURE);
}
stack->size = new_size;
}
void push(struct Stack *stack, int element)
{
int *stack_data = stack->data; // --> I think the bug is here
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.");
resize(stack);
}
assert(stack->pointer < stack->size);
stack_data[stack->pointer] = element;
}
但是当我将推送功能更改为此时,一切正常并且输出正确:
void push(struct Stack *stack, int element)
{
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.");
resize(stack);
}
assert(stack->pointer < stack->size);
(stack->data)[stack->pointer] = element; // <--- changed line
}
现在输出正确吗? 发生了什么事?
这是问题所在:
void push(struct Stack *stack, int element)
{
int *stack_data = stack->data;
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.");
resize(stack);
// stack_data still points to old memory allocation
}
assert(stack->pointer < stack->size);
stack_data[stack->pointer] = element;
}
由于您在 resize()
中修改了 stack->data
,您保存的指针 stack_data
可能指向 resize()
之后的无效内存位置。您从未将新的 stack->data
分配给 stack_data
而您直接使用后者,这会导致内存访问冲突。
您的修复版本一直在使用 stack->data
,因此上述问题不再存在。
指针 stack->data
可能会被
stack->data = realloc(stack->data, (sizeof *stack->data) * new_size);
在 resize
函数中。但是,它并没有反映到变量 stack_data
中,它仍然指向旧的和可能无效的地方。
要使该函数起作用,您必须在调用 resize
之后分配给 stack_data
。
void push(struct Stack *stack, int element)
{
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.");
resize(stack);
}
int *stack_data = stack->data; // move this after the call of resize()
assert(stack->pointer < stack->size);
stack_data[stack->pointer] = element;
}
当你调用函数 realloc
就像在这个语句中一样
stack->data = realloc(stack->data, (sizeof *stack->data) * new_size);
返回的指针不必与存储在用作函数参数的指针中的地址相同。
因此在函数push
的第一次实现中,指针stack_data
在其初始化后
int *stack_data = stack->data; // --> I think the bug is here
调用函数resize
后,不一定等于指针stack->data
的值
resize(stack);
因此这个语句
stack_data[stack->pointer] = element;
通常可以调用未定义的行为,因为可以使用指向已释放内存的指针 stack_data
。
在函数 push
的第二个实现中,使用了存储在数据成员 stack->data
(stack->data)[stack->pointer] = element;
所以第二个函数实现没有第一个函数实现中存在的错误。
注意push和resize函数不能发出任何消息。决定是否输出消息的是函数的调用者。所以该函数应该有一种方法来报告它们是否成功。
函数可以这样定义
static int resize(struct Stack *stack)
{
const int new_size = stack->size * 2;
int *tmp = realloc(stack->data, (sizeof *stack->data) * new_size);
int success = tmp != NULL;
if ( success )
{
stack->data = tmp;
stack->size = new_size;
}
return success;
}
int push(struct Stack *stack, int element)
{
stack->pointer = stack->pointer + 1;
int success = stack->pointer < stack->size;
if ( !success )
{
success = resize( stack );
}
if ( success ) stack->data[stack->pointer] = element;
return success;
}
问题出在您本地使用 stack_data
。请注意 realloc
完全可以 更改 整个内存区域。来自手册页:
If the area pointed to was moved, a free(ptr) is done.
当您调用 resize
(因此调用 realloc
)时,stack->data
的内存区域可能已更改位置。如果是这样,当您从 push
中的 resize
返回时,stack_data
不再指向有效的内存区域,实际上,访问 free
d 内存会调用 undefined行为。在那里使用本地真的没有意义,只需将 push
更改为
void push(struct Stack *stack, int element)
{
stack->pointer = stack->pointer + 1;
if (stack->pointer >= stack->size)
{
printf("Stack is full. Expanding the stack size.\n");
resize(stack);
// stack->data might point somewhere entirely different now.
}
assert(stack->pointer < stack->size);
// access stack->data directly, no need for a local
stack->data[stack->pointer] = element;
}
我创建了一个 mre that potentially illustrates this problem (took a few liberties with parts omitted in the OP). realloc
doesn't have to change the memory region, so you will only see your error if that happens: https://godbolt.org/z/4nEoM65Yc