在不以空字符(\0)结尾的情况下定义字符串

37

在C/C++中,有哪些定义字符串并且不在末尾添加空字符(\0)的方法?

编辑:我只关心字符数组,而不是STL字符串。


12
C/C++ 是一种流行的程序设计语言,被广泛用于开发操作系统、应用软件、嵌入式系统等。 - Prasoon Saurav
11
这在C语言中不是一个字符串。在C语言中,字符串被定义为一个以NUL字符结尾的字符数组。你所要求的只是一个字符数组。 - paxdiablo
1
好像很多人都在重复同样的答案:使用std::string。 - Alexander Rafferty
1
自C++11起,std::string是以空字符结尾的: http://zh.cppreference.com/w/cpp/string/basic_string/data - Michael
9个回答

46
通常情况下,正如另一位作者所写的那样:
char s[6] = {'s', 't', 'r', 'i', 'n', 'g'};

如果您当前的C字符集是ASCII(通常为真)(今天很少有EBCDIC),

char s[6] = {115, 116, 114, 105, 110, 107};

还有一种很少被注意的方法,只适用于C语言(而非C ++)

char s[6] = "string";

如果数组大小太小,不能容纳最后的0(但足够大以容纳常量字符串的所有其他字符),则最后的零将不会被复制,但它仍然是有效的C代码(但无效的C++代码)。
显然,您也可以在运行时执行此操作:
char s[6];
s[0] = 's';
s[1] = 't';
s[2] = 'r';
s[3] = 'i';
s[4] = 'n';
s[5] = 'g';

或(同上所述ASCII字符集的备注)
char s[6];
s[0] = 115;
s[1] = 116;
s[2] = 114;
s[3] = 105;
s[4] = 110;
s[5] = 103;

您可以使用memcopy(或memmove、bcopy,但在这种情况下没有优势)。

memcpy(c, "string", 6);

或者strncpy

strncpy(c, "string", 6);

需要翻译的内容:

需要理解的是,在C语言中并不存在“字符串”(在C++中存在字符串对象,但那完全是另一回事)。所谓的字符串只不过是字符数组。而且,即使是char这个名称也是具有误导性的,它不是char类型,而只是一种数字类型。我们本可以称之为byte,但在早期,使用9位寄存器等奇怪硬件时,byte意味着8个比特。

由于char通常用于存储字符编码,C语言设计者想出了一种比将数字存储在char中更简单的方法。你可以在简单引号之间放置一个字母,编译器会理解它必须将此字符编码存储在char中。

我的意思是(例如),你不必做以下操作

char c = '\0';

要将代码0存储在字符中,只需执行以下操作:

char c = 0;

由于我们经常需要处理长度可变的一大堆字符,C设计者选择了一种“字符串”的约定。只需在文本结束处放置一个代码0即可。顺便说一下,这种字符串表示法有一个名称:“零终止字符串”,如果你看到一个变量名以“sz”开头,通常意味着它的内容是一个零终止字符串。
“C sz字符串”根本不是一种类型,只是普通的字符数组,就像整型数组一样正常,但是字符串操作函数(strcmp、strcpy、strcat、printf等)理解并使用0结尾的约定。这也意味着,如果你有一个没有零结尾的字符数组,你不应该调用任何这些函数,因为它很可能会做错事情(或者你必须特别小心,使用带有n字母的函数,如strncpy)。
这种约定最大的问题是,在许多情况下它是低效的。一个典型的例子是:你想把东西放在一个0结尾的字符串的末尾。如果你保留了大小,你可以直接跳到字符串的末尾,但是使用sz约定,你必须逐个字符地检查它。当处理编码Unicode等时,还会出现其他类型的问题。但是在创建C时,这个约定非常简单,完美地完成了工作。
现在,双引号之间的字母(如“字符串”)不再像过去一样是普通的字符数组,而是const char *。这意味着指针指向的内容是一个不应该被修改的常量(如果你想修改它,你必须先复制它),这是一个好事情,因为它有助于在编译时检测到许多编程错误。

