在 C 和 C++ 中初始化数组的常用方法是:
int a[3] = { 0, 1, 2 };
另外:您可以选择省略数组绑定并从初始化器列表中推导出它,或者具有比初始化器更大的边界:
int aa[] = { 0, 1, 2 }; // 另一个由三个整数组成的数组
int aaa[5] = { 0, 1, 2 }; // 相当于 { 0, 1, 2, 0, 0}
对于字符数组,有一个特殊规则允许从字符串文字初始化数组,数组的每个元素都从字符串文字中的相应字符初始化。
您的第一个示例使用字符串文字"D:",因此数组的每个元素都将被初始化为该字符串中的一个字符,相当于:
char disk[3] = { 'D', ':', '\0' };
(第三个字符是null terminator,它隐含在所有字符串文字中)。
另外:在这里,您也可以选择省略数组绑定并从字符串文字推导出它,或者具有比字符串长度更大的边界:
char dd[] = "D:"; // 另一个三个字符的数组
字符 ddd[5] = "D:"; // 等价于 { 'D', ':', '\0', '\0', '\0'}
就像上面的 aaa 示例一样,ddd 中在字符串中没有对应字符的额外元素将被初始化为零。
您的第二个示例有效,因为字符串文字 "D:" 将由编译器输出并作为三个字符的数组存储在可执行文件中的某个位置。当可执行文件运行时,包含数组(和其他常量)的段将被映射到进程的地址空间。因此,您的 char* 指针随后被初始化为指向该数组的位置,无论它发生在哪里。从概念上讲,它类似于:
const char __some_array_created_by_the_compiler[3] = "D:";
const char* disk = __some_array_created_by_the_compiler;
由于历史原因(主要是 const 在 C 的早期不存在),使用非常量 char* 指向该数组是合法的,即使该数组实际上是只读的,因此 C 和第一个 C++ 标准允许您使用非 const char* 指针指向字符串文字,即使它引用的数组实际上是 const:
const char __some_array_created_by_the_compiler[3] = "D:";
char* disk = (char*)__some_array_created_by_the_compiler;
这意味着尽管看起来你的两个示例并不完全相同,因为这仅允许用于第一个:
disk[0] = 'C';
对于第一个没问题的例子,它改变了数组的第一个元素。
对于第二个示例,它可能会编译,但会生成undefined behaviour,因为它实际上正在修改__some_array_created_by_the_compiler 的第一个元素,它是只读的。在实践中可能会发生的情况是进程会崩溃,因为尝试写入内存的只读页面会引发分段错误。
重要的是要了解 C++ 中有很多东西(C 中甚至更多)编译器会很高兴地编译,但会导致在执行代码时发生非常糟糕的事情。