我已经编写了一个方法来释放我的结构。现在我有一个问题。当我调用这个方法两次时,它给我一个错误。但我确实检查了我的结构中是否有某些内容,所以我不知道它怎么可能给我错误。
我的结构:
typedef struct {
int num_rows;
int num_cols;
int** data;
} matrix;
我的免费方法:
void free_matrix(matrix* m){
int i;
for(i=0;i<m->num_rows;i++){
if(m->data[i]!=NULL){
free(m->data[i]);
}
}
if(m->data!=NULL){
free(m->data);
}
}
额外方法:
void fill_matrix_a(matrix* m){
m->data[0][0] = 1;
m->data[0][1] = 0;
m->data[0][2] = 2;
m->data[1][0] = 0;
m->data[1][1] = 3;
m->data[1][2] = 1;
}
void fill_matrix_b(matrix* m){
m->data[0][0] = 0;
m->data[0][1] = 3;
m->data[1][0] = 2;
m->data[1][1] = 1;
m->data[2][0] = 0;
m->data[2][1] = 4;
}
void init_matrix(matrix* m, int num_rows, int num_cols){
int i;
m->num_cols = num_cols;
m->num_rows = num_rows;
m->data = (int**) calloc(num_rows,sizeof(int*));
if(m->data==NULL){
printf("%s\n", "ERROR: probleem bij geheugenallocatie.");
exit(1);
}
for(i=0;i<num_rows;i++){
m->data[i] = (int*) calloc(num_cols,sizeof(int));
if(m->data[i]==NULL){ /* THE PROGRAM SAYS THIS IS TRUE BUT IT ISN'T BECAUSE I ALREADY FREED THE DATA OF B!! */
printf("%s\n", "ERROR: probleem bij geheugenallocatie.");
exit(1);
}
}
}
给我一个错误的方法:
void ex_1_matrix_operations(){
matrix a,b,c;
/* init a(2,3) and fill with elements */
init_matrix(&a,2,3);
fill_matrix_a(&a);
print_matrix(&a);
/* init b (default) and fill with elements */
init_matrix_default(&b);
fill_matrix_b(&b);
print_matrix(&b);
free_matrix(&a);
free_matrix(&b);
/* create unity matrix */
init_identity_matrix(&a,2);
print_matrix(&a);
free_matrix(&a);
free_matrix(&b); /* THIS IS WHERE MY ERROR OCCURS*/
}
最佳答案
您为 a
调用了两次 free_matrix
,为 b
调用了两次。对于a
,您首先分配内存,然后释放它,按照这个顺序,您执行这些操作两次;然而,对于b
,您先分配然后释放,然后在不再次分配的情况下尝试释放,这会导致崩溃。
调用free
来释放分配的内存,不会将指向它的指针设置为NULL
,这必须手动完成。如果不这样做,指针就会变成 dangling pointer即指向进程无法读取(不可访问)的某个位置的指针。为了避免这种情况,使用辅助函数来释放内存并将指针设置为 NULL
的情况并不罕见。
void free_data(void **pp)
{
if (pp && *pp)
{
free(*pp);
*pp = NULL;
}
}
虽然检查指针是否非空是完全有效的,但从语言的角度来看,取消引用它是未定义的行为(UB)。调用free
会要求C运行时库尝试执行此操作,从而根据语言规范进入UB区域;对于 C 运行时,它将尝试访问不再属于进程的内存,从而崩溃,因为操作系统会引发某种形式的访问被拒绝错误,例如Linux 会将此称为(以及其他)Segmentation Fault ,而 Windows 会显示访问冲突。
顺便说一句,有些人认为将释放的指针设置为 NULL
并不是一个好的做法,因为它掩盖了双删除错误。假设您将释放的指针设置为NULL
,并且在其他地方使用相同的指针调用free
,即您实际上会调用free(NULL);
。这是传递给 free 的有效参数,它不会执行任何操作,因此永远不会让世界知道刚刚发生了双重删除。如果未将其设置为 NULL,则在尝试释放已释放的位置时,第二次调用 free 会引发访问冲突错误,从而导致双重删除错误为人所知。
关于C 释放检查不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26466940/