C有字符串类型吗? [关闭]
Posted
技术标签:
【中文标题】C有字符串类型吗? [关闭]【英文标题】:Does C have a string type? [closed] 【发布时间】:2013-01-20 11:39:15 【问题描述】:我最近开始使用 C 语言进行编程,来自 Java 和 Python。现在,在我的书中,我注意到要制作一个“Hello World”程序,语法是这样的:
char message[10]
strcpy(message, "Hello, world!")
printf("%s\n", message);
现在,这个例子使用了一个 char 数组,我想知道 - 字符串发生了什么?为什么我不能简单地使用其中之一?也许有其他方法可以做到这一点?
【问题讨论】:
C 没有字符串。 你需要char信息[14]; 顺便说一下,你的 strcpy 会溢出你的 char 数组。您至少需要一个长度为 14 的 char 数组(13 个字符 + nul 终止符) @Grhmstrncmp
是错误的函数有两个原因,首先它是一个 cmp
函数而不是 cpy
函数,其次你应该使用 strlcpy
来确保 nul 终止字节被使用。 strncpy
可能会给你一个未终止的字符串。
@wich:我的意思是strncpy
- 但不知道strlcpy
看起来确实是一个更好的选择。谢谢,我今天学到了一些东西..
【参考方案1】:
C 不支持一流的字符串类型。
C++ 有 std::string
【讨论】:
【参考方案2】:C
中没有 string
类型。您必须使用 char 数组。
顺便说一句,您的代码将不起作用,因为数组的大小应该允许整个数组适合加上一个额外的零终止字符。
【讨论】:
【参考方案3】:在 C 中,字符串只是一个字符数组,以空字节结尾。因此,当您阅读 C 代码时,char*
通常发音为“字符串”。
【讨论】:
【参考方案4】:C 不像 Java 那样拥有自己的 String 数据类型。
只有我们可以在 C 中使用字符数组或字符指针来声明 String 数据类型 例如:
char message[10];
or
char *message;
但你至少需要声明:
char message[14];
复制“你好,世界!”进入消息变量。
13 : “Hello, world!”的长度 1:用于标识字符串结尾的'\0'空字符【讨论】:
【参考方案5】:首先,您不需要做所有这些。特别是,strcpy
是多余的 - 您不需要将字符串复制到 printf
它。您的message
可以使用该字符串来定义。
其次,您没有为“Hello, World!”留出足够的空间。字符串(message
至少需要 14 个字符,允许空终止符多出一个)。
关于为什么,这是历史。在汇编程序中,没有字符串,只有字节、单词等。Pascal 有字符串,但是静态类型存在问题,因为它 - string[20]
是与 string[40]
不同的类型。即使在早期,也有一些语言避免了这个问题,但这会导致间接和动态分配开销,这在当时更多是一个效率问题。
C 只是选择避免开销并保持非常低的水平。字符串是字符数组。数组与指向第一项的指针密切相关。当数组类型“衰减”为指针类型时,缓冲区大小信息会从静态类型中丢失,因此您不会遇到旧的 Pascal 字符串问题。
在 C++ 中,std::string
类可以避免很多此类问题 - 并且具有动态分配开销,但现在我们通常不关心这些。无论如何,std::string
是一个库类——下面有 C 风格的字符数组处理。
【讨论】:
【参考方案6】:C 没有也从来没有原生字符串类型。按照惯例,该语言使用以空字符结尾的char
数组,即'\0'
。语言标准库中的函数和宏支持以空字符结尾的字符数组,例如,strlen 迭代 char
的数组,直到遇到 '\0'
字符,strcpy 从源字符串复制直到它遇到'\0'
。
在 C 中使用以空字符结尾的字符串反映了这样一个事实,即 C 只比汇编语言更高级一点。当时assembly language for the PDP-10 and 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
将从message
地址开始将所有 14 个字节(包括空终止符)写入内存。前 10 个字节将写入堆栈上为 message
分配的内存,其余 4 个字节将简单地写入堆栈的末尾。在这种情况下,将这四个额外字节写入堆栈的后果很难预测(在这个简单的示例中,它可能不会造成任何伤害),但在实际代码中,它通常会导致数据损坏或内存访问违规错误。
【讨论】:
一个没有'\0'
字节的char
数组不是字符串。
写入 4 个额外字节是否会导致初始 10 个字节损坏或损坏堆栈上的其他一些内存位置(4 个字节)?在什么情况下会抛出分段错误而不是内存损坏?【参考方案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'
)。请注意,在上述情况下,哨兵字符会自动为您附加。
【讨论】:
以上是关于C有字符串类型吗? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章