【问题标题】:Is it necessary to supply the null character when declaring an character's array?声明字符数组时是否需要提供空字符?
【发布时间】:2014-06-10 18:59:32
【问题描述】:

C 中的字符串常量存储为字符数组,在逐个元素创建这样的数组时,是否需要提供空字符。

我需要存储一个字符串常量,比如S[number]= "hello\n"。字符串常量在 C 中存储为字符数组,此外,这样的字符串以空字符 '\0' 结尾。将短语存储在数组中时,是否需要考虑空字符并分配额外的空间,还是只需要提及需要存储的字符数?

【问题讨论】:

  • 这取决于声明中数字的值。它可以添加(在大多数情况下)或不添加(在一个有效的声明中)。

标签: c arrays string


【解决方案1】:

在将短语存储在数组中时,是否需要考虑空字符并分配额外的空间,还是只需要提及需要存储的字符数?

是的,您应该计算空字符来分配额外的空间。您必须注意这里的重点:

S[number]= "hello\n";

如果number 值等于(或大于)字符串"hello\n" 的长度,则将\0 附加到S[] 数组中@ 为\0 字符(或者如果您根本不提供大小)如s[] = "hello\n";)。

来自:ISO/IEC 9899:201x 委员会草案,2011 年 4 月 12 日 N1570:

6.7.9 [Initialization]

[...]
14:字符类型的数组可以由字符串文字或 UTF-8 字符串文字初始化,可选用大括号括起来。字符串文字的连续字节(包括终止空字符如果有空间或如果数组大小未知)初始化数组的元素。
[..]
示例 8 声明

char s[] = "abc", t[3] = "abc";

定义“普通”字符数组对象 s 和 t,其元素用字符串字面量初始化。此声明等同于:

  char s[] = { 'a', 'b', 'c', '\0' },
       t[] = { 'a', 'b', 'c' }; // you are missing this point

数组的内容是可以修改的。另一方面,声明

 char *p = "abc";

定义p 类型为pointer to char'' and initializes it to point to an object with typearray of char'',长度为4,其元素用字符串字面量初始化。如果尝试使用 p 修改数组的内容,则行为未定义。

正如我对您的问题的评论:这取决于您在声明中给出的 number 的值。它可能添加'\0' char(在大多数情况下)或可能不添加(在一个有效的声明中)。这是非常重要的一点,大多数答案都丢失了。

更珍贵:

