【问题标题】:Character array initialization with the first element being null第一个元素为空的字符数组初始化
【发布时间】:2020-08-26 08:09:40
【问题描述】:

我最近遇到了一行代码和四个选项:

char fullName[30] = {NULL};
  1. A) 第一个元素被分配一个 NULL 字符。

  2. B) 数组的每个元素都被赋值为 0(零)

  3. C) 数组的每个元素都被赋值为 NULL

  4. D) 数组为空。

我们选择的答案是选项 C,因为虽然数组仅使用单个 NULL 进行初始化,但 C++ 使用 NULL 填充数组的其余部分。

但是,我们的教授不同意,他说答案是 A,他说:

所以第一个元素是NULL,当您显示它时,它会显示第一个元素,即NULL

引用完整地显示了问题;没有提供其他信息。我很好奇哪一个是正确的,如果有人能解释为什么所说的答案是正确的。

【问题讨论】:

  • 选项AB 并不相互排斥。形式上,它们都是正确的。
  • 我认为这可能取决于您使用的编译器。我确实记得在 90 年代初期使用过这种初始化,我很确定它只是将第一个字符设置为 \0。在这些日子里,NULL 被定义为 '\0',或 (void)0,或其他取决于编译器的东西。
  • @MPW 你会想错的。没有元素被“未分配”。
  • @MPW 所有元素都已初始化。

标签: c++ arrays null initialization


【解决方案1】:

问题定义不明确,但选项 B 似乎是最正确的答案。

结果取决于NULL 是如何定义的,depends on the compiler(更准确地说,是在标准库实现上)。如果定义为nullptr,代码将无法编译。 (我认为没有任何主要的实现可以做到这一点,但仍然如此。)

假设NULL 未定义为nullptr,那么它必须定义为具有值0 的整数文字(即0,或0L,或类似的东西),这使得您的代码相当于char fullName[30] = {0};

这会用零填充数组,因此选项 B 是正确答案。

一般来说,当你用大括号括起来的列表初始化一个数组时,每个元素都会被初始化。如果您提供的初始值设定项少于元素的数量,则其余元素将归零。

关于其余选项:

  • 选项C不清楚,因为如果代码编译通过,那么NULL等价于0,所以可以认为选项C等价于选项B。

  • 选项 A 是否有效取决于您的解释方式。如果这意味着其余元素未初始化,那就错了。如果它没有指定剩余元素会发生什么,那么它是一个有效的答案。

  • 选项 D 完全错误,因为数组不能为“空”。

【讨论】:

  • @KonradRudolph 我想作者所说的“NULL 字符”是指“NUL 字符”,'\0' 被解释为 ASCII。
  • @Ruslan 是的,但教授显然不知道,同样清楚地混淆了 NUL 和 NULL。
  • @HolyBlackCat 当然,但是这个术语是明确的不正确,而将包含空字符串的数组称为“空”在最坏的情况下是不精确的。无论如何,这对我来说都是毫无意义的迂腐。我们可以同意这位教授的说法很离谱。
  • 轻微迂腐:NULL 不是 定义而不是 编译器 定义吗?我们经常使用“由编译器定义”作为“由实现定义”的转喻,但如果我们想要准确地说“取决于标准库实现”可能更正确?
  • @mtraceur -- (void*)0 在 C++ 中不是有效的空指针常量。从来没有。与 C 不同,没有从 void* 到其他指针类型的隐式转换。所以int *ip = (void*)0; 是一个错误。
【解决方案2】:
char fullName[30] = {NULL};

这是不应该写的东西。

NULL 是一个扩展为空指针常量的宏。一个字符——不是指针——在这里被初始化,所以使用NULL是没有意义的。

碰巧一些空指针常量也是值为 0 的整数字面量(例如 0 或 0L),如果 NULL 扩展为这样的字面量,那么显示的程序在技术上是结构良好的,尽管滥用的NULL。宏扩展的具体内容由语言实现定义。

如果 NULL 而不是扩展为一个不是整数文字的空指针常量,例如 nullptr - 这是完全可能的 - 那么程序是错误的。

