31

为什么数字数组不以空字符结尾?

例如,

char name[] = {'V', 'I', 'J', 'A', 'Y', '\0'};

但是在数字数组的情况下,最后没有空字符的符号......

例如,

int marks[] = {20, 22, 23};

这背后的原因是什么?

4

12 回答 12

30

问的问题包含一个隐藏的假设,即所有char数组都以空字符结尾。事实上并非总是如此:这个char数组不以 结尾\0

char no_zero[] = { 'f', 'o', 'o' };

必须以空字符结尾的char数组是那些用作字符串的数组,它们确实需要终止。

在您的示例中,char数组仅以空字符结尾,因为您这样做了。编译器将为您插入空字符的唯一位置是从字符串文字声明 char 数组时,例如:

char name[] = "VIJAY";

在这种情况下,会自动插入空字符以使结果数组成为有效的 C 字符串。其他数值类型的数组不存在这样的要求,它们也不能从字符串文字初始化。换句话说,将零附加到数值数组将毫无用处,因为没有代码使用零来查找数组结尾,因为零是一个完全有效的数字。

指针数组有时会以 NULL 指针终止,这是有道理的,因为 NULL 指针不能与有效指针混淆。argv由 接收的字符串数组就是这种main()数组的一个示例。

于 2013-10-12T08:06:45.503 回答
7

数组可以以任何数组元素类型的有效值结尾。但只有\0终止的char数组才称为字符串。

例如

char name[]={'V','I','J','A','Y'};

有效,但不是字符串,限制是您不能在需要字符串strlen等的函数中使用它。


为了从下面的 OP 评论中澄清,根据 C 标准,任何字符文字,如'a','1'等,包括'\0'type int。您可以将 a放在数组'\0'的末尾,如下所示:int

int arr[] = {20, 22, 23, '\0'};

但是人们通常不会这样做,因为它是传统的,'\0'只用于终止字符串。上面的代码等价于

int arr[] = {20, 22, 23, 0};
于 2013-10-12T08:04:35.513 回答
5

字符串以 0 终止符结尾,但字符串数组不同。我们使用数组来存储字符串,但我们也使用数组来存储不是字符串的东西。这就是为什么数组通常不会自动附加 0 的原因。

此外,在 的任何通用数组中int,0 可能是有效(非标记)值。

于 2013-10-12T12:39:47.663 回答
4

如果你愿意,你也可以使一个int数组结束0

int iarray[] = {1, 2, 3, 0};

由于'\0'0完全相同,您甚至可以将0上面的内容替换为'\0'.

您的困惑可能是由于'\0'在声明中自动插入,例如:

char s[] = "hello";

在上面, 的定义s等价于char s[] = {'h', 'e', 'l', 'l', 'o', '\0'};。认为这是 C 标准提供的方便快捷方式。如果需要,您可以通过明确说明大小来强制使用非零终止字符数组:

char s[5] = "hello";

在上面的例子中,s不会被NUL终止。

另请注意,C 中的字符文字属于 类型int,因此'\0'实际上是int. (此外,char是一个整数类型。)

于 2013-10-12T08:20:18.607 回答
3

跟踪数组长度的方法有 3 种,也许有 4 种,其中只有两种在 C 语言中很常见:

  • 自己跟踪长度并将其与指针一起传递。

    这就是数组通常的工作方式。它不需要任何特殊格式,并且使子数组视图可以轻松表示。(添加到指针,减去长度,然后就可以了。)

    标准库中与非字符串数组一起使用的任何函数都已经期望这一点。为了安全起见,甚至一些与字符串混淆的函数(如strncator fgets)也会这样做。

  • 用一些“哨兵”值终止数组。

    这就是 C 字符串的工作方式。因为几乎每个现有的字符集/编码都定义'\0'为不可打印的“什么都不做”控制字符,因此它不是文本的典型部分,因此使用它来终止字符串是有意义的。

    请注意,当您使用 achar[]作为字节数组时,您仍然必须指定一个长度。那是因为字节不是字符。 一旦你处理的是字节而不是字符,0它就失去了作为哨兵值的意义,并回到普通的旧数据。

    sizeof(type)最大的问题是,对于大多数基本类型,每个可能的字节排列都可能代表一个有效的、有用的值。对于整数值,零特别常见;它可能是所有计算中最常用和最有用的数字之一。我完全希望能够将 a0放入整数数组中而不会丢失一半的数据。

    那么问题就变成了,什么是好的哨兵值?数组中应该禁止哪些其他合法数字?这个问题没有好的、普遍的答案;这完全取决于您的数据。所以如果你想做这样的事情,你就靠自己了。

    除了缺乏一个像样的哨兵值之外,这种方法在非字符类型上也失败了,还有一个原因:表示数组的子集更复杂。为了让递归函数将数组的一部分传递给自身,它必须插入标记值,调用自身,然后恢复旧值。要么,要么它可以传递一个指向范围开始和范围长度的指针。但是等等……这不是你想要避免的吗?:P

