我能直接将指向char的指针分配给字符串字面值吗?

4
我想知道是否可以直接将指向char的指针分配给字符串常量,而不必先将字符串常量分配给左值指针。通常,如果参数需要某物的指针,您可以使用&传递对象的地址,而字符串常量似乎被(或至少衰减为)char指针,因为它们可以分配给char指针。这就是我期望以下之一能够工作的原因:
const char ** ppchar = &"StringLiteral"; // This doesn't compile

const char ** ppchar = (const char **)&"StringLiteral"; // This compiles, but...

std::cout << *ppchar;   // Throws an exception

我希望至少这两个中的第二个能够工作,因为 "StringLiteral" 至少对我来说似乎会衰减成一个指针,所以在下面的代码中:

const char** ppchar = (const char**) &"StringLiteral";

第一个指针将指向字符串字面量的地址。如果先将字符串字面量分配给左值指针,第一个指针将指向第二个指针,后者实际上保存了它的地址。对我来说是相同的。
编辑:另外,似乎我可以这样做:
const char * _pptr = (const char*)&"StringLiteral";
std::cout << _pptr; // Prints "Hello"

所以似乎没有规则禁止直接赋值给字符串字面量。

编辑:正如许多人显而易见的那样,我的间接层级是错误的。这在Vlad from Moscow的答案中得到了解释。在:

const char ** pptr = (const char**)&"StringLiteral";

编译器期望pptr指向存储char地址的内容,但我将其赋值为“StringLiteral”本身的地址。这意味着当我使用cout输出时,它会解引用第一个指针以查找哪个地址包含char。问题在于它直接指向“StringLiteral”的地址,这意味着它使用“StringLiteral”的第一个字母的值作为要查找char的地址,基本上它将使用“S”作为要去的地址。真恨自己这么蠢。

1
我正在学习Vulkan API,它有一些像(int argc, char** args)这样的东西,我尝试直接分配一个字符串字面量,我以为没问题,显然我完全错了。 - Zebrafish
1
@来自莫斯科的Vlad 不是这样的,一开始我也以为它是一个rvalue,但字符串字面值不是rvalue,对吧?它在内存中有一个分配的位置。 - Zebrafish
3
字符串字面值具有常量字符数组的类型。 - Vlad from Moscow
2
字符串字面量的类型是 const char[N],其中 N 是某个数字。因此,你需要将 char const (*)[N] 强制转换为 char const**。你不能通过指针来别名数组。我已经在 Stack Overflow 上说过无数次了,数组不是指针,指针也不是数组。 - StoryTeller - Unslander Monica
1
@Zebrafish 没什么好想的。你在问题中引用的是 const char ** 类型的指针,而你正在使用的是 const char * 类型的指针。 - Vlad from Moscow
显示剩余16条评论
2个回答

5
这个表达式
&"StringLiteral"

在C++中,该类型为const char(*)[14],在C中则为char(*)[14],因为字符串字面值分别具有const char [14]char [14]类型。
因此,您不能将这样的指针赋给const char **char **类型的对象,因为它们的指针不兼容。
如果您尝试在声明中使用转换,那么...
const char** ppchar = (const char**) &"StringLiteral";

然而,像*ppchar这样解引用指针是没有意义的,因为你得到的不是类型为char *的对象,而是对应于字符串字面值第一个字符值的值,而不是地址值。

例如,您可以首先将字符串字面值转换为类型const char *(或在C中为char *),方法如下:

StringLiteral + 0

因为在这种表达式中,字符串字面值被转换为指向其第一个字符的指针。但是您不能对此表达式应用一元运算符&,例如:

&( StringLiteral + 0 )

获取类型为const char **的对象,因为您可能无法将运算符应用于rvalue

所以您可以执行以下操作:

const char *pchar = "StringLiteral";
const char **ppchar = &pchar;

我理解你的想法。正如某人所说,我想我弄错了间接级别。就像你所说的那样,它指向第一个字符。不过,我在想,当我执行cout << ppchar; 或 cout << *ppchar;时,它应该打印单词或第一个字母,但它抛出异常。 - Zebrafish
@Zebrafish 编译器将表达式 *ppchar 中存储的值视为地址值。但实际上,它获取了字符串字面量的第一个字符,并将其解释为地址,尝试使用该字符作为地址访问内存。 - Vlad from Moscow
哦,那就有道理了。 - Zebrafish
你将获得与字符串字面值第一个字符的值相对应的值 - 实际上这是严格别名违规,因此任何事情都可能发生。 - M.M

1

除了@Vlad from Moscow的出色回答外。

我可以直接将指向char指针的指针分配给字符串字面值吗?

几乎可以。在C中,代码可以使用复合字面量来形成一个const char *,它用字符串字面值初始化,然后取该指针的地址分配给ppchar

const char **ppchar = &((const char *){"StringLiteral"});
puts(*ppchar);

1
这几乎就像我尝试做的事情,即 const char ** ppchar = &((const char*)"StringLiteral"); 但我认为强制转换使其成为 rvalue,所以我猜我不能取其地址。 - Zebrafish
“(const char *){"StringLiteral"}”不是一个临时变量吗?这样会使“ppchar”成为一个临时变量的地址,因此在没有UB的情况下无法使用,对吗? - Daniel H
"StringLiteral" 是一个字符串字面量。在 C 中,(const char *){"StringLiteral"} 是一个复合字面量。在 C++ 中不是这样,因为 C++ 没有复合字面量的概念;在那里,它是将 "StringLiteral" 转换为临时 const char * 的强制转换。问题被标记为两者都适用,但使用了 C++ 构造。请参见此处,了解 Clang 在 C++ 模式下对该结构的处理方式。 - Daniel H
@DanielH 哦,是一个复合字面量而不是一个字符串字面量。 - chux - Reinstate Monica
1
在C语言中没有强制类型转换;这是复合字面量的语法,与强制类型转换表达式有一些相似之处。但它不是强制类型转换表达式,因为由“{ }”括起来的内容并不构成表达式。由于同样的原因,在C++中该代码无效。 - M.M

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