【问题标题】:realloc says Invalid next size [duplicate]realloc 说下一个大小无效 [重复]
【发布时间】:2013-07-02 12:15:49
【问题描述】:

这是我的代码,它应该为存储字符串动态分配内存:

#define _XOPEN_SOURCE 700

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    char **path = NULL;

    path = (char **) malloc(sizeof(char *));

    for (int i = 0; i < 5; i++)
    {
            path[i] = strdup("TEST");
            path = (char **) realloc(path, sizeof(char *));
    }

    for (int i = 0; i < 5; i++)
    {
            printf("%s\n", path[i]);
    }

    return 0;
}

上面的代码在我重新分配内存的那一行中断。

据我所知,在第一个 malloc 上分配给路径的内存如下所示:

path -----> |   char *  | // path points to a character pointer which       inturn is pointing to nothing(Dangling).

所以,在程序运行该行的这个时间点:

path = (char **) malloc(sizeof(char *));

path[0] 当前在边界内,我们可以在 path[0] 处存储一个且只有一个字符串的起始地址,此时 path[1] 应该超出边界。

所以,当我们第一次进入 for 循环时,我们将字符串的地址存储在 path[i] 中,我们将能够:

path[0] = strdup("TEST"); // is in bounds.

要存储另一个字符串,我们需要更多的路径应该指向的内存。 所以我在下面的行中做了 realloc:

path = (char **) realloc(path, (char *));

所以,根据我的说法,内存中的路径应该如下所示:

path --------->|old char *|----->"TEST"
  |----------->|new char *|----->(Dangling) // Pointing Nowhere.

所以,现在 path[1] 也在边界内,我们应该能够使用该内存位置。所以我无法弄清楚为什么我在运行我的代码时会出现这个分段错误:

*** glibc detected *** ./dynamic: realloc(): invalid next size: 0x0000000000602010 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x75018)[0x7f743153e018]
/lib64/libc.so.6(+0x7ae96)[0x7f7431543e96]
/lib64/libc.so.6(realloc+0xfa)[0x7f74315441aa]
./dynamic[0x40067f]
/lib64/libc.so.6(__libc_start_main+0xe6)[0x7f74314e7bc6]
./dynamic[0x400569]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:27 14459340                           /home/xpansat/c/basic/dynamic
00600000-00601000 r--p 00000000 00:27 14459340                           /home/xpansat/c/basic/dynamic
00601000-00602000 rw-p 00001000 00:27 14459340                           /home/xpansat/c/basic/dynamic
00602000-00623000 rw-p 00000000 00:00 0                                  [heap]
7f742c000000-7f742c021000 rw-p 00000000 00:00 0 
7f742c021000-7f7430000000 ---p 00000000 00:00 0 
7f74312b2000-7f74312c8000 r-xp 00000000 fd:01 173                        /lib64/libgcc_s.so.1
7f74312c8000-7f74314c7000 ---p 00016000 fd:01 173                        /lib64/libgcc_s.so.1
7f74314c7000-7f74314c8000 r--p 00015000 fd:01 173                        /lib64/libgcc_s.so.1
7f74314c8000-7f74314c9000 rw-p 00016000 fd:01 173                        /lib64/libgcc_s.so.1
7f74314c9000-7f743161d000 r-xp 00000000 fd:01 27                         /lib64/libc-2.11.1.so
7f743161d000-7f743181d000 ---p 00154000 fd:01 27                         /lib64/libc-2.11.1.so
7f743181d000-7f7431821000 r--p 00154000 fd:01 27                         /lib64/libc-2.11.1.so
7f7431821000-7f7431822000 rw-p 00158000 fd:01 27                         /lib64/libc-2.11.1.so
7f7431822000-7f7431827000 rw-p 00000000 00:00 0 
7f7431827000-7f7431846000 r-xp 00000000 fd:01 20                         /lib64/ld-2.11.1.so
7f7431a14000-7f7431a17000 rw-p 00000000 00:00 0 
7f7431a44000-7f7431a45000 rw-p 00000000 00:00 0 
7f7431a45000-7f7431a46000 r--p 0001e000 fd:01 20                         /lib64/ld-2.11.1.so
7f7431a46000-7f7431a47000 rw-p 0001f000 fd:01 20                         /lib64/ld-2.11.1.so
7f7431a47000-7f7431a48000 rw-p 00000000 00:00 0 
7fff62f8c000-7fff62fa2000 rw-p 00000000 00:00 0                          [stack]
7fff62fff000-7fff63000000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