为了完整起见,其他两种方法:

  • 创建一个可以存储数组长度以及指向数据的指针的结构。

    这是一种更加面向对象的方法,也是数组在几乎所有现代语言中的工作方式(以及向量在 C++ 中的工作方式)。如果您有一个 API 来管理此类结构,并且如果您虔诚地使用该 API,它在 C 中可以正常工作。(面向对象的语言提供了一种将 API 附加到对象本身的方法。C 没有,所以你要坚持使用 API。)但是任何不是为使用你的结构而设计的函数都需要使用上述两种方法之一传递一个指针(可能还有一个长度)。

  • 传递两个指针。

    这是在 C++ 中传递“范围”的常用方法。您传递一个指向数组开头的指针,以及一个刚刚超过结尾的指针。但是,它在 C 中不太常见,因为使用原始指针,(start,length)(start,end)表示相同的数据——而 C 没有迭代器和模板,这使得它变得更加有用。

于 2013-10-21T16:58:30.677 回答
2

您需要以 C 字符串结尾,'\0'因为这是库知道字符串在哪里结束的方式。-termination 是 char 数组与字符串(a -terminated char-array)
的区别。NULNUL大多数字符串操作函数依赖于NUL知道字符串何时完成(并且它的工作已经完成),并且不适用于简单的字符数组(例如,它们将继续工作超出数组的边界,并一直持续到它会在内存中找到NUL某个位置 - 通常会破坏内存)。

于 2013-10-12T08:33:03.423 回答
2

Char 数组以特殊字符结尾'\0',因此可以将其视为字符串。并且当您操作字符串时,必须有某种方法来判断该字符串的长度(边界)。

strcpy的函数原型

char * strcpy ( char * destination, const char * source );

它如何知道从源复制多少字符到目标?答案是通过查看'\0'的位置。

在将'\0'字符串作为char *. 如果没有'\0'作为结束标记,您将无法将其char *视为字符串。

于 2013-10-12T08:46:31.240 回答
2

您不必'\0'在字符数组的末尾有一个字符!这是一个错误的假设。没有规则说你这样做。字符(char类型)与任何其他类型的数据完全一样。

如果要使用标准printf-family 函数打印数组,则必须有一个以空字符结尾的 char 数组。但仅仅是因为这些函数依赖于字符数组的结尾'\0'——char。

函数通常有关于它们期望的数据类型的规则。String ( char[]) 函数也不例外。但这不是语言要求,而是您使用的 API 具有这些要求。

于 2013-10-12T08:20:42.787 回答
1

char 数组不一定以 \0 结尾。

字符串以 \0 结尾是 C 约定。
这对于查找字符串的结尾很有用。

但是,如果您只对保存 char 类型的数据感兴趣,则可以在末尾添加 \0 或不添加。

如果您的 char 数组打算用作字符串,则应在其末尾添加 \0。

编辑:以 \0 结尾的是字符串文字,而不是 char 数组。
问题表述不当。

于 2013-10-12T08:05:38.723 回答
1

数组本身不必0\终止,它是以特定方式使用字符数组需要\0终止它们。作用于字符数组的标准库函数将使用\0来检测数组的结尾并因此将其视为字符串,这种行为意味着这些函数的用户将需要遵循\0终止前提条件。如果您的字符数组使用不使用任何此类功能,则不需要\0终止符。

于 2013-10-12T08:05:45.563 回答
0

一个例子说明如果在整数数组中使用 \0 将如何混淆:-

int marks[]={20,22,23,0,93,'\0'};
                      ^  

所以现在你的数组将假设 0(marked) 是数组的结尾,这是不正确的。

\0通常用于终止字符串。in string\0被视为字符串的结尾。

在您的示例中,您不需要用 '\0' 终止它

发现了一个非常有趣的wiki帖子:-

在开发 C(及其派生语言)时,内存非常有限,因此仅使用一个字节的开销来存储字符串的长度是很有吸引力的。当时唯一流行的替代方法,通常称为“Pascal 字符串”(尽管早期版本的 BASIC 也使用),使用前导字节来存储字符串的长度。这允许字符串包含 NUL 并使得查找长度只需要一次内存访问(O(1)(常数)时间)。然而,C 设计者 Dennis Ritchie 选择遵循已经在 BCPL 中建立的 NUL 终止约定,以避免将计数保存在 8 位或 9 位槽中而导致的字符串长度限制,部分原因是维护根据我们的经验,计数似乎不如使用终结器方便。

另请查看相关帖子:- nul terminating a int array

于 2013-10-12T08:06:08.110 回答
-3

我们有一个约定:'0'带有数字代码的特殊字符0,标记字符串的结尾。

但是如果你想标记int数组结束,你怎么知道这0是一个有效的数组成员还是数组结束标记?所以,一般来说,不可能有这样的标记。

换句话说:

字符'\0'(但不是 character '0', code 48)在文本字符串的上下文中没有任何意义(按照惯例,它是一个特殊字符,标记结束),因此它可以用作数组结束标记:

整数0\0(相同)是有效整数。它可以有 sense,这就是为什么它不能用作数组结束标记的原因:

int votesInThisThread[] = { 0, -1, 5, 0, 2, 0 }; // Zeroes here is a valid numbers of upvotes

如果您尝试通过搜索来检测此示例数组的结尾0,您将得到大小为 0。

那是什么问题?

于 2013-10-12T08:09:55.300 回答