注意"hello"是六字符长字符串(包括字符串终止nul char '\0'

S[6]= "hello";

等同于:

S[6]= {'h', 'e', 'l', 'l', 'o', '\0'};
// ^ is 6

但是

S[5]= "hello";

在 C 中有效,但不要附加 nul \0 char。相当于:

S[5]= {'h', 'e', 'l', 'l', 'o'};
// ^  is 5 

现在要注意这一点非常重要,在此声明中,如果在声明中给出了 5 = 5,那么您可以使用循环将字符打印为 for(i = 0; i < sizeof(S); i++),但您不能使用 %s 或类似 strcpy 的函数在S[] 上——使用这个调用未定义的行为。

除此之外,我还建议您在编译代码时始终使用 -Wall 和 -pedantic 标志。现在看看它是如何工作的:

让我们编译这段代码,使用char s[4] = "hello";

int main(){
  char s[4] = "hello";
  int i = 0;
  for (i = 0; i < sizeof s; i++) 
   printf("%c", s[i]);
  printf("\n");
  return 0;
}

您将收到如下警告:

$ gcc -Wall -pedantic x.c
x.c: In function ‘main’:
x.c:3:15: warning: initializer-string for array of chars is too long 
[enabled by default]

但是代码在 C 中是有效的,char s[5] = "hello"; 编译相同的代码它不会给你任何警告,这也意味着它是一个有效的代码,但显然它没有添加 \0

#include <stdio.h>
int main(){
  char s[5] = "hello";
  int i = 0;
  for (i = 0; i < sizeof s; i++) 
   printf("%c", s[i]);
  printf("\n");
  return 0;
}

检查这个(你应该注意到这次编译器不会发出任何警告):

$ gcc -Wall -pedantic x.c
@:~$ ./a.out 
hello

但这一次我们应该使用printf("%s", s);,它会调用未定义的行为。

因此,当您使用字符串字面量创建数组时,最好避免在声明中使用大小,例如:

  char s[] = "hello";

但这与char s[6] = "hello"; 相同,并且您不能将新字符附加到s[]strcat(s, " world!")

如果您打算在代码中修改s[],那么您可能希望创建一个足够大的字符串:

 char s[100] = "hello";

现在strcat(s, " world!") 是完全有效的代码。

【讨论】:

  • 先生,假设我已经分配了一个 char 数组,如下 s[1000],我现在希望在其中存储“hello\n”,所以,为了让程序将它作为字符串 hello 然后换行,我必须进行以下分配 s[0]="h",s[1]="e",s[2]="l",s[3]="l", s[4]="o",s[5]="\n",s[6]="\0" ?如果我没有完成最后一项任务,我会面临什么问题?
  • @Mr.Sherlock 是的,如果你声明char s[1000];,那么s[] 的内容就是垃圾,那时你必须将'\0' 显式存储为s[6] = '\0'(注意使用单个@ 987654366@,不是双倍的" 用于字符)。您也可以将 strcpy 函数用作strcpy(s, "hello\n"); 现在您可能想阅读这个答案What does sizeof(&array) return? 我试图解释字符串
  • @Mr.Sherlock 注意"1" 是一个字符串,'1' 是一个字符,1 是一个单整数,1u1U 是一个无符号整数。一切都不一样。
  • 正确指出,我的错。谢谢你,先生。只是我之前忘记问的另一个疑问。 char S[6]="hello\n" 是一个有效的字符串赋值吗?
  • @Mr.Sherlock 是的 char S[6]="hello\n" 在 C 中有效(但在 C++ 中无效)。需要注意的重要一点,正如我在回答中解释的那样,如果您将 S[] 减速为 char S[6]="hello\n" 然后 '\0' 终止 null char is not 附加在 S[] 中,此声明与 S[6]= {'h', 'e', 'l', 'l', 'o', '\n'}; 相同(没有空终止),并且您不能在 printf()%s 或 string.h 函数(如 strlen())中使用它,因为这些除外以空终止的字符串。我建议您再次阅读我的答案。
【解决方案2】:

如果你打算使用 C 字符串函数,比如 strlen,那么答案是 YES。您的字符串应该以空字符结尾。如果您引入自定义函数来处理字符串 - 您可以随意存储它。

值得一提的是,如果您使用字符串常量创建数组,它会自动为空字符保留空间。例如。以下代码的输出:

char s[] = "hello";
printf("%d", sizeof(s) / sizeof(char));

6

对于 'h、'e'、'l'、'l'、'o' 是 5,对于 '\0' 是 1。

【讨论】:

  • 顺便说一句,sizeof(char) 是多余的。
  • @PeterSchneider 为什么它是冗余的?如果您暗示我们知道 sizeof(char) apriory - 一般而言,依靠此类假设并不是一个好习惯。
  • @unwind 这不是重点!您可以在生产代码中省略它,但如果它是一个示例,我的 sn-p 肯定是,我最好这样写来强调,sizeof 返回字节数而不是元素数。
  • @DenisKulagin 当然,数组中元素的数量是惯用的。不过,我认为在这种情况下最好写成sizeof s / sizeof *s,这样可以防止您重复该类型,并且(可能)在数组类型发生变化时使用错误的类型。
  • 更重要的是,这会导致未定义的行为,因为 size_t 不匹配 %d ...要么将值转换为 (int),要么在 C99 或更高版本中使用 %zu
【解决方案3】:

不,但你应该忽略number

char S[]= "hello\n";

将有尾随的 0 字符和根据需要调整大小的数组。

number 太小,您可能会不小心切断 0 字符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-25
    • 1970-01-01
    • 2022-11-14
    • 2012-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多