我最近开始用C编程,来自Java和Python。现在,在我的书中,我注意到要制作一个“Hello World”程序,语法是这样的:
char message[10]
strcpy(message, "Hello, world!")
printf("%s\n", message);
现在,这个例子使用了一个char数组,我想知道 - 字符串发生了什么?为什么我不能简单地使用其中一个?也许有不同的方法来做到这一点?
答案 0 :(得分:60)
C没有,也从未有过本机字符串类型。按照惯例,该语言使用以{char}结尾的char
数组,即'\0'
。语言标准库中的函数和宏提供对以null结尾的字符数组的支持,例如strlen遍历char
数组,直到遇到'\0'
字符和{{3}为止从源字符串复制,直到遇到'\0'
。
在C中使用以null结尾的字符串反映了这样一个事实,即C只是比汇编语言更高级。当时已在strcpy中直接支持以零终止的字符串。
值得注意的是,C字符串的这个属性会导致相当多的讨厌的缓冲区溢出错误,包括严重的安全漏洞。例如,如果您忘记将作为源参数传递的字符串空值终止为strcpy
,则该函数将继续从源字符串末尾的内存中发生的任何事件中复制连续字节,直到它发生为遇到0
,可能会覆盖目标字符串在内存中的位置之后的任何有价值的信息。
在您的代码示例中,字符串文字“Hello,world!”将编译成一个14字节长的char
数组。前13个字节将包含字母,逗号,空格和感叹号,最后一个字节将保存空终止符'\0'
,由编译器自动添加。如果您要访问数组的最后一个元素,您会发现它等于0
。 E.g:
const char foo[] = "Hello, world!";
assert(foo[12] == '!');
assert(foo[13] == '\0');
但是,在您的示例中,message
只有10个字节长。 strcpy
将把所有14个字节(包括空终止符)写入从message
地址开始的内存中。前10个字节将写入堆栈中为message
分配的内存中,剩余的4个字节将简单地写入堆栈的末尾。在这种情况下,将这四个额外字节写入堆栈的结果很难预测(在这个简单的示例中,它可能不会伤害事物),但在实际代码中,它通常会导致数据损坏或内存访问冲突错误。
答案 1 :(得分:13)
string
中没有C
类型。你必须使用char数组。
顺便说一句,你的代码不起作用,因为数组的大小应该允许整个数组加上一个额外的零终止字符。
答案 2 :(得分:7)
在C中,字符串只是一个字符数组,以空字节结尾。因此,当您正在阅读C代码时,char*
通常会发音为“string”。
答案 3 :(得分:7)
用您提到的语言注明:
Java:
String str = new String("Hello");
的Python:
str = "Hello"
Java和Python都有“字符串”的概念,C没有“字符串”的概念。 C具有可以“只读”或可操作的字符数组。
C:
char * str = "Hello"; // the string "Hello\0" is pointed to by the character pointer
// str. This "string" can not be modified (read only)
或
char str[] = "Hello"; // the characters: 'H''e''l''l''o''\0' have been copied to the
// array str. You can change them via: str[x] = 't'
字符数组是一系列连续字符,末尾有唯一的标记字符(通常是NULL终结符'\0'
)。请注意,在上述情况下,会自动为您附加哨兵角色。
答案 4 :(得分:3)
C不支持第一类字符串类型。
C ++有std :: string
答案 5 :(得分:1)
C没有自己的String数据类型,如Java。
只有我们可以使用字符数组或字符指针在C中声明String数据类型 例如:
char message[10];
or
char *message;
但你需要至少声明:
char message[14];
复制“Hello,world!”进入消息变量。
答案 6 :(得分:0)
首先,你不需要做所有这些。特别是,strcpy
是多余的 - 您不需要将字符串复制到printf
它。您可以使用该字符串定义message
。
其次,你没有足够的空间来做那件事" Hello,World!" string(message
需要至少14个字符,允许额外的一个用于null终止符。)
关于为什么,它的历史。在汇编程序中,没有字符串,只有字节,单词等.Pascal有字符串,但静态类型存在问题,因为 - string[20]
是string[40]
的不同类型。有些语言甚至在早期就避免了这个问题,但是这导致了间接和动态分配开销,这在当时更多的是效率问题。
C只是选择避免开销并保持非常低的水平。字符串是字符数组。数组与指向其第一项的指针密切相关。当数组类型"衰变"对于指针类型,缓冲区大小信息会从静态类型中丢失,因此您不会得到旧的Pascal字符串问题。
在C ++中,std::string
类避免了很多这些问题 - 并且具有动态分配开销,但是现在我们通常不关心这一点。无论如何,std::string
是一个库类 - 下面是C风格的字符数组处理。