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