C++中字符串字面量的数据类型是什么?

16

有关C++中字符串字面值的数据类型已经有类似的问题被问及。

许多人引用了标准:

窄字符串字面值的类型为“n个const char的数组”,其中n是如下所定义的字符串的大小,并具有静态存储期(3.7)。

我在主函数中编写了以下语句:

  char cstring[]= "hellohellohellohellohellohello";

但是我在程序集中找不到任何以静态数据形式存储的字符串字面量。实际上,程序集显示该字符串被分解并直接 "存储" 在指令中。

    movl    $1819043176, -48(%rbp)
    movl    $1818585199, -44(%rbp)
    movl    $1701343084, -40(%rbp)
    movl    $1752132716, -36(%rbp)
    movl    $1869376613, -32(%rbp)
    movl    $1819043176, -28(%rbp)
    movl    $1818585199, -24(%rbp)
    movw    $28524, -20(%rbp)
    movb    $0, -18(%rbp)

当在全局作用域中有类似的语句时,其结果是将字符串存储为静态数据。

char cstring1[] = "hellohellohellohellohellohello";

汇编语言
cstring1:
    .string "hellohellohellohellohellohello"

上述示例可以在此处在线查看:这里
因此,这似乎不符合引用标准。也许有一些例外情况没有被引用到?

5
一个对象的“类型”与它在字节码中的表现无关。“cstring”和“cstring1”的类型相同(如果我没数错的话是“char[31]”),但它们的行为不同。 - Barry
尝试使用char const cstring[]= "hellohellohellohellohellohello"; - Richard Critten
11
根据 as-if 规则,编译器可以优化掉某些内容。 - NathanOliver
1
@RichardCritten 这并没有改变任何事情。请使用我的链接在实际环境中尝试一下。 - Gab是好人
有关“as-if规则”的更多信息,请参见https://dev59.com/NGUo5IYBdhLWcg3w3ycF。 - Christian Hackl
3个回答

20

根据“仿佛”规则,它符合标准。

由于字符串字面值仅用于初始化cstring,因此不需要为其创建任何对象表示形式。编译器已经删除了它,而是选择用替代方法初始化cstring,该方法具有等效的结果,但编译器认为在某些方面更好(速度或代码大小)。


1
虽然这是正确的,但我认为也很重要注意到语言确实定义了问题中提到的情况之间的真正区别。 - Ben Voigt

10

表达式具有类型。如果字符串字面值作为表达式使用,则具有类型。 你的没有。

考虑以下代码:

#include <stdio.h>

#define STR "HelloHelloHello"

char global[] = STR;

int main(void)
{
    char local[] = STR;
    puts(STR);
}

这个程序中有三个字符串字面值使用相同的标记形成,但它们不被同等处理。

第一个是 global 的初始化器,它是一个具有静态生命周期的对象的静态初始化部分。根据3.6.2节,静态初始化不必在运行时进行,编译器可以安排结果以预格式化的二进制图像的形式出现,使得进程在数据已经准备好的情况下开始执行,并且在此处已经这样做了。如果在全局动态初始化之前执行,以与 local[] 相同的方式初始化此对象也是合法的。

第二个是 local 的初始化器,它是一个字符串字面值,但它实际上不是一个表达式。它在8.5.2的特殊规则下处理,该规则指定字符串字面值中的字符独立用于初始化数组元素;字符串字面值不会作为一个整体使用。这个对象具有动态初始化,导致在运行时加载该值。

第三个是传递给 puts() 调用的参数,它实际上将字符串字面值用作表达式,并且它的类型将是 const char[N],调用时会衰减为 const char*。如果您真的想研究用于处理字符串字面值的运行时类型的对象代码,您应该在表达式中使用文字,就像这个函数调用所做的那样。


你是说如果在表达式中没有使用到字符串字面量,那它们就没有类型吗?我相信它们是有类型的。这种类型可能与生成的代码无关,但也可能有关。实际上,在生成的代码中通常不存在类型。 - Keith Thompson
是的。字符串常量为什么不是表达式? - Keith Thompson
@Keith:这是语言语法问题。不管怎样,此特定初始化的 RHS 是否为表达式,只需匹配标准中指定处理此情况的其他语法产生式即可。例如,对于聚合初始化,我认为它不是,所以如果 Ben 的说法正确的话,就有道理,而我太懒得检查了,但我没有任何怀疑的理由。 - Steve Jessop
2
@SteveJessop:是的,没错。看看C++11标准第8.5节中initializer的语法。initializer -> brace-or-equal-initializer -> = initializer-clauseinitializer-clause -> assignment-expression。在char cstring1[] = "hellohellohellohellohellohello";中,字符串字面值是一个assignment-expression。我不知道有任何上下文可以出现字符串字面值而不是某种表达式--无论它如何使用。我可以将42;写成语句,并且表达式42没有被使用--但它仍然是具有类型的表达式。 - Keith Thompson
2
显然我在这里漏掉了什么。正如你所说,语法initializer-clause -> assignment-expression已经允许在那个位置使用string-literal。因此,在那种情况下,string-literal是一个assignment-expression。该表达式的值和类型可能与如何使用它有关--但它仍然是一个表达式,并且仍然具有类型const char[N]。为什么字符串字面值由于其上下文而没有类型?在语句42;中,常量42不是类型为int吗? - Keith Thompson
显示剩余12条评论

0

我认为你引用的定义必须被解释为指代存储位置未明确声明的字符串字面值,例如 printf() 中的格式表达式。为了使这样的代码工作,这些字符串字面值必须被存储在 某个地方;如果无法从上下文推断,则该定义指定它们存储的位置。

顺便说一句:你的 main() 中的字符串字面值看起来不像静态数据,因为函数中声明的变量默认是“自动”的。如果你改为编写 static char cstring[]=...,那么你就会在同一个位置看到它,就像 cstring1[] 一样。

还有一件事:存储位置并不是数据类型的一部分!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接