释放在 C 中实现的堆栈
Free the stack implemented in C
我在 C 中实现了一个堆栈及其函数。现在调用完所有函数后,我想释放堆栈。
我的问题是我应该先释放堆栈 "st" 的基指针还是直接释放堆栈 "st"?两者似乎都适用于我的代码。
#include <stdio.h>
#include <stdlib.h>
#define init_size 10
#define increment 1
typedef struct sqStack
{
int* top;
int* base;
int stack_size;
}sqStack;
int init_stack(sqStack* sq)
{
if(sq->base==NULL)
{
sq->base = (int*)malloc(init_size*sizeof(int));
}
if(sq->base==NULL) exit(-1);
sq->stack_size=init_size;
sq->top=sq->base;
return 1;
}
int push(sqStack* sq, int e)
{
if(sq==NULL) exit(-1);
if(sq->top-sq->base==sq->stack_size-1)//pointer top reaches the top of the stack
{
int* q = (int*)realloc(sq->base,(sq->stack_size+increment)*sizeof(int));
if(q==NULL) exit(-1);
sq->base=q;
sq->top=sq->base+sq->stack_size-1;
sq->stack_size += increment;
}
*sq->top++=e;
return 1;
}
int pop(sqStack* sq,int* e)
{
if(sq==NULL) exit(-1);
if(sq->base==sq->top) exit(-1);
sq->top--;
*e=*sq->top;
sq->stack_size--;
return *e;
}
int top(sqStack* sq,int* e)
{
if(sq==NULL) exit(-1);
if(sq->base==sq->top) exit(-1);
*e=*(sq->top-1);
return *e;
}
int empty(sqStack* sq)
{
if(sq->base==sq->top) return 1;
else return 0;
}
int main() {
sqStack* st= (sqStack*)calloc(1,sizeof(sqStack)) ;
int e;
init_stack(st);
for(int i=0;i<12;i++)
{
push(st,i+1);
}
for(int i=0;i<12;i++)
{
printf("%d\n",top(st,&e));
printf("%d\n",pop(st,&e));
}
free(st->base);
free(st);
return 0;
}
结果是:
12
12
11
11
10
10
9
9
8个
8个
7
7
6个
6个
5个
5个
4个
4个
3个
3个
2个
2个
1个
1个
每个数字都在一行上。
就 malloc
和 free
而言,你所拥有的是正确的。
您应该只将 malloc
或 realloc
返回的内容传递给 free
。您为结构和堆栈分配 space 并释放两者,因此不会泄漏任何内存。
您不想先 free(st)
因为这样做之后 st
指向的内存不再有效,您随后无法 free(st->base)
安全。此外,因为 free
对给定内存的内容一无所知,所以它不会尝试释放内存可能包含的任何指针。所以只要调用 free(st)
就会泄漏 st->base
.
中的内存
每个分配都应与相应的 free
配对,因此在您的情况下,这意味着同时释放 st->base
和 st
(按此顺序)。但是您的程序仅在终止之前立即执行其释放,并且当程序终止时,它没有释放的任何内容都将被 OS 回收。诸如 Valgrind 之类的内存使用分析器可以为您检测出差异,但实际上这无关紧要。
您的代码是正确的。相反的顺序可能有效,但如果确实如此,那将是偶然的。
原因是free(st)
释放了存储对象*st
的内存。指针 st->base
存储在这个内存的一部分中。假设某个其他任务或线程在释放内存后立即获取了相同的内存。然后怎样呢?也就是说,当 free(st->base)
最终被调用时会发生什么?
答案:未定义会发生什么。
即使仍然可以从 st->base
中检索到地址(也可能不可能),该地址可能已被任意数据覆盖,在这种情况下 free(st->base)
——解释作为地址的任意数据——会要求操作系统解除分配……好吧,你不知道什么它会要求操作系统解除分配。在这种情况下,很难期望有好的结果。
你做得很好。你的排序是正确的。
其他注意事项
为了安全起见,现代操作系统内核有时会自动用空数据或随机数据覆盖已释放的内存。此外,它们有时会撤销程序对硬件 page 的访问权限(通过该硬件 page 已(虚拟)寻址释放的内存,或者收紧允许访问的范围。其中一些比其他的更有可能,但我已经看到至少三个中的两个发生了。关键是,根据内核最新的内存管理和安全算法,内核可以随心所欲地立即、早、晚或根本不做这些事情,因为你的程序不应该关心释放时会发生什么程序释放后的内存。
我在 C 中实现了一个堆栈及其函数。现在调用完所有函数后,我想释放堆栈。
我的问题是我应该先释放堆栈 "st" 的基指针还是直接释放堆栈 "st"?两者似乎都适用于我的代码。
#include <stdio.h>
#include <stdlib.h>
#define init_size 10
#define increment 1
typedef struct sqStack
{
int* top;
int* base;
int stack_size;
}sqStack;
int init_stack(sqStack* sq)
{
if(sq->base==NULL)
{
sq->base = (int*)malloc(init_size*sizeof(int));
}
if(sq->base==NULL) exit(-1);
sq->stack_size=init_size;
sq->top=sq->base;
return 1;
}
int push(sqStack* sq, int e)
{
if(sq==NULL) exit(-1);
if(sq->top-sq->base==sq->stack_size-1)//pointer top reaches the top of the stack
{
int* q = (int*)realloc(sq->base,(sq->stack_size+increment)*sizeof(int));
if(q==NULL) exit(-1);
sq->base=q;
sq->top=sq->base+sq->stack_size-1;
sq->stack_size += increment;
}
*sq->top++=e;
return 1;
}
int pop(sqStack* sq,int* e)
{
if(sq==NULL) exit(-1);
if(sq->base==sq->top) exit(-1);
sq->top--;
*e=*sq->top;
sq->stack_size--;
return *e;
}
int top(sqStack* sq,int* e)
{
if(sq==NULL) exit(-1);
if(sq->base==sq->top) exit(-1);
*e=*(sq->top-1);
return *e;
}
int empty(sqStack* sq)
{
if(sq->base==sq->top) return 1;
else return 0;
}
int main() {
sqStack* st= (sqStack*)calloc(1,sizeof(sqStack)) ;
int e;
init_stack(st);
for(int i=0;i<12;i++)
{
push(st,i+1);
}
for(int i=0;i<12;i++)
{
printf("%d\n",top(st,&e));
printf("%d\n",pop(st,&e));
}
free(st->base);
free(st);
return 0;
}
结果是: 12 12 11 11 10 10 9 9 8个 8个 7 7 6个 6个 5个 5个 4个 4个 3个 3个 2个 2个 1个 1个 每个数字都在一行上。
就 malloc
和 free
而言,你所拥有的是正确的。
您应该只将 malloc
或 realloc
返回的内容传递给 free
。您为结构和堆栈分配 space 并释放两者,因此不会泄漏任何内存。
您不想先 free(st)
因为这样做之后 st
指向的内存不再有效,您随后无法 free(st->base)
安全。此外,因为 free
对给定内存的内容一无所知,所以它不会尝试释放内存可能包含的任何指针。所以只要调用 free(st)
就会泄漏 st->base
.
每个分配都应与相应的 free
配对,因此在您的情况下,这意味着同时释放 st->base
和 st
(按此顺序)。但是您的程序仅在终止之前立即执行其释放,并且当程序终止时,它没有释放的任何内容都将被 OS 回收。诸如 Valgrind 之类的内存使用分析器可以为您检测出差异,但实际上这无关紧要。
您的代码是正确的。相反的顺序可能有效,但如果确实如此,那将是偶然的。
原因是free(st)
释放了存储对象*st
的内存。指针 st->base
存储在这个内存的一部分中。假设某个其他任务或线程在释放内存后立即获取了相同的内存。然后怎样呢?也就是说,当 free(st->base)
最终被调用时会发生什么?
答案:未定义会发生什么。
即使仍然可以从 st->base
中检索到地址(也可能不可能),该地址可能已被任意数据覆盖,在这种情况下 free(st->base)
——解释作为地址的任意数据——会要求操作系统解除分配……好吧,你不知道什么它会要求操作系统解除分配。在这种情况下,很难期望有好的结果。
你做得很好。你的排序是正确的。
其他注意事项
为了安全起见,现代操作系统内核有时会自动用空数据或随机数据覆盖已释放的内存。此外,它们有时会撤销程序对硬件 page 的访问权限(通过该硬件 page 已(虚拟)寻址释放的内存,或者收紧允许访问的范围。其中一些比其他的更有可能,但我已经看到至少三个中的两个发生了。关键是,根据内核最新的内存管理和安全算法,内核可以随心所欲地立即、早、晚或根本不做这些事情,因为你的程序不应该关心释放时会发生什么程序释放后的内存。