GCC - memcpy自动复制空字符?

4

来自 cplusplus.com 关于 memcpy 函数的描述:"此函数不会检查源字符串是否有终止的 null 字符 - 它总是精确地复制 num 个字节。"

那么以下代码应该会导致运行时错误,对吗?

char str1[20] = "";
char str2[20] = "Another Text---";

memcpy(str1, str2, strlen(str2));
printf("%s\n%s", str1, str2);

但是,我总是在我的gcc编译器中得到正确的输出。这是否意味着memcpy实际上从str2的末尾复制了空字符,还是只是一个随机情况?
编辑:我使用str1 [20] =“A”获得相同的行为,因为一些答案指出str1 [20] =“”会用所有NULL字符初始化字符串。

在您的情况下,num字节是“Another Text---”长度,因此从缓冲区str2复制该数量的字符到缓冲区str1不会导致运行时错误。但是,如果未将str1初始化为null,则将其用作空终止字符串将导致崩溃(可能)。 - paulm
4个回答

16

这个谜题的关键在于初始化器""。它等同于:

char str1[20] = { 0 };

在GCC中,这句话与以下内容相同(在C++中始终如此):

char str1[20] = { };
或者
char str1[20] = { 0, 0, 0, ..., 0 };   // 20 times

所有这些初始化器都将数组初始化为二十个零。

memcpy不会复制空终止符(因为strlen不计算它),但目标数组在一开始被正确地清零,所以一切都很好。


+1,但 {} 的初始化器对于 C 来说不正确,它只存在于 C++ 中,遗憾。 - Jens Gustedt
3
@Rafi提到的C99标准中6.7.8.21规定,如果花括号内的初始化列表少于数组或结构体成员数量,或者用于初始化已知大小数组的字符串字面量字符数少于数组元素数量,则剩余部分将隐式地与具有静态存储期限的对象一样被初始化,这些对象将被零初始化。 - netcoder
1
@JensGustedt:空的初始化列表是由gcc扩展支持的(问题标记为gcc)。 - netcoder
@R..:啊,谢谢,我以为在较新的C语言中允许使用空括号初始化器,但我错了。我添加了一条注释说明这是GCC的扩展功能。 - Kerrek SB
1
@R.. 我完全同意,这是我已经列在两种语言之间的不必要差异清单上的一项:http://p99.gforge.inria.fr/defects-and-improvements/ - Jens Gustedt
显示剩余5条评论

1

用一个比str2更长的字符串初始化str1,然后再试一次,你会发现它不会复制空值。你只是幸运地得到了一个零填充的str1。我改变了代码以显示输出对str1内容的依赖性:

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

int main()
{
    char str1[20] = "xxxxxxxxxxxxxxxxxxx";
    char str2[20] = "Another Text---";

    memcpy(str1, str2, strlen(str2));
    printf("%s\n%s", str1, str2);

    return 0;
}

输出将会是:

Another Text---xxxx
Another Text---

1

memcpy是将strlen(str2)(这并不意味着空字节被复制)个字节从str2复制到str1。由于str1有足够的空间容纳那么多字节,因此它可以正常工作。memcpy会精确地复制内存,就像它所说的那样。

如果您尝试从一个变量复制超过20个字符的内容到另一个变量中,则会出现错误。您也不应该在任何一个变量中存储超过19个字符。

正如kerrek所解释的那样,在这种特定情况下,将str1初始化为nulls也有助于您不必担心空终止符。


这个答案没有抓住重点,Kerrek的解释更好,并且特别是目标缓冲区已经被初始化为0这一事实是理解这里发生了什么至关重要的。 - Jens Gustedt

1

你已经告诉它要复制 strlen(str2) 个字符,这恰好是字符串的长度(空索引)。

这与使用 strcpy() 相同。它试图表达的意思是,如果你需要复制 NULL,可以使用 memcpy,因为 strcpy() 会在找到第一个 null 时停止。


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