NULL 根本不应该用 C++ 编写,即使是初始化指针也是如此。它的存在是为了向后兼容 C,以便更容易地将 C 程序移植到 C++。


现在,让我们假设 NULL 在这个特定的 C++ 实现中恰好扩展为整数字面量。

示例中没有任何内容是分配的。赋值是对预先存在的对象进行的操作。在这里,数组正在初始化。

数组的第一个元素用零字面量初始化。其余元素是值初始化的。两者都导致空字符。因此,整个数组将被空字符填充。

一个简单而正确的写法是:

char fullName[30] = {};

B 和 C 同样接近正确,除了关于“分配”的措辞。他们没有提到值初始化,但至少结果是一样的。 A 也没有错,尽管它并不完整,因为它没有描述其余元素是如何初始化的。

如果“空”被解释为“不包含元素”,则 D 不正确,因为数组包含 30 个元素。如果它被解释为“包含空字符串”,那么 D 将是一个正确的答案。

【讨论】:

  • 我什至会接受答案 D 是正确的:“空”不是一个精确定义的术语,但是对于该术语的合理定义,数组确实是空的,因为它在语义上表示一个空C 风格的字符串。明确错误的唯一答案是A。
  • @KonradRudolph 就我个人而言,我将“空”理解为“不包含任何元素”。因此,具有 30 个元素的数组不会为空。我想那时可能还有其他解释。
  • 我的意思是,您的解释显然不是不合理的,并且可能是故意的。我的主要观点是,具有讽刺意味的是,教授的首选答案是最不正确的
  • @KonradRudolph 对问题的措辞进行了非常微小且可能微妙的更改,但 D 将是明确正确的:该数组确实包含空的以 null 结尾的字符串。
  • +1。关于“空”,对于固定大小的数组,我理解它的意思是“包含未定义的值”。对于动态数组,它指的是长度为零,但这不是动态数组。您可以在野外看到这种用法的其他示例,例如numpy.empty(...).
【解决方案3】:

你几乎是正确的。

教授不正确。确实,显示在第一个 NULL 处结束(当使用某些方法时),但这并没有说明数组其余部分的值,无论如何都可以简单地检查。

[dcl.init/17.5]:: [..] 对于每个 1 ≤ i ≤ k,第 i 个数组元素使用 xi 进行复制初始化,并且对于每个 k [..]

但是,没有一个选项是严格正确且措辞得当的。

发生的情况是 NULL 用于初始化第一个元素,而其他元素为零初始化。最终结果实际上是选项 B。

如果 NULL 在您的平台 (which it isn't, but it is permitted to be) 上被定义为 std::nullptr_t 类型的表达式,the example won't even compile!

NULL 是一个指针,而不是一个数字。 从历史上看,在某种程度上混合和匹配这两个东西是可能的,但近年来 C++ 试图加强这一点,你应该避免模​​糊界限。

更好的方法是:

char fullName[30] = {};

最好的方法是:

std::string fullName;

【讨论】:

  • 这段代码甚至不能保证编译。如果NULL 定义为nullptr,它可能根本无法编译
  • @Ayxan(或任何其他std::nullptr_t
  • “从观察中推理,这不是科学有效的方法。”礼貌地说,在认识论上是有争议的。从观察中推理一般而言是完全有效的甚至是必需的,只要观察到一些注意事项。
  • @KonradRudolph 我忘记了一个更好的术语。一会儿去找
  • @KonradRudolph Meh,也许这不是谬论。稍微改变了我的措辞。
【解决方案4】:

显然,你的教授是对的,让我们看看如何

char someName[6] = "SAAD";

字符串名称在内存中的表示方式:

0 1 2 3 4 5

S A A D

基于数组的 C 字符串

组成字符串的各个字符存储在数组的元素中。字符串以空字符终止。空字符后的数组元素不是字符串的一部分,它们的内容无关。

“空字符串”是第一个字符为空字符的字符串:

0 1 2 3 4 5

/0

空 C 字符串

空字符串的长度为0。

【讨论】:

    猜你喜欢
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多