我不知道我做错了什么,因为如果我如下更改我的 realloc 行,我就能够运行我的代码:

path = (char **) realloc(sizeof(char *)*8);

如果是乘以 8 我的代码运行但我想分配我想要的确切内存,任何想法为什么没有乘以 8 就无法工作。

【问题讨论】:

  • 您在某处出现缓冲区溢出。您似乎在 Linux 上,所以只需在 Valgrind 下运行即可找到它。
  • path = (char **) malloc(sizeof(char *));,然后是path = (char **) realloc(path, sizeof(char *)); for (int i = 0; i < 5; i++) 中访问path[1] 是未定义的行为。你践踏了你无权访问的所有内存。
  • 请注意,realloc 完全没有意义,因为您请求的大小与最初分配数组的大小完全相同。请记住,sizeof(type) 是编译时常量!
  • 不需要在 C 中强制转换 malloc()/calloc()/realloc(),更不推荐stackoverflow.com/q/605845/694576

标签: c memory realloc


【解决方案1】:

这里

path = (char **) malloc(sizeof(char *));

你正在分配一个指向char的指针,这是一个指向C-“字符串”的指针的空间。

那么这里:

for (int i = 0; i < 5; i++)
{
   path[i] = strdup("TEST");
   path = (char **) realloc(path, sizeof(char *));
}

您不仅使用一个指向 C-"字符串" 的指针,而且重新分配更多的内容只是重新分配与以前相同的大小,但实际上您访问时就像在每次迭代中为一个指针添加了内存一样。

这样做会覆盖未分配的内存(或已经被其他东西使用)并搞乱程序的内存管理。

一个更直接的方法是:

  ...

  char ** path = NULL;

  for (size_t i = 0; i < 5; ++i)
  {
    path = realloc(path, (i+1) * sizeof(*path));
    path[i] = strdup("TEST");
  }

  ...

【讨论】:

  • 你好,实际上我不明白 OP 想用这段代码做什么,但是当我包含 strdup 的声明时,代码有效,即测试打印了 5 次。请问给我解释一下。谢谢!
  • @PHIfounder C 允许你做一些导致未定义行为的事情,比如这里的原始代码。在这种情况下发生的事情可能是非常随机的,可能是灾难性的失败,或者至少在今天看起来可以正常工作。
  • @PHIfounder:您可能希望使用 Valgrind (valgrind.org) 运行从 OP 的原始源代码编译的程序,即使程序可能不会崩溃,您也会看到幕后出了什么问题.
  • Valgrind 可以在 Windows 上运行吗?我使用 gdb。
  • @PHIfounder:你用的是MinGW还是Cygwin?对于 MinGW,这可能会有所帮助:sourceforge.net/p/valgrind4win/wiki/DevelopmentEnvironment
【解决方案2】:

跟随你的风格,改变

path = (char **) realloc(path, sizeof(char *));

path = realloc(path, (i+2) * sizeof(char *));

这分配给 next 循环。如果您想在每个循环中重新分配,则分配 beforehand 更有意义,如下所示:

path = realloc(path, (i+1) * sizeof(char *));
path[i] = strdup("TEST");

正如其他人所说,使用path = malloc(5 * sizeof(char *)) 预先分配所有内存。删除 (char **) 的演员表是首选的编码实践。

使用

预先分配所有内存
path = calloc(5, sizeof(char *));

malloc(size) 相比,calloc(num,size) 有 2 个好处。内存是零填充的。如果 num * size 的乘法溢出(在这种情况下显然不会),calloc() 仍然会正确处理它。


另一种编码实践思想:使用ptr = malloc(sizeof(*ptr))ptr = malloc(sizeof(char *)) 有一个很好的优势。如果*ptr 的类型发生变化(尽管这里不太可能),则在各种malloc() 调用中不需要更改编码。 (也适用于 realloc() 和 calloc()。)

【讨论】:

  • 这可能是代码的意思,但它应该得到一些解释。
  • 需要i+2,访问path[i]后,path必须已经至少指向i+1项目,否则你有UB。
  • @Daniel Fischer 不错的 +2。
  • @DanielFischer 在运行代码后同意你的看法,但不明白为什么 i+2 是必要的,无法得到什么是 UB。
  • @msatyam 8 个字节。前 4 个在 realloc () 调用中发布。
猜你喜欢
  • 2011-02-25
  • 2012-10-31
  • 2013-09-28
  • 1970-01-01
  • 2018-06-13
  • 2020-12-27
  • 1970-01-01
相关资源
最近更新 更多