“以零结束”是什么意思?
我正在进入C / C ++,很多术语对我来说都是不熟悉的。 其中一个是由零终止的变量或指针。 内存空间被零终止是什么意思?
以ASCII格式取字符串Hi
。 它在内存中最简单的表示是两个字节:
0x48 0x69
但那段记忆在哪里结束? 除非你还准备传递字符串中的字节数,否则你不知道 – 内存块本身并没有长度。
所以C有一个标准,字符串以零字节结束,也称为NUL
字符:
0x48 0x69 0x00
字符串现在明确地是两个字符长,因为在NUL
之前有两个字符。
它是一个保留值,用于指示字符串中(例如)字符序列的结尾。
更正确地称为null(或NUL)终止 。 这是因为使用的值为零,而不是’0’的字符代码。 为了澄清区别,请查看ASCII字符集的表格。
这是必要的,因为像C这样的语言具有char
数据类型,但没有string
数据类型。 因此,由devleoper决定如何在其应用程序中管理字符串。 通常的做法是使用一个带有空值的char
数组来终止(即表示结束)字符串。
请注意,字符串的长度与最初声明的char数组的长度之间存在区别。
char name[50];
这声明了一个包含50个字符的数组。 但是,这些值将是未初始化的。 因此,如果我想存储字符串"Hello"
(5个字符长),我真的不想打扰将剩余的45个字符设置为空格(或其他一些值)。 相反,我将NUL值存储在字符串中的最后一个字符之后。
更新的语言,如Pascal,Java和C#,定义了特定的string
类型。 它们具有标题值以指示字符串中的字符数。 这有几个好处; 首先,您不需要走到字符串的末尾来查找其长度,其次您的字符串可以包含空字符 。
维基百科在字符串(计算机科学)条目中有更多信息。
终止于零
这是你的尖头发老板解雇你的时候。
C中的数组和字符串只是指向内存位置的指针。 通过指针,您可以找到数组的开头。 数组的结尾是未定义的。 字符数组的末尾(字符串)是零字节。
所以,在内存字符串中,hello写成:
68 65 6c 6c 6f 00 |hello|
它指的是C字符串如何存储在内存中。 字符串迭代中由\ 0表示的NUL字符出现在内存中C字符串的末尾。 例如,没有其他元数据与C字符串相关联,例如长度。 注意NUL字符和NULL指针之间的不同拼写。
处理可以具有不同长度内容的数组(如字符串)有两种常用方法。 第一种是分别保存数组中存储的数据长度。 Fortran和Ada以及C ++的std :: string等语言都是这样做的。 这样做的缺点是你不得不将这些额外的信息传递给处理你的数组的所有东西。
另一种方法是在数组末尾保留一个额外的非数据元素作为标记。 对于哨兵,您使用的值不应出现在实际数据中。 对于字符串,0(或“NUL”)是一个不错的选择,因为它是不可打印的,并且在ASCII中没有其他用途。 那么C(以及从C复制的许多语言)做的是假设所有字符串都以0结尾(或“被”终止“)。
这有几个缺点。 首先,它很慢。 每当例程需要知道字符串的长度时,它就是O(n)操作(搜索整个字符串寻找0)。 另一个问题是你有一天可能因为某些原因想在字符串中输入0,所以现在你需要一整套第二组字符串例程来忽略null并且无论如何都使用一个单独的长度(例如:strnlen())。 第三个大问题是,如果有人忘记将0放在最后(或者它以某种方式被删除),那么进行长时检查的下一个字符串操作将快速地通过内存进行,直到它发生随机找到另一个0,崩溃,或者用户失去耐心并杀死它。 这样的错误可能是一个严肃的PITA追踪。
出于所有这些原因,通常认为C方法不受欢迎。
C风格的字符串由NUL字符(’\ 0’)终止。 这为对字符串(例如strlen,strcpy)进行操作的函数提供了标记,以用于标识字符串的结尾。
虽然“以零结尾”的典型例子是C中的字符串,但这个概念更为通用。 它可以应用于存储在数组中的任何事物列表,其大小未明确知道。
诀窍就是避免通过将一个sentinel值附加到数组的末尾来传递数组大小。 通常,使用某种forms的零,但它可以是任何其他forms(如果数组包含浮点值,则为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
成员被定义为表示列表末尾的struct
成员。 我已经看过这样的function定义,但我现在无法找到一个很好的例子,抱歉。 无论如何,调用代码看起来像这样: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);