为啥使用三元运算符进行数组初始化是非法的?
Posted
技术标签:
【中文标题】为啥使用三元运算符进行数组初始化是非法的?【英文标题】:Why is array initialization with ternary operator illegal?为什么使用三元运算符进行数组初始化是非法的? 【发布时间】:2019-06-22 23:17:59 【问题描述】:C
让我可以经常互换使用char
指针和数组,以至于我经常认为它们是完全可以互换的。但是下面的代码证明这不是真的。谁能解释一下为什么在下面的代码中使用三元运算符初始化const char d[]
是非法的?
/* main.c */
#include <stdio.h>
int main()
const char* a = "lorem";
const char b[] = "ipsum";
int* p;
const char* c = ( *p ? "dolor" : "sit" );
const char d[] = ( *p ? "amet" : "consectetur" ); // Why am I an error?
return 0;
编译:
> gcc -g main.c
main.c: In function \u2018main\u2019:
main.c:10:20: error: invalid initializer
const char d[] = ( *p ? "amet" : "consectetur" ); // Why am I an error?
相关问题:如果我的术语在这里不准确:描述const char d[]
的正确术语是什么?它是一个数组吗?变长数组?还有什么?它不被视为指针 - 是吗?
编辑:我相信Array initialization with a ternary operator?没有回答这个问题
RE:引用的问题,我相信前提略有不同。例如。接受的答案解释说 1, 2 ;
(或 'a', 'b' );
)不是有效的C
表达式,我已经知道并接受。但是"amet";
和"consectetur";
是有效的C
表达式。
【问题讨论】:
数组和指针几乎完全不相关。但是,数组可以隐式转换为指针。就是这样。 Array initialization with a ternary operator?的可能重复 @melpomene - 对:数组是一个固定的内存块,可能保存用户初始化的内容,而指针实际上是一个整数,其值恰好是一个内存位置。这是完全正确的,还是我错过了一些微妙之处? @StoneThrow C 不保证指针实际上是整数。 C 有一些“奇怪”的实现,其中指针的行为更像是引擎盖下的结构。但是,是的,差不多就是这样。请记住,每个指针都有一个类型,这会影响例如指针算术。p
未初始化,因此您不知道它指向什么。但是,这与编译代码无关。
【参考方案1】:
6.7.9 初始化
... 14 字符类型的数组可以由字符串字面量或 UTF-8 字符串初始化 字面量,可选地用大括号括起来。字符串文字的连续字节(包括 如果有空间或数组大小未知,则终止空字符)初始化 数组的元素。
C 2011 Online Draft
( *p ? "amet" : "consectetur" )
不是 字符串文字,也不是评估 字符串文字。它计算为 char *
类型的表达式,它本身不是有效的数组初始化器,并且直到运行时才会进行计算。
更不用说,p
是未初始化的,所以表达式一开始是未定义的。
【讨论】:
如果数组d
的条件初始化有效,sizeof(d)
会返回什么?这是否取决于 p
的初始化对象?当然,您根本无法初始化 VLA。
@JohnBode - 所以,如果我理解正确,并且 wrt pre-C11
: char arrays
只能由 "abc"
或 'a', 'b', 'c, '\0'
等字符串文字初始化(前者隐含后者如果我没记错的话),并且在 :
的任一侧具有两个字符串文字的三元运算 不 评估为字符串文字,而是评估为 char*
- 这是准确的吗?
@StoneThrow:正确。在?:
运算符的上下文中,字符串文字被隐式转换为char *
类型的表达式,并且它们计算为每个字符串的第一个字符的地址。请记住,?:
是在运行时计算的,因此结果不能是数组的初始值设定项。【参考方案2】:
字符串文字有点神奇。通常(在表达式中使用时),它们表示数组。数组可以“衰减”(隐式转换)为指针,这就是为什么例如
const char *p = "foo";
有效。 "foo"
是这里的正常表达式。你也可以写
const char *p;
p = "foo"; // assignment, not initialization
但是,如果使用字符串字面量来初始化数组,它的行为类似于初始化字符列表:
char s[] = "foo";
// equivalent to:
char s[] = 'f', 'o', 'o', '\0' ;
在你的例子中
const char d[] = ( *p ? "amet" : "consectetur" );
初始化器不是唯一的字符串文字;它是一种表达方式。因此,两个字符串文字都衰减为指针,然后您会收到错误,因为您无法从指针初始化数组。 (事实上,你根本无法从表达式初始化数组。)
【讨论】:
好的,我想我明白了:你不能用指针初始化一个char
数组 - 我可以用它。当一个字符串文字 - 并且只有一个字符串文字 - 用于初始化一个数组时,它隐含地是一个字符数组。例如。 char c[] = "abc";
隐含为 char c[] = 'a', 'b', 'c', '\0' ;
。但是,当字符串文字位于三元运算符中 :
的任一侧时,它们不再是隐含的 char
数组,而是衰减到它们各自的指针,并且如前所述,数组不能用指针初始化 -这是一个准确的总结吗?
@StoneThrow 基本上是的。它似乎暗示 'a', 'b', 'c', '\0'
是一个字符数组,但它不是(它是特殊的初始化语法)。字符串文字是其他任何地方的字符数组。【参考方案3】:
您只能使用初始化列表来初始化数组,而不能使用表达式。在你的代码中
const char* c = ( *p ? "dolor" : "sit" );
这里c是一个指针变量,字符串常量只代表地址。这就是为什么我们可以使用三元运算符,我们可以将字符串常量的地址分配给指针 c,我们可以初始化数组。但是
const char d[] = ( *p ? "amet" : "consectetur" );
这里 d 是一个数组,但 d 代表数组的地址,字符串常量也只代表地址。因此,我们不能将地址分配给地址,这意味着将字符串常量分配给数组 d。这就是为什么我们会得到错误。
请访问链接以获得更好的理解:
https://www.geeksforgeeks.org/whats-difference-between-char-s-and-char-s-in-c/
Array initialization with a ternary operator?
【讨论】:
我也看到了那个,但接受的答案并不完全符合我的前提。例如。我理解并同意 1, 2
;不是有效的 C 表达式,而是“amet”;和"consectetur";
是。
@StoneThrow 但是“amet”;和“consectetur”;是。 这就是为什么你不能用它们来初始化一个数组。初始化器只能是可赋值表达式或初始化器列表。但是你不能给数组赋值。【参考方案4】:
在您尝试定义 d 之前,您是错误的。在尝试定义 c 时,您会问 *p ?但是 p 没有初始化。我相信这种行为没有定义。
【讨论】:
以上是关于为啥使用三元运算符进行数组初始化是非法的?的主要内容,如果未能解决你的问题,请参考以下文章