无法在 c 中的二维数组中释放二维数组
Trouble freeing 2d array in 2d array in c
我的程序的简要概述:它接受了大约 500 组初始参数的列表。然后它将这 500 个拆分为 "chunks" of 50,以便它可以计算 50 并将结果写入文件,然后继续进行下一个 "chunk" of 50,等等。这是一个检查点系统。无论如何,对于每个 "chunk",都会创建一个名为 calc_result
的结构数组来保存 50 个结果。每个 calc_result
都包含一个名称、一个索引和一个用于实际结果的二维字符数组。然后工作完成并写入文件,当前 calc_result
数组被释放。这是基本程序:
typedef struct
{
char* name;
int index;
char** finals;
} calc_results;
int main(int argc, char** argv)
{
//read in sets of initial parameters
int numChunks = 10;
int calcsPerChunk = 50;
for(int i = 0; i < numChunks; i++)
{
calc_result** results = malloc(sizeof(calc_result*)*calcsPerChunk);
for(int j = 0; j < calcsPerChunk; j++)
{
results[j] = malloc(sizeof(calc_result));
results[j]->name = "blah";
results[j]->index = j;
results[j]->finals = malloc(sizeof(char*) * 12); //12 final results
for(int k = 0; k < 12; k++)
{
results[j]->finals[k] = malloc(70); //max string size is 70
}
}
//DO ACTUAL WORK
//WRITE RESULTS TO FILE
//FREE STUFF:
for(int a = 0; a < calcsPerChunk; a++)
{
for(int b = 0; b < 12; b++)
{
free(results[a]->finals[b]);
}
free(results[a]->name);
free(results[a]->finals);
free(results[a]);
}
free(results);
}
}
我在释放结果时遇到问题。该程序运行了10个"chunks"中的大约7个,并运行了释放calcsPerChunk
中的1个(即i = 8
、a = 1
和b = 0
),然后它抛出指向 free(results[a]->finals[b])
行的错误。错误是无用的:"Program.exe" 已触发断点。”我不确定我在这里做错了什么,有人可以帮忙吗?
注意:您的代码中没有数组,因此您的问题标题用词不当。您正在为 pointer-to-pointer 分配类型 ,您首先在其中分配指针,然后为每个 object 分配一个存储块,然后为每个分配起始地址指向指针的存储块。 (规则:指针不是数组,数组也不是指针——尽管在访问时根据 C11 Standard - 6.3.2.1(p3) 将数组转换为指向第一个元素的指针)
您唯一的问题是尝试 free (results[a]->name);
未分配,然后您未能 验证 return 每个分配。除此之外,你们非常非常亲密。您知道 free
需要什么以及执行顺序,free(results[a]->name);
看起来更像是一个 "...duh..."
错误,而不是任何理解上的错误。
您还可以通过使用 取消引用的指针 为每个分配调整 type 的大小,而不是试图调用 X-type
对于 sizeof(X-type)
,在某些情况下可以是 error-prone。例如,而不是:
calc_result** results = malloc(sizeof(calc_result*)*calcsPerChunk);
您可以使用 sizeof *results
(解除引用的指针)来设置每个 object 的大小,例如
calc_results **results = malloc (sizeof *results * calcsPerChunk);
注意: '*'
或 '**'
通常与指针而不是类型一起使用。为什么?,语义和使指针明确。例如:
calc_results* a, b, c;
当然不会声明3-pointers to calc_result
。相反,它声明 指向 calc_result a
的指针和两个 struct calc_result b, c
。确保 '*'
在指针上可以清楚地表明这一点,例如
calc_results *a, b, c;
(合法的syntax-wise,没有区别,编译器可以毫无问题地解析它——这是human-side容易出现问题的地方)
确认您的方法的快速示例
对于测试,您不需要:
int numChunks = 10;
int calcsPerChunk = 50;
(3
和 5
(或任何超过 1
的东西)都可以)
将一个简短的示例放在一起,通过使用取消引用的指针来验证每个分配以及每个分配的大小,(并为每个级别吐出输出只是为了好玩)你可以这样做:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char *name;
int index;
char **finals;
} calc_results;
int main(void)
{
int numChunks = 3;
int calcsPerChunk = 5;
for(int i = 0; i < numChunks; i++) {
calc_results **results = malloc (sizeof *results * calcsPerChunk);
if (!results) {
perror ("malloc-results");
return 1;
}
for(int j = 0; j < calcsPerChunk; j++)
{
if (!(results[j] = malloc (sizeof *results[j]))) {
perror ("malloc-results[j]");
return 1;
}
results[j]->name = "blah";
results[j]->index = j;
if (!(results[j]->finals=malloc(sizeof *results[j]->finals*12))) {
perror ("malloc-results[j]->finals");
return 1;
}
for(int k = 0; k < 12; k++) {
if (!(results[j]->finals[k] = malloc(70))) {
perror ("malloc-results[j]->finals[k]");
return 1;
}
sprintf (results[j]->finals[k], "grade %d", k+1);
}
}
/* DO ACTUAL WORK */
/* output & free stuff */
printf ("results[%2d]\n", i);
for (int a = 0; a < calcsPerChunk; a++) {
printf (" %s %2d\n", results[a]->name, results[a]->index);
for (int b = 0; b < 12; b++) {
printf (" %s\n", results[a]->finals[b]);
free (results[a]->finals[b]);
}
// free(results[a]->name);
free(results[a]->finals);
free(results[a]);
}
free(results);
}
}
内存Use/Error检查
现在进入关键部分。在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 责任:(1) 始终保留指向起始地址的指针[=86] =] 对于内存块,(2) 当它不再需要时可以 释放 。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/free_nested_struct
==13663== Memcheck, a memory error detector
==13663== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13663== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==13663== Command: ./bin/free_nested_struct
==13663==
results[ 0]
blah 0
grade 1
grade 2
grade 3
grade 4
grade 5
grade 6
...
<snip>
==13663==
==13663== HEAP SUMMARY:
==13663== in use at exit: 0 bytes in 0 blocks
==13663== total heap usage: 213 allocs, 213 frees, 14,520 bytes allocated
==13663==
==13663== All heap blocks were freed -- no leaks are possible
==13663==
==13663== For counts of detected and suppressed errors, rerun with: -v
==13663== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。你真的非常接近,我怀疑你实际上是在你的实际代码中为 name
分配,所以很可能你的代码应该有效。
我的程序的简要概述:它接受了大约 500 组初始参数的列表。然后它将这 500 个拆分为 "chunks" of 50,以便它可以计算 50 并将结果写入文件,然后继续进行下一个 "chunk" of 50,等等。这是一个检查点系统。无论如何,对于每个 "chunk",都会创建一个名为 calc_result
的结构数组来保存 50 个结果。每个 calc_result
都包含一个名称、一个索引和一个用于实际结果的二维字符数组。然后工作完成并写入文件,当前 calc_result
数组被释放。这是基本程序:
typedef struct
{
char* name;
int index;
char** finals;
} calc_results;
int main(int argc, char** argv)
{
//read in sets of initial parameters
int numChunks = 10;
int calcsPerChunk = 50;
for(int i = 0; i < numChunks; i++)
{
calc_result** results = malloc(sizeof(calc_result*)*calcsPerChunk);
for(int j = 0; j < calcsPerChunk; j++)
{
results[j] = malloc(sizeof(calc_result));
results[j]->name = "blah";
results[j]->index = j;
results[j]->finals = malloc(sizeof(char*) * 12); //12 final results
for(int k = 0; k < 12; k++)
{
results[j]->finals[k] = malloc(70); //max string size is 70
}
}
//DO ACTUAL WORK
//WRITE RESULTS TO FILE
//FREE STUFF:
for(int a = 0; a < calcsPerChunk; a++)
{
for(int b = 0; b < 12; b++)
{
free(results[a]->finals[b]);
}
free(results[a]->name);
free(results[a]->finals);
free(results[a]);
}
free(results);
}
}
我在释放结果时遇到问题。该程序运行了10个"chunks"中的大约7个,并运行了释放calcsPerChunk
中的1个(即i = 8
、a = 1
和b = 0
),然后它抛出指向 free(results[a]->finals[b])
行的错误。错误是无用的:"Program.exe" 已触发断点。”我不确定我在这里做错了什么,有人可以帮忙吗?
注意:您的代码中没有数组,因此您的问题标题用词不当。您正在为 pointer-to-pointer 分配类型 ,您首先在其中分配指针,然后为每个 object 分配一个存储块,然后为每个分配起始地址指向指针的存储块。 (规则:指针不是数组,数组也不是指针——尽管在访问时根据 C11 Standard - 6.3.2.1(p3) 将数组转换为指向第一个元素的指针)
您唯一的问题是尝试 free (results[a]->name);
未分配,然后您未能 验证 return 每个分配。除此之外,你们非常非常亲密。您知道 free
需要什么以及执行顺序,free(results[a]->name);
看起来更像是一个 "...duh..."
错误,而不是任何理解上的错误。
您还可以通过使用 取消引用的指针 为每个分配调整 type 的大小,而不是试图调用 X-type
对于 sizeof(X-type)
,在某些情况下可以是 error-prone。例如,而不是:
calc_result** results = malloc(sizeof(calc_result*)*calcsPerChunk);
您可以使用 sizeof *results
(解除引用的指针)来设置每个 object 的大小,例如
calc_results **results = malloc (sizeof *results * calcsPerChunk);
注意: '*'
或 '**'
通常与指针而不是类型一起使用。为什么?,语义和使指针明确。例如:
calc_results* a, b, c;
当然不会声明3-pointers to calc_result
。相反,它声明 指向 calc_result a
的指针和两个 struct calc_result b, c
。确保 '*'
在指针上可以清楚地表明这一点,例如
calc_results *a, b, c;
(合法的syntax-wise,没有区别,编译器可以毫无问题地解析它——这是human-side容易出现问题的地方)
确认您的方法的快速示例
对于测试,您不需要:
int numChunks = 10;
int calcsPerChunk = 50;
(3
和 5
(或任何超过 1
的东西)都可以)
将一个简短的示例放在一起,通过使用取消引用的指针来验证每个分配以及每个分配的大小,(并为每个级别吐出输出只是为了好玩)你可以这样做:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char *name;
int index;
char **finals;
} calc_results;
int main(void)
{
int numChunks = 3;
int calcsPerChunk = 5;
for(int i = 0; i < numChunks; i++) {
calc_results **results = malloc (sizeof *results * calcsPerChunk);
if (!results) {
perror ("malloc-results");
return 1;
}
for(int j = 0; j < calcsPerChunk; j++)
{
if (!(results[j] = malloc (sizeof *results[j]))) {
perror ("malloc-results[j]");
return 1;
}
results[j]->name = "blah";
results[j]->index = j;
if (!(results[j]->finals=malloc(sizeof *results[j]->finals*12))) {
perror ("malloc-results[j]->finals");
return 1;
}
for(int k = 0; k < 12; k++) {
if (!(results[j]->finals[k] = malloc(70))) {
perror ("malloc-results[j]->finals[k]");
return 1;
}
sprintf (results[j]->finals[k], "grade %d", k+1);
}
}
/* DO ACTUAL WORK */
/* output & free stuff */
printf ("results[%2d]\n", i);
for (int a = 0; a < calcsPerChunk; a++) {
printf (" %s %2d\n", results[a]->name, results[a]->index);
for (int b = 0; b < 12; b++) {
printf (" %s\n", results[a]->finals[b]);
free (results[a]->finals[b]);
}
// free(results[a]->name);
free(results[a]->finals);
free(results[a]);
}
free(results);
}
}
内存Use/Error检查
现在进入关键部分。在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 责任:(1) 始终保留指向起始地址的指针[=86] =] 对于内存块,(2) 当它不再需要时可以 释放 。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/free_nested_struct
==13663== Memcheck, a memory error detector
==13663== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13663== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==13663== Command: ./bin/free_nested_struct
==13663==
results[ 0]
blah 0
grade 1
grade 2
grade 3
grade 4
grade 5
grade 6
...
<snip>
==13663==
==13663== HEAP SUMMARY:
==13663== in use at exit: 0 bytes in 0 blocks
==13663== total heap usage: 213 allocs, 213 frees, 14,520 bytes allocated
==13663==
==13663== All heap blocks were freed -- no leaks are possible
==13663==
==13663== For counts of detected and suppressed errors, rerun with: -v
==13663== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您还有其他问题,请告诉我。你真的非常接近,我怀疑你实际上是在你的实际代码中为 name
分配,所以很可能你的代码应该有效。