9
+1,但是有一点需要挑剔的是,字符串字面值(例如“hi”)的类型不是const char,而是const char [3],其中3是字符数加上1(用于结尾的0)。它可以直接赋给const char,因为数组会衰减为指向第一个元素的指针,但是这个简单的测试将显示差异:assert( sizeof(const char*) != sizeof("Hi there!") ) - David Rodríguez - dribeas
@David Rodríguez - dribeas:是的,你说得对,但我认为我的答案已经足够复杂了,不需要增加有关数组类型和指针之间差异的细节。对于那些对此感兴趣的人,我在这个回答中尝试解释它:https://dev59.com/u3A65IYBdhLWcg3w1SNC#3613350 - kriss

6

终止符用于终止字符串。如果没有它,您需要其他方法来确定其长度。

您可以使用预定义的长度:

char s[6] = {'s','t','r','i','n','g'};

你可以模拟Pascal风格的字符串:
unsigned char s[7] = {6, 's','t','r','i','n','g'};

您可以使用C++中的std::string(因为您对std :: string不感兴趣)。

最好使用一些预先存在的处理unicode的技术,或者至少了解字符串编码(即wchar.h)。

另外一条建议:如果您将此放入旨在在实际计算机上运行的程序中,可以考虑typedef您自己的“字符串”。这将鼓励您的编译器,如果您意外地尝试将其传递给期望C风格字符串的函数,则会抛出错误。

typedef struct {
    char[10] characters;
} ThisIsNotACString;

到目前为止,最完整的答案得分+1,主要缺少的是关于char s[3] = "abc";的讨论... - Tony Delroy
如果你已经定义了自己的结构体类型,那么混合长度和字符来模拟Pascal字符串是没有好处的。最好为长度单独设置一个字段,这样会更加清晰。这不会改变底层内存布局,但可以避免一些潜在的混淆。 - kriss
Pascal类型的字符串... 嗯... 字符串长度放在前面 - 没想到啊... - Isaaс Weisberg

6

C++的std::string不是以NUL结尾的。

P.S:NULL是一个宏1NUL\0。不要混淆。

1:C.2.2.3宏NULL

在任何<clocale><cstddef><cstdio><cstdlib><cstring><ctime><cwchar>中定义的宏NULL是这个国际标准(18.1)中实现定义的C++空指针常量。


1
NULL和NUL都只是说0的花哨方式。 - Alexander Rafferty
3
我希望我能再为那个带有脚注的NULL澄清加上一个+1。 - JoshD
4
NUL是空字符'\0'的名称,而NULL是空指针。在C语言中,它通常被定义为(void*)0,而在C++中仅为0。请注意,区别在于类型,而不是值。 - David Rodríguez - dribeas
5
自 C++11 起,std::string 是以空字符结尾的。 - Chaoran

1
在C++中,您可以使用string类,而无需处理空字符。

1

只是为了完整起见,彻底解决这个问题。

向量<字符>


0
在 C 语言中通常不会有更简单的解决方案。你可能可以像 Pascal 一样,在第一个字符中放置字符串的长度,但这有点麻烦,并且会限制你的字符串长度为可以适合第一个字符空间的整数大小。
在 C++ 中,我肯定会使用 std::string 类,可以通过以下方式访问:
#include <string>

作为一个常用的库,这几乎肯定比自己编写字符串类更可靠。

0

使用 std::string。

有数十种其他存储字符串的方法,但使用库通常比自己制作更好。我相信我们都可以想出很多没有空终止符的疯狂字符串方法 :).


0

NULL 终止的原因是为了让字符串的处理程序可以确定其长度。如果您不使用 NULL 终止符,您需要通过单独的参数/变量或作为字符串的一部分传递字符串的长度。否则,您可以使用另一个分隔符,只要它不在字符串本身中使用即可。

老实说,我不太理解您的问题,或者它是否实际上是一个问题。


-1

即使是 string 类也会将其存储为 null。如果您绝对不希望在内存中的字符串末尾有 null 字符,那么您必须手动创建一块字符块,并自行填充它。

我个人想不出任何实际情况,需要这样做,因为 null 字符是表示字符串结束的信号。如果您也要存储字符串的长度,那么我猜您已经以某种方式保存了一个字节,代价是变量大小(可能是 4 个字节),并且获得了更快访问所述字符串长度的速度。


你需要一个没有空字符的字符串用于HTTP头。它们可能会引起问题。 - Travis Pessetto
有一些地方,比如微控制器,它们具有有限的内存存储资源。根据存储的字符串数量,这可能会带来很大的开销。 - Adrian

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