C语言有字符串类型吗?

57

最近我开始学习C语言编程,之前我学过Java和Python。现在,在我的书里我注意到如果要编写一个“Hello World”程序,语法应该是这样的:

char message[10]
strcpy(message, "Hello, world!")
printf("%s\n", message);

现在,这个例子使用了char数组,我想知道-字符串呢?为什么我不能简单地使用其中之一?也许有另一种方法来做到这一点?


14
C语言没有字符串。 - sashoalm
7
你需要一个长度为14的字符数组 message。 - acraig5075
3
你的strcpy会溢出你的char数组。顺便说一下,你需要一个长度为14(13个字符+空终止符)的char数组。 - wich
5
strncmp这个函数有两个问题,首先它是一个比较(cmp)函数而不是一个复制(cpy)函数;其次,你应该使用strlcpy,它可以确保使用空终止符。strncpy可能会给你一个没有以空字符结尾的字符串。 - wich
1
字符串被包含在标准库<string.h>中,甚至Java的String也是来自于标准库。请注意,String不是一种数据类型,而是Java中的一个类名。 - user2493476
显示剩余3条评论
7个回答

92
C语言从未有过本地字符串类型。按照惯例,该语言使用以空字符(即'\0')结尾的char数组。语言标准库中的函数和宏提供对以空字符结尾的字符数组的支持,例如strlen遍历char数组直到遇到'\0'字符,strcpy复制源字符串直到遇到'\0'字符。
在C中使用以空字符结尾的字符串反映了C只是比汇编语言稍微高级一点的事实。零终止字符串在那个时候已经直接在PDP-10和PDP-11的汇编语言中得到支持。
值得注意的是,C字符串的这个特性导致了许多严重的缓冲区溢出漏洞,包括一些严重的安全漏洞。例如,如果你忘记将传递给源参数的字符字符串以空字符结尾,那么 strcpy 函数将继续从源字符串末尾所在内存的连续字节中复制,直到它恰好遇到一个 0 字符,这可能会覆盖内存中目标字符串位置后面的任何有价值的信息。
在你的代码示例中,字符串字面量 "Hello, world!" 将被编译成一个 14 个字节长的 char 数组。前 13 个字节将包含字母、逗号、空格和感叹号,最后一个字节将包含由编译器自动添加的以空字符结尾的字符 '\0'。如果你要访问数组的最后一个元素,你会发现它等于 0。例如:
const char foo[] = "Hello, world!";
assert(foo[12] == '!');
assert(foo[13] == '\0');

然而,在你的例子中,message 只有 10 个字节长。 strcpy 将写入所有 14 个字节(包括空终止符)到从 message 的地址开始的内存中。前 10 个字节将被写入为 message 分配的堆栈内存中,剩余的四个字节将简单地写入堆栈的末尾。在这种情况下,写入这四个额外的字节到堆栈上的后果很难预测(在这个简单的例子中,可能不会有任何问题),但在实际代码中,它通常会导致数据损坏或内存访问违规错误。

1
一个char数组如果没有'\0'字节,它就不是一个字符串。 - 12431234123412341234123
1
写入额外的4个字节会导致最初的10个字节损坏,还是会破坏堆栈上的其他内存位置(4个字节)?在什么情况下会抛出分段错误而不是内存损坏? - ns15

17

To note it in the languages you mentioned:

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'

一个字符数组是一系列连续的字符,在结尾处有一个唯一的哨兵字符(通常是一个空字符 '\0')。请注意,在上述情况下,哨兵字符会自动附加在末尾。

17

在C语言中,没有string类型。必须使用char数组来代替。

另外,你的代码将无法正常工作,因为数组的大小应该足够容纳整个数组,再加上一个零终止字符。


9

C语言中,字符串只是一个以空字符结尾的字符数组。因此,当你阅读C代码时,char*通常被发音为"字符串"。


7

C语言不支持一流字符串类型。

C++有std::string。


2

C语言没有像Java那样拥有自己的字符串数据类型。

在C语言中,我们只能使用字符数组或者字符指针声明字符串数据类型。例如:

 char message[10]; 
 or 
 char *message;

但是您至少需要声明以下内容:
    char message[14]; 

将“Hello, world!”复制到消息变量中。

  • 13:长度为“Hello, world!”
  • 1:用于标识字符串结尾的'\0'空字符

1
首先,你不需要做那么多事情。特别是,strcpy 是多余的 - 你不需要复制一个字符串只为了 printf 它。你可以用那个字符串来定义你的 message
其次,你没有为 "Hello, World!" 字符串留足够的空间(message 至少需要14个字符,允许额外的一个空字符)。
关于为什么这样做,这是历史原因。在汇编语言中,没有字符串,只有字节、字等。Pascal 有字符串,但由于此原因存在静态类型问题 - string[20] 是不同于 string[40] 的类型。即使在早期也有一些避免这个问题的语言,但这会导致更多的间接和动态分配开销,而那时效率问题更加突出。
C 选择避免这些开销并保持非常底层。字符串是字符数组。数组与指向它们的第一个项的指针非常相关。当数组类型“衰变”为指针类型时,缓冲区大小信息从静态类型中丢失,因此您不会遇到旧的 Pascal 字符串问题。
在C++中,有一个名为std::string的类,它避免了许多这些问题 - 并且具有动态分配开销,但现在我们通常不关心这个。无论如何,std::string是一个库类 - 其下面有C风格的字符数组处理。

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