为什么可以将char char *分配给char *?

时间:2012-04-22 14:17:38

标签: c++ string char literals

我知道例如"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"这样的文字字符串,它会在我的所有程序中占用内存,或者它就像临时变量会在语句结束时被销毁?

5 个答案:

答案 0 :(得分:24)

事实上,"hello"的类型为char const[6]

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

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

也就是说,大多数编译器都可以配置为警告关于不推荐使用的代码,或者甚至默认情况下这样做。此外,C ++ 11完全不允许这样做,但编译器可能还没有强制执行它。


Standerdese粉丝:
[参考1] C ++ 03标准:§4.2/ 2

  

不是宽字符串文字的字符串文字(2.13.4)可以转换为“指向字符的指针”的右值;可以将宽字符串文字转换为“指向wchar_t的指针”类型的右值。在任何一种情况下,结果都是指向数组第一个元素的指针。仅当存在明确的适当指针目标类型时才考虑此转换,而不是在通常需要从左值转换为右值时。 [注意:此转化已弃用。见附录D.]为了在重载决策(13.3.3.1.1)中进行排序,这种转换被认为是一个数组到指针的转换,然后是一个限定转换(4.4)。 [示例:“abc”转换为“指向const char的指针”作为数组到指针的转换,然后转换为“指向char的指针”作为限定转换。 ]

C ++ 11简单地删除了上述引用,这意味着它在C ++ 11中是非法代码。

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

  

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

     

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

答案 1 :(得分:2)

  

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

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

const char*强制转换为char*的唯一原因是与c的同步性,如winapi系统调用。而这种演员阵容与其他任何常规演员不同,都是无法解释的。

答案 2 :(得分:1)

只需使用string

std::string s("hello");

这将是C ++的方式。如果你真的必须使用char,你需要创建一个数组并将内容复制到其上。

答案 3 :(得分:1)

第二个问题的答案是变量s作为类型指针指向char存储在RAM中。如果它是全局的或静态的,它将在堆上分配并在运行程序的生命周期内保留。如果它是一个本地(“自动”)变量,它将在堆栈上分配并保持不变直到当前函数返回。在任何一种情况下,它都占用了保持指针所需的内存量。

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

请注意,因为字符串是常量而s是指针,所以不需要复制。指针s只指向存储字符串的位置。

答案 4 :(得分:-3)

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

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