为什么 C++ 变量是指针时不需要正确定义?
Why C++ variable doesn't need defining properly when it's a pointer?
我是 C++ 语言的新手(尤其是指针,经验主要集中在 PHP),希望对以下内容有一些解释(我已经尝试寻找答案)。
两行代码如何能够在我的程序中完成完全相同的工作?第二行似乎违背了我到目前为止所学和理解的关于指针的一切。
char disk[3] = "D:";
char* disk = "D:";
我如何能够初始化指向内存地址以外的任何内容的指针?不仅如此,在第二行我也没有正确声明数组 - 但它仍然有效?
char disk[3] = "D:";
被视为
char disk[3] = {'D',':','[=11=]'};
在 C++11 及更高版本中
char* disk = "D:";
是一个错误,因为字符串文字的类型是 const char[]
,不能分配给 char *
。不过,您可以将其分配给 const char *
。
字符串文字实际上是只读的、以零结尾的字符数组,使用字符串文字会为您提供指向数组中第一个字符的指针。
所以在第二个例子中
char* disk = "D:";
您将 disk
初始化为指向三个字符数组的第一个字符。
请注意,在我上面的第一段中,我说过字符串文字是 只读 数组,这意味着有一个普通的 char*
指向这个数组可以使你认为修改这个数组是可以的,但它不是(试图修改字符串文字会导致 未定义的行为 )。这就是通常使用 const char*
的原因:
const char* disk = "D:";
从 C++11 开始,不使用 const char*
实际上是一个错误,但大多数编译器仍然只对此发出警告,而不是产生错误。
在 C 和 C++ 中初始化数组的常用方法是:
int a[3] = { 0, 1, 2 };
Aside: And you can optionally leave out the array bound and have it deduced from the initializer list, or have a larger bound than there are initializers:
int aa[] = { 0, 1, 2 }; // another array of three ints
int aaa[5] = { 0, 1, 2 }; // equivalent to { 0, 1, 2, 0, 0}
对于字符数组,有一个特殊规则允许从字符串文字初始化数组,数组的每个元素都从字符串文字中的相应字符初始化。
您的第一个示例使用字符串文字 "D:"
因此数组的每个元素都将初始化为该字符串中的一个字符,相当于:
char disk[3] = { 'D', ':', '[=11=]' };
(第三个字符是 null terminator,它隐式存在于所有字符串文字中)。
Aside: Here too you can optionally leave out the array bound and have it deduced from the string literal, or have a larger bound than the string length:
char dd[] = "D:"; // another array of three chars
char ddd[5] = "D:"; // equivalent to { 'D', ':', '[=25=]', '[=25=]', '[=25=]'}
Just like the aaa
example above, the extra elements in ddd
that don't have a corresponding character in the string will be zero-initialized.
您的第二个示例有效,因为字符串文字 "D:"
将由编译器输出并作为三个字符的数组存储在可执行文件的某处。当可执行文件是 运行 时,包含数组(和其他常量)的段将被映射到进程的地址 space。因此,您的 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++ 标准允许您使用非常量 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 中甚至更多)编译器会很乐意编译,但在执行代码时会导致非常糟糕的事情发生。
你说指针只能存储内存地址是完全正确的。那么第二个陈述如何有效?让我解释一下。
当您将一系列字符放在双引号中时,屏幕后面发生的事情是该字符串存储在只读计算机内存中,并返回存储该字符串的位置的地址。所以在 运行 时间,表达式被求值,字符串求值到内存地址,这是一个字符指针。正是这个指针被分配给你的指针变量。
那么这两种说法有什么区别呢?第二种情况的字符串是常量,而第一种语句声明的字符串是可以改变的。
我是 C++ 语言的新手(尤其是指针,经验主要集中在 PHP),希望对以下内容有一些解释(我已经尝试寻找答案)。
两行代码如何能够在我的程序中完成完全相同的工作?第二行似乎违背了我到目前为止所学和理解的关于指针的一切。
char disk[3] = "D:";
char* disk = "D:";
我如何能够初始化指向内存地址以外的任何内容的指针?不仅如此,在第二行我也没有正确声明数组 - 但它仍然有效?
char disk[3] = "D:";
被视为
char disk[3] = {'D',':','[=11=]'};
在 C++11 及更高版本中
char* disk = "D:";
是一个错误,因为字符串文字的类型是 const char[]
,不能分配给 char *
。不过,您可以将其分配给 const char *
。
字符串文字实际上是只读的、以零结尾的字符数组,使用字符串文字会为您提供指向数组中第一个字符的指针。
所以在第二个例子中
char* disk = "D:";
您将 disk
初始化为指向三个字符数组的第一个字符。
请注意,在我上面的第一段中,我说过字符串文字是 只读 数组,这意味着有一个普通的 char*
指向这个数组可以使你认为修改这个数组是可以的,但它不是(试图修改字符串文字会导致 未定义的行为 )。这就是通常使用 const char*
的原因:
const char* disk = "D:";
从 C++11 开始,不使用 const char*
实际上是一个错误,但大多数编译器仍然只对此发出警告,而不是产生错误。
在 C 和 C++ 中初始化数组的常用方法是:
int a[3] = { 0, 1, 2 };
Aside: And you can optionally leave out the array bound and have it deduced from the initializer list, or have a larger bound than there are initializers:int aa[] = { 0, 1, 2 }; // another array of three ints int aaa[5] = { 0, 1, 2 }; // equivalent to { 0, 1, 2, 0, 0}
对于字符数组,有一个特殊规则允许从字符串文字初始化数组,数组的每个元素都从字符串文字中的相应字符初始化。
您的第一个示例使用字符串文字 "D:"
因此数组的每个元素都将初始化为该字符串中的一个字符,相当于:
char disk[3] = { 'D', ':', '[=11=]' };
(第三个字符是 null terminator,它隐式存在于所有字符串文字中)。
Aside: Here too you can optionally leave out the array bound and have it deduced from the string literal, or have a larger bound than the string length:char dd[] = "D:"; // another array of three chars char ddd[5] = "D:"; // equivalent to { 'D', ':', '[=25=]', '[=25=]', '[=25=]'}Just like theaaa
example above, the extra elements inddd
that don't have a corresponding character in the string will be zero-initialized.
您的第二个示例有效,因为字符串文字 "D:"
将由编译器输出并作为三个字符的数组存储在可执行文件的某处。当可执行文件是 运行 时,包含数组(和其他常量)的段将被映射到进程的地址 space。因此,您的 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++ 标准允许您使用非常量 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 中甚至更多)编译器会很乐意编译,但在执行代码时会导致非常糟糕的事情发生。
你说指针只能存储内存地址是完全正确的。那么第二个陈述如何有效?让我解释一下。
当您将一系列字符放在双引号中时,屏幕后面发生的事情是该字符串存储在只读计算机内存中,并返回存储该字符串的位置的地址。所以在 运行 时间,表达式被求值,字符串求值到内存地址,这是一个字符指针。正是这个指针被分配给你的指针变量。
那么这两种说法有什么区别呢?第二种情况的字符串是常量,而第一种语句声明的字符串是可以改变的。