【问题标题】:realloc() not behaving as expected when shrinking char *realloc() 在缩小 char * 时没有按预期运行
【发布时间】:2015-08-30 01:10:34
【问题描述】:

我正在尝试使用 realloc() 缩小指针“ptr”,如下示例代码所示:

char * ptr = malloc(sizeof(char) * 7);
int i;
for(i = 0;i<7;i++){
    ptr[i]= "pointer"[i];
}
puts(ptr);
ptr = realloc(ptr,sizeof(char) * 3);
puts(ptr);
free(ptr);

期望代码释放未使用的内存,我认为它会返回以下内容:

pointer
poi

但是,它反而返回:

pointer
pointer

这是编译器的错误,还是我对 realloc() 理解的错误?

【问题讨论】:

  • 鉴于你的 char * 不是空终止的,我预计恶魔会从你的鼻子飞出。
  • 注意:你不能缩小一个指针,但它指向的对象。而且您正在调用未定义的行为,因为您没有在收缩后终止字符串。第一个循环恰好终止了您复制的字符串,但您分配的 char 少于要求:也是未定义的行为。
  • @WilliamPursell:char * 永远不会以 NUL 结尾,因为它是一个指针。我想你的意思是它指向的 C 字符串。
  • @Olaf,是的,但是这个 char * 不指向 C 字符串。
  • @Olaf 在我看来,他认为puts 读取到分配的内存块的末尾,而实际上它读取的是一个 C 字符串。

标签: c memory memory-management


【解决方案1】:

你误解了 realloc。 realloc 不一定将数据移动到新位置,并且由于您正在缩小内存,因此几乎可以肯定(取决于实现)不会。它不会(必然)修改原始内容,在您的情况下也没有。您的程序正在调用未定义的行为,除了程序中没有错误。 (使用不指向空终止字符串的 char * 调用 puts 是错误的。)

【讨论】:

  • realloc does move 数据到新位置!
  • @Olaf 他的意思是它不需要改变它所在的位置。
  • @Havenard:“那么不一定要分配不同的内存区域?这真的很难从“realloc 不一定将数据移动到新位置”中读取。他真的应该澄清这一点。
  • @Olaf realloc 可以将数据移动到新位置。在这种情况下,没有必要也没有发生。
  • @Olaf 新位置可能与旧位置具有相同的内存地址。在这种情况下,数据不会被移动。 (注意C11的文字并没有说数据被移动了,只是新旧对象的内容是一样的)
【解决方案2】:

realloc 返回一个更新后的指针,该指针指向(至少)请求大小的内存块。它可以通过分配一个新的内存块并释放旧的内存块来做到这一点,通过返回原始指针值(并更新系统的内部簿记信息以跟踪更新后的大小)。如果它分配了一块新的内存,它会从旧位置复制数据;复制的数量是旧尺寸或新尺寸,以较小者为准。

char * ptr = malloc(sizeof(char) * 7);
int i;
for(i = 0;i<7;i++){
    ptr[i]= "pointer"[i];
}
puts(ptr);

这具有未定义的行为。 puts 需要一个指向字符串的指针,而字符串需要一个空字符 '\0' 来终止它。所以对于一个长度为 7 个字符的字符串,需要分配 8 个字节。 (完成此操作后,您可以使用strcpy 复制数据。)

似乎可以工作,因为在刚刚超过分配内存末尾的位置可能恰好有一个空字符。

ptr = realloc(ptr,sizeof(char) * 3);
puts(ptr);

更多未定义的行为。在realloc 调用之后ptr 指向(至少)3 个字节的内存。由于新尺寸小于旧尺寸,系统能够将新块放置在与旧块相同的位置;它只是将其标记为较小的尺寸。

所以通过“运气”,ptr 指向一个字符序列:

'p', 'o', 'i', 'n', 't', 'e', 'r', '\0'

但只有前 3 个字节在内存中,您可以安全地访问。

realloc 可能分配了一个新的内存块,并且该块末尾之后的数据可能包含'n', 't', 'e', 'r', '\0'以外的其他内容。就此而言,即使realloc 返回指向同一位置的指针,新的较小块末尾之后的内存也可能被破坏。

还有一件事:realloc 如果无法分配内存,则返回一个空指针。即使您试图缩小分配的内存,您也应该始终检查这一点。如果realloc 失败,它会留下原始分配的内存;您应该考虑将结果分配给不同的指针对象,以便您仍然可以访问原始内存。 (除非你无论如何都要终止程序;那没关系。)

【讨论】:

    【解决方案3】:

    那是因为缩小时指针没有改变,将数据留在原处。

    所以剩余的 5 个字节 nter\0 仍然存在,在分配区域之外,但在它旁边的同一个地方。注意 C 中的字符串以空值结尾,这意味着符号 \0(值为零的字符)用作确定字符串结束位置的标记。

    当你打印一个 C 字符串时,它会一直打印,直到找到一个 \0,即使这个 \0 超出了分配的内存空间的范围,调用被概括为“未定义行为”的东西,你不应该这样做,这可能会或可能不会起作用,或者可能会或可能不会崩溃,具体取决于条件。

    即使它确实将您的变量移动到内存中的其他位置,它也只会移动poi 部分,并且会丢失空终止符,从而导致您的字符串格式错误,从而导致更多未定义的行为。它会打印内存垃圾,直到它崩溃或找到\0

    【讨论】:

    • 调用 UB 发生的最糟糕的事情是 似乎 工作(在测试期间)。根据墨菲定律,它最终会在最不愉快的情况下造成最大的麻烦。 (希望这是可以理解的)。
    猜你喜欢
    • 1970-01-01
    • 2015-05-26
    • 1970-01-01
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多