C 编程 - 释放一个用单引号填充的字符 ** 和用双引号填充的字符之间的区别 - valgrind

C programming - difference between freeing a char ** filled with simple quotes and filled with double quotes - valgrind

我正在尝试使用此程序测试免费功能

  char **tab;

  // allocation
  tab = malloc(5 * sizeof(char*));
  for(int j=0 ; j<5 ; j++)
    {
      tab[j] = malloc(4 * sizeof(char));
    }

  // FILL TAB FIRST WAY
  /*
  for(int j=0 ; j<5 ; j++)
    {
      for(int i=0 ; i<4 ; i++)
        {
          tab[j][i] = '@';
        }
    }
  */

  // FILL TAB SECOND WAY
  for(int j=0 ; j<5 ; j++)
    {
      tab[j] = "@@@@";
    }

  //free
  for(int j=0 ; j<5 ; j++)
    {
      free(tab[j]);
    }

  free(tab);

填充制表符的第一种方法(每个字符单独)returns valgrind 没有内存错误,而第二种方法(逐行填充制表符)returns 一些内存错误。

HEAP SUMMARY:
==447==     in use at exit: 20 bytes in 5 blocks
==447==   total heap usage: 6 allocs, 6 frees, 60 bytes allocated
==447==
==447== Searching for pointers to 5 not-freed blocks
==447== Checked 64,648 bytes
==447==
==447== LEAK SUMMARY:
==447==    definitely lost: 20 bytes in 5 blocks
==447==    indirectly lost: 0 bytes in 0 blocks
==447==      possibly lost: 0 bytes in 0 blocks
==447==    still reachable: 0 bytes in 0 blocks
==447==         suppressed: 0 bytes in 0 blocks
==447== Rerun with --leak-check=full to see details of leaked memory
==447==
==447== ERROR SUMMARY: 5 errors from 1 contexts (suppressed: 0 from 0)
==447==
==447== 5 errors in context 1 of 1:
==447== Invalid free() / delete / delete[] / realloc()
==447==    at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-
amd64-linux.so)
==447==    by 0x400607: main (test_malloc.c:35)

对此有何解释?

你做不到tab[j] = "@@@@"。这根本是无效的。或者至少,它并没有按照您的想法行事。

正确的方法是strncpy(tab[j], "@@@@", 4)

当你这样做时

tab[j] = "@@@@";

你覆盖了你之前做的分配

tab[j] = malloc(4 * sizeof(char));

字符串 "@@@@" 不是由您的代码分配的,无论如何也不是由 malloc() 分配的,这是静态区域中的一个地址,无法使用 free().[=18 释放=]

释放这样的地址是未定义的行为。

这与您的代码无关(因为分配的地址被覆盖),但是 malloc() 分配应该是 5 个字节以应对 "@@@@" 的最终 '[=16=]' .

这是将 4 个字节的内存分配给 tab[j],这 space 足以容纳 3 个字符加上 NUL 终止符

tab[j] = malloc(4 * sizeof(char));

此行将 tab[j] 的值替换为尚未分配给 malloc 的字符串。您的 free 几行之后不会释放您分配的内存,并且谁知道会做什么,因为它正在传递未分配的内容。

tab[j] = "@@@@";

第二行应该使用 strcpy,除了答案顶部提到的,您只为 3 个字符分配 space,而在这里,您试图存储 4 个。

对于第二种情况,您已使用以下方法分配内存:

tab[j] = malloc(4 * sizeof(char));

然后用"@@@@"(字符串文字)的地址覆盖指针:

tab[j] = "@@@@";

所以你刚刚:

  1. 忘记了 malloced 的记忆(你的 LEAK SUMMARY 状态)。
  2. 尝试 free() 字符串文字(您的 ERROR SUMMARY 声明的内容)。

如果你想将"@@@@"正确复制到内存中,你可以使用strncpy():

strncpy(tab[j], "@@@@", 4);

以下声明有差异

char s[4] = "@@@@";

and(也可以使用赋值表达式代替声明)

char *s = "@@@@";

在第一种情况下,字符串文字 "@@@@" 的元素用于初始化数组的元素。所以第一个声明等同于

char s[4] = { '@', '@', '@', '@' };

在第二种情况下,创建了一个具有静态存储持续时间的字符串文字,其类型为 char[5](由于附加的终止零)并且字符串文字的第一个字符的地址被分配给指针 s.

你可以想象这个声明(或assignmnet表达式) 以下方式

static char unnamed_string_literal[5] = { '@', '@', '@', '@', '[=13=]' };
char *s = &unnamed_string_literal[0];

char *s;
static char unnamed_string_literal[5] = { '@', '@', '@', '@', '[=14=]' };
s = &unnamed_string_literal[0];

来自 C 标准(6.4.5 字符串文字)

6 在翻译阶段 7 中,一个字节或值为零的代码被附加到每个由一个或多个字符串文字产生的多字节字符序列。78)然后使用多字节字符序列初始化 静态存储持续时间 和长度足以包含序列的数组。对于字符串文字,数组元素的类型为 char,并使用多字节字符序列的各个字节进行初始化....

所以在这段代码中

  for(int i=0 ; i<4 ; i++)
    {
      tab[j][i] = '@';`
    }

你用 '@'.

字符填充了分配的内存 "manually"

但是在此代码片段中,表达式 tab[j] 的类型为 char *

for(int j=0 ; j<5 ; j++)
{
  tab[j] = "@@@@";
}

然后你用一个或多个字符串文字的地址覆盖了指针的先前值,这些指针是已分配内存的地址 "@@@@".

这会导致内存泄漏,因为已分配内存的地址丢失,您可能无法将函数 free 应用于现在指向内存的指针,该内存具有字符串文字占用的静态存储持续时间或文字(取决于编译器如何存储彼此相等的字符串文字)。