“以零终止”是啥意思?

Posted

技术标签:

【中文标题】“以零终止”是啥意思?【英文标题】:What does it mean to be "terminated by a zero"?“以零终止”是什么意思? 【发布时间】:2010-04-19 13:17:39 【问题描述】:

我正在学习 C/C++,并且出现了很多我不熟悉的术语。其中之一是以零结尾的变量或指针。内存中的空间以零结尾是什么意思?

【问题讨论】:

这句话以句号结尾。这个也是。但不是这个! 乔尔有一篇关于这个(和相关的事情)的好文章:joelonsoftware.com/articles/fog0000000319.html 【参考方案1】:

以 ASCII 格式获取字符串 Hi。它在内存中最简单的表示是两个字节:

0x48
0x69

但是那段记忆在哪里结束呢?除非您也准备好传递字符串中的字节数,否则您不知道 - 内存块本质上没有长度。

所以 C 有一个标准,字符串以零字节结尾,也称为NUL 字符:

0x48
0x69
0x00

字符串现在明确地是两个字符长,因为在 NUL 之前有两个字符。

【讨论】:

当您没有意识到需要三个字节来存储两个字符时,就会发生缓冲区溢出。 @MSalters:不,当您意识到长度为 2 的字符串由三个字符组成时,就会发生这种情况。 :-)【参考方案2】:

这是一个保留值,用于指示字符串中(例如)字符序列的结束。

更正确地称为 null (or NUL) terminated。这是因为使用的值为零,而不是“0”的字符代码。要澄清区别,请查看ASCII character set 的表格。

这是必要的,因为像 C 这样的语言有 char 数据类型,但没有 string 数据类型。因此,由开发人员决定如何在他们的应用程序中管理字符串。执行此操作的常用方法是使用带有空值的 chars 数组来终止(即表示结束)字符串。

注意字符串的长度和最初声明的char数组的长度是有区别的。

char name[50];

这声明了一个 50 个字符的数组。但是,这些值将未初始化。因此,如果我想存储字符串"Hello"(5 个字符长),我真的不想费心将剩余的 45 个字符设置为空格(或其他值)。相反,我在字符串的最后一个字符之后存储一个 NUL 值。

Pascal、Java 和 C# 等较新的语言定义了特定的 string 类型。它们有一个标头值来指示字符串中的字符数。这有几个好处;首先你不需要走到字符串的末尾来找出它的长度,其次你的字符串可以contain null characters。

***在String (computer science) 条目中有更多信息。

【讨论】:

回复:更新的语言:IIRC,称为 Pascal 字符串 Pascal 字符串专门使用单个字节来保存字符串长度。您可以很快猜到,这还不够!现代string 类型可能使用size_t 代替;如果您的字符串不适合,则该字符串也不会完全保存在内存中。【参考方案3】:

C 中的数组和字符串只是一个指向内存位置的指针。通过指针,您可以找到数组的开头。数组的结尾未定义。字符数组(即字符串)的结尾是零字节。

所以,在内存字符串中,hello 写成:

68 65 6c 6c 6f 00                                 |hello|

【讨论】:

【参考方案4】:

它指的是 C 字符串是如何存储在内存中的。字符串迭代中由 \0 表示的 NUL 字符出现在内存中 C 字符串的末尾。例如,没有与 C 字符串相关的其他元数据,例如长度。注意 NUL 字符和 NULL 指针之间的不同拼写。

【讨论】:

【参考方案5】:

有两种常见的方法来处理可以具有可变长度内容的数组(如字符串)。第一种是单独保留数组中存储的数据的长度。 Fortran 和 Ada 等语言以及 C++ 的 std::string 就是这样做的。这样做的缺点是您必须以某种方式将这些额外信息传递给正在处理您的数组的所有内容。

另一种方法是在数组末尾保留一个额外的非数据元素作为哨兵。对于哨兵,您使用一个永远不会出现在实际数据中的值。对于字符串,0(或“NUL”)是一个不错的选择,因为它是不可打印的并且在 ASCII 中没有其他用途。所以 C(以及从 C 复制的许多语言)所做的就是假设所有字符串都以 0 结尾(或“被终止”)。

这样做有几个缺点。一方面,它很慢。任何时候例程需要知道字符串的长度,它都是一个 O(n) 操作(搜索整个字符串寻找 0)。另一个问题是你有一天可能出于某种原因想在你的字符串中放一个 0,所以现在你需要一整套忽略 null 并使用单独长度的字符串例程(例如: strnlen() )。第三个大问题是,如果有人忘记将 0 放在末尾(或者它以某种方式被清除),下一个进行长度检查的字符串操作将愉快地在内存中进行,直到它碰巧随机找到另一个 0,崩溃,或者用户失去耐心并杀死它。此类错误可能是一个需要追踪的严重 PITA。

由于所有这些原因,C 方法通常被视为不受欢迎。

【讨论】:

【参考方案6】:

C 风格的字符串以 NUL 字符 ('\0') 结束。这为操作字符串(例如 strlen、strcpy)的函数提供了一个标记,用于标识字符串的结尾。

【讨论】:

【参考方案7】:

虽然“以零结尾”的经典示例是 C 中的字符串,但这个概念更为笼统。它可以应用于存储在数组中的任何事物列表,其大小未知。

诀窍就是通过在数组末尾附加一个标记值来避免传递数组大小。通常会使用某种形式的零,但也可以是其他任何形式(例如,如果数组包含浮点值,则为 NAN)。

以下是此概念的三个示例:

    当然是 C 字符串。单个零字符附加到字符串:"Hello" 编码为 48 65 6c 6c 6f 00

    指针数组自然允许零终止,因为空指针(指向地址零的指针)被定义为从不指向有效对象。因此,您可能会发现这样的代码:

    Foo list[] =  somePointer, anotherPointer, NULL ;
    bar(list);
    

    而不是

    Foo list[] =  somePointer, anotherPointer ;
    bar(sizeof(list)/sizeof(*list), list);
    

    这就是为什么execvpe() 只需要三个参数,其中两个传递用户定义长度的数组。由于传递给execvpe() 的所有内容都是(可能很多)字符串,这个小函数实际上支持两个级别的零终止:终止字符串列表的空指针和终止字符串本身的空字符。

    即使数组的元素类型是更复杂的struct,它仍可能以零结尾。在许多情况下,struct 成员之一被定义为表示列表结束的成员。我见过这样的函数定义,但我现在无法找到一个很好的例子,抱歉。无论如何,调用代码看起来像这样:

    Foo list[] = 
         someValue, somePointer ,
         anotherValue, anotherPointer ,
         0, NULL 
    ;
    bar(list);
    

    甚至

    Foo list[] = 
         someValue, somePointer ,
         anotherValue, anotherPointer ,
            //C zeros out an object initialized with an empty initializer list.
    ;
    bar(list);
    

【讨论】:

以上是关于“以零终止”是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

“?”是啥意思?在 Erlang 中是啥意思? [复制]

“this”这个词是啥意思,“static”是啥意思?

“||”是啥意思在 var 语句中是啥意思? [复制]

CVE是啥意思,CVE是啥意思

“内容”是啥意思:在招摇/openapi“响应”中是啥意思:

TypeScript 这个语法“-?”是啥意思? (破折号问题)是啥意思?