为什么需要使用strcpy()函数?

40
请问为什么在以下代码片段中需要使用strcpy()函数将字符串赋值给字符数组?
int main(void) {

char s[4];

s = "abc"; //Fails
strcpy(s, "abc"); //Succeeds

return 0;
}

s = "abc"为什么会失败?并且为什么在声明后只有strcpy()才能用于将字符串赋值给char数组?对我来说,这似乎很奇怪,因为你必须使用一个函数来执行基本的赋值操作。

4个回答

41
在C语言中,数组无法直接赋值或复制初始化。这是C语言中数组的特点。在值上下文(赋值符号右边),数组会自动衰变为指针,正式因为这个原因,防止了赋值和复制初始化操作。所有类型的数组都适用,不仅限于char数组。
这种数组行为被C语言继承自其前身B和BCPL语言。在那些语言中,数组是由物理指针表示的。(很明显,当你将一个数组赋给另一个数组时,重分配指针并不是你想要发生的事情。)在C语言中,数组不是指针,但它们可以通过在大多数情况下自动衰变成指针来“模拟”B和BCPL数组的历史行为。到现在为止,这个历史的遗留问题使得C语言中的数组仍然无法复制。
上述规则有一个例外,就是使用字符串字面量进行初始化。例如:
char c[] = "abc";

从概念上讲,我们将字符串文字"abc"复制到数组c中。另一个例外是包含在结构类型中的数组,在整个结构对象被复制时被复制。就是这样。

这意味着每当您想要复制裸露的(非封装的)数组时,您必须使用库级别的内存复制函数,例如memcpystrcpy只是专门针对字符串工作的其中一种。


1
只是为了澄清,所有的数组类型都可以使用形如 { val0,val1,... } 的适当初始化器进行初始化。 - Jens Gustedt
你不一定需要使用库函数;你可以分配单个字符,例如 for (char *dst = s, *src = "abc"; *dst++ = *src++;) ;。然而,库函数是更好的选择,因为它更易于阅读,并且可能针对系统进行了优化。 - M.M
1
是的,更深入地谈一下@AnT所说的,strcpy()memcpy()几乎完全相同,只是它包括空字节。 - RastaJedi

16

这就是 C 语言中数组的本质。你不能直接对其进行赋值操作,但可以使用指针:

char *p;
p = "abc";

顺带一提,这里有一个C FAQ

C语言中的数组是“二等公民”;这种偏见的结果之一是你不能对它们进行赋值


是的,我确实使用指针,但我不明白为什么在我的例子中 s = "abc" 不起作用。s 是一个字符数组,"abc" 也是。 - C_p678
5
@s_p678 - 不是的,s 是一个字符数组,而 "abc" 是指向常量字符串的指针。 - MByD
3
@MByD:并不完全正确。"abc" 不是一个指针。"abc" 是一个类型为 char[4] 的数组,在此上下文中会衰减成类型为 char* 的指针。需要注意的是,在 C 语言中,字符串并非常量。它确实是不可修改的,但其本身的类型并没有包含 const 限定符。 - AnT stands with Russia
2
@AndryT:更严谨地说,“const”和“constant”是两个非常不同的概念。“const”可能应该被称为“readonly”。常量或常量表达式是可以在编译时计算的,而const对象是不能在运行时修改的。考虑const int r = rand(); - Keith Thompson

4
简短回答:历史原因。C语言没有内置的字符串类型。直到C++出现后,才有了std::string,但即使这样,它也不是在第一个实现中就出现了。
详细回答:"abc"的类型不是char[],而是char*。strcpy是一种机制,可以复制指针所指向的数据(在这种情况下是ABC)。
strcpy并不是初始化数组的唯一方法,但它足够智能,可以检测和遵守字符串末尾的终止0。你也可以使用memcpy将字符串复制到s中,但这需要传入要复制的数据的长度,并确保终止0(NULL)存在于s中。

3
"abc"的类型是char[4] - Jens Gustedt
3
“strcpy”不是一个初始化,而是一个赋值操作。字符数组可以像其他所有数组一样进行初始化,详见AndreyT的回答。 - Jens Gustedt

0

C语言缺乏方便的语法来获取指向字符串字面量及其长度的指针。一些语言,包括许多Pascal方言,在每个字符串前加上一个报告其长度的字节;这对于许多目的非常有效,但将字符串字面量限制为255个字符。C的方法允许容纳任意长度的字符串字面量,但仅添加一个字节的开销,无论长度如何。

零终止字符串在除了字符串字面量之外的几乎所有情况下都不如其他形式优秀,但是字面量远远是最常见的字符串形式,因此许多程序将不得不处理它们,因此有很大的优势使库函数有效地处理它们;然后变得更容易在不理想的情况下使用零终止字符串,而不是为其他类型设置单独的库例程。


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