为什么可以将常量字符*分配给字符*

Why is it possible to assign a const char* to a char*?

本文关键字:字符 分配 常量 为什么      更新时间:2023-10-16

我知道例如"hello"const char*型。所以我的问题是:

  1. 我们如何将像 "hello" 这样的文字字符串分配给非const char*

    char* s = "hello";  // "hello" is type of const char* and s is char*
                        // and we know that conversion from const char* to
                        // char* is invalid
    
  2. "hello" 这样的文字字符串会占用我所有程序中的内存,还是就像临时变量一样,在语句结束时会被销毁?

事实上,"hello" 属于 char const[6] 型。

但问题的要点仍然是正确的——为什么C++允许我们将只读内存位置分配给非const类型?

唯一的原因是向后兼容不知道const的旧 C 代码。如果C++在这里严格,它会破坏很多现有的代码。

也就是说,大多数编译器都可以配置为警告已弃用的代码,甚至可以默认这样做。此外,C++11 完全不允许这样做,但编译器可能还不会强制执行它。

<小时 />

对于斯坦德德斯球迷:
[参考文献 1]C++03 标准: §4.2/2

不是宽字符串文本的

字符串文本 (2.13.4) 可以转换为类型为"指向字符的指针"的右值;宽字符串文本可以转换为类型为"指向wchar_t的指针"的右值。在任一情况下,结果都是指向数组第一个元素的指针。仅当存在显式的适当指针目标类型时,才考虑此转换,而不是在一般需要从左值转换为右值时考虑此转换。[注意:此转换已弃用。见附件D。为了在重载分辨率 (13.3.3.1.1) 中进行排名,此转换被视为数组到指针的转换,然后是限定转换 (4.4)。[示例:"abc"作为数组到指针的转换转换为"指向常量字符的指针",然后转换为"指针到字符"作为限定转换。

C++11 只是删除了上述引文,暗示它是 C++11 中的非法代码。

[参考文献 2] C99 标准 6.4.5/5 "字符串文字 - 语义":

在转换阶段 7 中,值为零的字节或代码将追加到由一个或多个字符串文本生成的每个多字节字符序列。然后,使用多字节字符序列初始化静态存储持续时间和长度刚好足以包含该序列的数组。对于字符串文本,数组元素的类型为 char,并使用多字节字符序列的各个字节进行初始化;对于宽字符串文本,数组元素的类型为 wchar_t,并使用宽字符序列进行初始化...

如果这些数组

的元素具有适当的值,则无法指定这些数组是否不同。如果程序尝试修改此类数组,则行为未定义。

是像"hello"这样的文字字符串将在我所有的程序中占用内存,就像一个临时变量,当语句结束时会被破坏。

它保存在程序数据中,因此在程序的生存期内可用。可以从当前范围返回对此数据的指针和引用。

const char*被投射到char*的唯一原因是与c的可协调性,就像winapi系统调用一样。而且这个强制转换是明确的,不像任何其他 const 转换。

只需使用string

std::string s("hello");

这将是C++方法。如果您确实必须使用 char ,则需要创建一个数组并在其上复制内容。

第二个问题的答案是变量s作为类型指针到字符存储在 RAM 中。 如果它是全局的或静态的,则会在堆上分配,并在正在运行的程序的生存期内保持在那里。 如果它是一个局部("auto")变量,它将在堆栈上分配并保留在那里,直到当前函数返回。 在任一情况下,它都会占用保存指针所需的内存量。

字符串"Hello"是一个常量,它与所有其他常量和初始值设定项一起存储为程序本身的一部分。 如果构建程序以在设备上运行,则字符串将存储在 ROM 中。

请注意,由于字符串是常量,并且s是指针,因此无需复制。 指针s仅指向字符串的存储位置。

在您的示例中,您不是在分配,而是在构造。 例如,std::string确实有一个std::string(const char *)构造函数(实际上它更复杂,但没关系)。同样,char *(如果它是一个类型而不是指向类型的指针)可以有一个 const char * 构造函数,它正在复制内存。

实际上不知道编译器在这里是如何工作的,但我认为它可能类似于我上面描述的:"Hello"的副本是在堆栈中构造的,s是用这个副本的地址初始化的。