realloc 函数如何处理字符串中的 null 字节?

3

我是一名相对新手的C程序员。我正在为一个练习C语言的项目编写教程,现在正在审查以下代码。 abuf 结构体的目的是创建一个可以追加的字符串。以下是代码:

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

typedef struct abuf {
    char* str;
    unsigned int size;
} abuf;


void abAppend(abuf *ab, const char *s, int len) {
  char *new = realloc(ab->str, ab->size + len);
  if (new == NULL) return;
  memcpy(&new[ab->size], s, len);
  ab->str = new;
  ab->size += len;
}

int main(void) {
    abuf ab = {
        NULL,
        0
    };

    char *s = "Hello";

    abAppend(&ab, s, 5);
    abAppend(&ab, ", world", 7);

    return 0;
}

一切都编译通过了,我的测试(为简单起见已删除)显示字符串“Hello”存储在ab的str指针中,然后在第二次调用abAppend后变成“Hello, world”。但是,这段代码中的某些内容让我感到困惑。在对abAppend进行初始调用时,str指针为空,因此根据其手册,realloc应该像malloc一样分配5个字节的空间来存储字符串。但是,字符串“Hello”还包含终止的空字节\0。如果我理解正确,这应该是字符串的第六个和最后一个字节。如果我们将“Hello\0”存储在只足以存储“Hello”的malloc容器中,那么这个空字节不就丢失了吗?

在第二次调用abAppend时,我们将", world"连接到strrealloc将扩大str到12个字节,但第13个字节\0没有计算在内。然而,一切都正常运行,如果我使用像for (int i = 0; ab.str[i] != '\0'; i++)这样的循环测试空字节,循环可以正常工作并增加i 12次(0到11),并停止,这意味着它在第13次迭代时遇到了空字节。我不明白的是,如果我们没有为其分配空间,为什么会遇到空字节。

我尝试通过奇怪的字符串组合来破坏此代码,但无济于事。我还尝试在每次调用abAppend时分配一个额外的字节,并稍微更改函数以考虑额外的空间,结果与此版本完全相同。我不明白空字节是如何被处理的。


2
如果你想要NUL终止,你需要添加+1 - tadman
2
除了realloc会复制原始内存内容(但不会初始化额外的内存),mallocrealloc都不会初始化分配的内存。 - Weather Vane
4
malloc()realloc()对字符串一无所知,它们只分配请求的内存量,可以用于任何类型的数据。你需要自己考虑空字节的问题。 - Barmar
2
“Hello” 是6个字节,而不是5个字节,“,world” 是8个字节,而不是7个字节。 - Weather Vane
2个回答

5

realloc 如何对待字符串中的空字节?

realloc 的行为不受其管理的内存内容影响。

但字符串 "Hello" 含有终止空字节 \0。应该是字符串的第六个和最后一个字节...

使用 memcpy(&new[ab->size], s, len); 进行字符复制,其中 len 为 5。 memcpy 不考虑是否存在终止空字节就进行字符复制。给定长度为 5,它将复制 5 个字节,不会在这些字节后追加终止空字符。

realloc 将扩大 str 到 12 字节,但未计算第 13 个字节 \0。

在对 abAppend 进行第二次调用时,通过 memcpy 再复制了 7 个字节,紧接着前面的 5 个字节。 memcpy 给定长度为 7,只会复制 7 个字节。

...它在第 13 次迭代时遇到了空字节。

当您测试 ab.str[12] 时,您超出了 C 标准定义的行为规则。 ab.str[12] 超出了已分配内存的范围。如果此操作是在一个已完成一些先前工作的较大程序中进行的,则该字节可能仅包含空字节,因为该内存尚未被其他进程用于其他目的,这就是为什么您的循环停止的原因。如果您在一个已经执行过其他工作的更大程序中尝试此操作,那么该字节可能包含不同的值,并且您的测试可能以各种方式出错。


0

你说得对,你只为字符串“Hello”中的字符分配了空间,但没有为终止的空字节分配空间,第二次调用只添加了足够的字节以容纳字符串“,world”的字符,而没有空终止字节。

因此,你拥有的是一个字符数组,而不是一个字符串,因为它没有空终止符。如果你尝试读取超过分配的字节,就会触发未定义行为,UB可能表现为事情似乎正常工作的一种方式。

所以你“幸运地”发现事情好像是为终止的空字节分配了空间并设置了它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接