C字符串:简单的问题

时间:2011-06-27 04:54:26

标签: c++ c string

我在下面初始化了三个变量:

char c1[] = "Hello";
char c2[] = { 'H', 'e', 'l', 'l', 'o', '\0'};
char* c3 = "Hello";

我知道c1和c2是相同的,并且它们都是字符串,因为它们被\ 0终止。但是,c3与c1和c2不同。这是因为c3不以\ 0结尾吗?这是否意味着c3不是字符串?如果c3不是字符串,那么printf("%s", c3);为什么不给出错误?谢谢!

修改

有没有理由可以修改c1和c2但是c3不能?

9 个答案:

答案 0 :(得分:10)

就C而言,c3与其他人之间最相关的区别是,您不能尝试使用c3修改基础角色。我经常觉得这样想是有帮助的:

char *xyz = "xyz";

将在堆栈上创建一个可修改的指针,并使其指向不可修改的字符序列{'x','y','z','\0'}。另一方面,

char xyz[] = "xyz";

将在堆栈上创建一个可修改的数组,其大小足以容纳字符序列{'x','y','z','\0'},然后将该字符序列复制到其中。然后可以修改数组内容。请记住,标准没有说明堆栈,但这通常是如何完成的。毕竟,它只是一种记忆辅助。

形式上,c3是指向字符串文字的指针,而c1c2都是字符数组,两者都碰巧以空字符结尾。当它们被传递给像printf这样的函数时,它们会衰减到指向数组的第一个元素的指针,这意味着它们将在该函数中与c3相同地处理(实际上它们在相当的情况下衰减)在极少数情况下,请参见以下c99中的第三项引用(例外)。

C99的相关部分为6.4.5 String literals,这解释了为什么不允许您修改c3指向的内容:

  

如果这些数组的元素具有适当的值,则未指定这些数组是否相同。如果程序试图修改这样的数组,则行为是未定义的。

以及为什么 具有空终止符:

  

在转换阶段7中,将值0的字节或代码附加到由字符串文字或文字产生的每个多字节字符序列。

6.3.2.1 Lvalues, arrays, and function designators下的6.3 Conversions状态:

  

除非它是sizeof运算符或一元&的操作数。运算符,或者是用于初始化数组的字符串文字,具有类型''数组类型''的表达式将转换为类型为''指向类型'的指针的表达式,指向数组对象的初始元素,不是左值。如果数组对象具有寄存器存储类,则行为未定义。

答案 1 :(得分:5)

第一点,

char* c3 = "Hello"; // may be valid C, but bad C++!

是一种容易出错的样式,所以不要使用它。而是使用,

const char* c3 = "Hello";

这是一个有效的代码。指针c3指向存储"Hello"字符串的位置的地址。但是你不能修改 *c3(即c3的内容)作为早期案例(如果这样做,则是未定义的行为)。

答案 2 :(得分:3)

c3是一个指向字符串的指针,这是printf("%s", ...)期望的参数。

printf("%s", c1)printf("%s", c2)也可以起作用的原因是因为在C数组中,在表达式中很容易“衰减”指针。实际上,关于数组名称不会衰减到表达式中的指针的唯一情况是它被用作sizeof运算符的操作数或&的操作数(地址-of )运营商。

这导致了常见的混淆,即指针和数组在C中是等价的,这是不正确的。只是在C数组中几乎可以在任何地方使用指针。一个例外是它们不能被分配,除非它们是下标的(结果是一个表达式将它们视为指针)。

请注意,最后一个字符串还有另外一个区别 - 因为它是一个sting文字,它不能被修改(如果你尝试将会发生什么,这是未定义的)。 `

答案 3 :(得分:1)

c1c2分配6个字节的内存并在其中存储以null结尾的字符串。

然而,

c3 program 内存中分配(也是以null结尾的)字符串,并创建一个指向它的指针,即字符串与其他指令一起存储而不是在堆栈(或堆?有人纠正我)所以编辑它将是不安全的。

答案 4 :(得分:1)

在C中,常量"string"可以有两个含义,基于它使用的上下文。它可以表示可执行文件的ro部分中的字符串(虽然我不认为标准拼写出来),使const char *foo = "bar"语句初始化foo指向某个位置在加载的可执行文件的内存中。如果二进制blob("bar")确实位于ro部分,并且您执行了foo[0] = 'x'之类的操作,则最终会得到SIGSEGV

但是,当您编写char x[] = "Hello"(或char x[6] = "Hello")时,您使用"Hello"作为数组初始值设定项(如int x[2] = { 1, 2 })和x }只是在堆栈上分配的常规(可写)数组。在这种情况下,"Hello"只是{'H', 'e', 'l', 'l', 'o', '\0' }的缩写。

"bar""Hello"均为空终止。

答案 5 :(得分:0)

c3不会被NUL或NULL终止。它是指向由NUL终止的字符串的指针。

答案 6 :(得分:0)

这是一个字符串它指向一个字符串,但它有风险。

答案 7 :(得分:0)

它是指向具有不同终止的字符串的指针。

答案 8 :(得分:0)

C3是指向字符串第一个单元格的指针。 C1,C2只是一个没有人指出的常规数组。