【问题标题】:Defining a string with no null terminating char(\0) at the end定义一个结尾没有 null 终止字符(\0)的字符串
【发布时间】:2010-09-30 06:32:09
【问题描述】:

在 C/C++ 中有哪些不同的方法来定义一个结尾没有空终止字符(\0)的字符串?

编辑:我只对字符数组感兴趣,而不对 STL 字符串感兴趣。

【问题讨论】:

  • C/C++ ?? [.....]
  • 不是 C 中的字符串。在C 中,字符串被定义为以NUL 字符结尾的字符数组。你要的只是一个字符数组。
  • 似乎很多人只是在重复相同的答案:使用 std::string。
  • std::string 自 C++11 以来被 nul 终止:en.cppreference.com/w/cpp/string/basic_string/data

标签: c++ c


【解决方案1】:

通常正如另一张海报所写:

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(但足以容纳常量字符串的所有其他字符),则不会复制最后的 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 而是一种数字类型。我们或许可以将其称为字节,但在过去,使用 9 位寄存器等奇怪的硬件,字节意味着 8 位。

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

我的意思是(例如)你不必这样做

char c = '\0';

要将代码 0 存储在 char 中,只需:

char c = 0;

由于我们经常需要处理一堆可变长度的字符,C 设计者也选择了“字符串”的约定。只需将代码 0 放在文本应该结束的地方。顺便说一句,这种字符串表示形式有一个名称“零终止字符串”,如果您在变量名称的开头看到两个字母 sz,通常意味着它的内容是一个以零结尾的字符串。

“C sz strings”根本不是一种类型,只是一个字符数组,就像一个 int 数组一样,但是字符串操作函数(strcmp、strcpy、strcat、printf 和许多其他函数)可以理解并使用 0 结尾约定。这也意味着如果你有一个非零终止的 char 数组,你不应该调用这些函数中的任何一个,因为它可能会出错(或者你必须格外小心并使用带有 n 的函数em> 名称中的字母,例如 strncpy)。

这种约定的最大问题是在很多情况下它效率低下。一个典型的例子:你想在一个以 0 结尾的字符串的末尾放一些东西。如果你保持了大小,你可以在字符串的末尾跳转,使用 sz 约定,你必须逐个字符地检查它。处理编码的 unicode 等时会出现其他类型的问题。但是在创建 C 时,这个约定非常简单,并且完美地完成了这项工作。

如今,像“string”这样的双引号之间的字母不再像过去那样是纯字符数组,而是const char *。这意味着指针指向的是一个不应该被修改的常量(如果你想修改它,你必须先复制它),这是一件好事,因为它有助于在编译时检测到许多编程错误。

【讨论】:

  • +1,但要挑剔,字符串文字的类型(即"hi")不是const char*,而是const char[3],其中3是字符数+1尾随 0。它可以直接分配给 const char*,因为数组衰减为指向第一个元素的指针,但这个简单的测试将显示差异:assert( sizeof(const char*) != sizeof("Hi there!") )
  • @David Rodríguez - dribeas:是的,你是对的,但我相信我的答案已经足够复杂,没有添加有关数组类型和指针之间差异的详细信息。对于那些对该主题感兴趣的人,我试图在该答案中进行解释:stackoverflow.com/questions/3613302/…
【解决方案2】:

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

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

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

你可以模拟帕斯卡风格的字符串:

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

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

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

还有一条评论:如果您将其放入旨在在实际计算机上运行的程序中,您可能会考虑对您自己的“字符串”进行类型定义。如果您不小心尝试将其传递给需要 C 样式字符串的函数,这将鼓励您的编译器出错。

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

【讨论】:

  • +1 是迄今为止最完整的答案,主要缺少的是对char s[3] = "abc";的讨论...
  • 如果您定义自己的结构类型,将长度与字符混合来模拟帕斯卡字符串没有任何好处。为 len 设置一个单独的字段会更干净。它根本不会改变底层内存布局,但会避免一些潜在的混乱。
【解决方案3】:

C++ std::strings 不是 NUL 终止的。

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

1:C.2.2.3 宏 NULL

宏NULL,定义在<clocale><cstddef><cstdio><cstdlib><cstring><ctime><cwchar> 是此 International 中实现定义的 C++ 空指针常量 标准 (18.1)。

【讨论】:

  • NULL 和 NUL 都只是 0 的一种花哨的说法。
  • 我希望我可以再给一个 +1 说明 NULL 的脚注。
  • @Alexander Rafferty:NUL 是空字符 '\0' 的名称,而 NULL 是空指针。在 C 中它通常被定义为 (void*)0,而在 C++ 中则只有 0。请注意,区别在于类型,而不是值。
  • 自 c++11 起,std::string 为空终止。
【解决方案4】:

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

【讨论】:

    【解决方案5】:

    只是为了完整起见,完全确定这一点。

    vector<char>

    【讨论】:

      【解决方案6】:

      使用 std::string。

      还有很多其他方法可以存储字符串,但使用库通常比自己制作更好。我相信我们都可以想出很多古怪的方法来处理没有空终止符的字符串:)。

      【讨论】:

        【解决方案7】:

        在 C 中通常不会有更简单的解决方案。您可能会做 pascal 所做的事情并将字符串的长度放在第一个字符中,但这有点麻烦,并且会将您的字符串长度限制为可以放入第一个字符空间的整数的大小。 在 C++ 中,我肯定会使用可以通过

        访问的 std::string 类
        #include <string>
        

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

        【讨论】:

          【解决方案8】:

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

          说实话,我不太明白你的问题,或者它是否真的是一个问题。

          【讨论】:

            【解决方案9】:

            即使是string 类也会将其存储为空值。如果出于某种原因,您绝对不希望在内存中的字符串末尾出现空字符,则必须手动创建一个字符块,然后自己填写。

            我个人想不出任何实际情况来解释您为什么要这样做,因为空字符是字符串结束的信号。如果您还要存储字符串的长度,那么我猜您已经以变量大小(可能是 4 个字节)为代价保存了一个字节,并且可以更快地访问所述字符串的长度。

            【讨论】:

            • 您需要一个不带空字符的 HTTP 标头字符串。它们可能会导致问题。
            • 有些地方如微控制器内存存储资源有限。根据存储的字符串数量,这可能会产生很高的开销。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-12-03
            • 1970-01-01
            • 2019-02-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多