我一直认为声明
char *c = "line";
和...相同
char c[] = "line";
于是我就这样做了。
char **choices = { "New Game", "Continue Game", "Exit" };
这使我得到了一个不兼容的指针类型,原因是
char *choices[] = { "New Game", "Continue Game", "Exit" };
不懂。有什么帮助可以理解吗?
我一直认为声明
char *c = "line";
和...相同
char c[] = "line";
于是我就这样做了。
char **choices = { "New Game", "Continue Game", "Exit" };
这使我得到了一个不兼容的指针类型,原因是
char *choices[] = { "New Game", "Continue Game", "Exit" };
不懂。有什么帮助可以理解吗?
char *c = "line";
不是同一个意思
char c[] = "line";
这实际上与之相同
static const char hidden_C0[] = "line";
char *c = (char *)hidden_C0;
除了变量 hidden_C0
无法直接访问以外,其他都一样。但如果您转储生成的汇编语言,您会看到它(通常会有一个不是有效C标识符的名称,例如.LC0
)。在您的字符串常量数组示例中也是如此:
char *choices[] = { "New Game", "Continue Game", "Exit" };
变成
const char hidden_C0[] = "New Game";
const char hidden_C1[] = "Continue Game";
const char hidden_C2[] = "Exit";
char *choices[] = { (char *)hidden_C0, (char *)hidden_C1, (char *)hidden_C2 };
现在,这是仅适用于字符串常量的特殊情况行为。您不能编写
int *numbers = { 1, 2, 3 };
你必须写
int numbers[] = { 1, 2, 3 };
这就是为什么你无法编写
char **choices = { "a", "b", "c" };
你的困惑是C语言中一个常见的误解的特例,即认为数组和指针是“相同的”。实际上不是这样的。数组就是数组。具有数组类型的变量在被使用时(几乎在所有的情况下)会发生类型衰退而成为指针类型,但在定义时不会。
嗯,它们并不相同。对于大多数人来说,把它们看作相同的东西更容易,因此每个人都开始这样想,直到遇到上述问题 :-)
我本来要写一些冗长的东西,但后来我想...其他人肯定已经做过了。他们也确实做到了。这是一个相当好的解释:
http://www.lysator.liu.se/c/c-faq/c-2.html
最简单的思考方式是,当你做类似以下代码时:
char *foo = "something";
你实际上在做以下事情:
char randomblob[] = "something"; char *foo = randomblob;
现在...这并不是一个准确的图片(虽然我不是编译器专家)。但至少让你以稍微更正确的方式思考事情。
所以,回到你的问题,如果我理解正确的话(这绝不是保证),你不能在C中执行你的第三行示例。你是对的,有人可以编写一个编译器,在这里执行正确的操作,但gcc没有。然而,第四个示例则会执行“正确的操作”,并为您提供“一个指向每个指向const char数组本身的指针数组”。
我曾经看过一个网页,可以将复杂的C类型翻译成英语。虽然那可能是在90年代初期,但我敢打赌如果你搜索足够的话,它会为你提供比我刚才草率写的更准确的措辞描述。
没问题,只需写下来
char **choices = (char *[]){ "新游戏", "继续游戏", "退出" };
然而,choices
只能用于线性寻址。例如:
printf ("%s", &(*choices)[0]);
输出:新游戏
printf ("%s", &(*choices)[1]);
输出:游戏
printf ("%s", &(*choices)[9]);
输出:继续游戏
所以这不是一个玩笑,它是一个有效的初始化。只是另一种用法。
你也可以在这里找到一个非常相似的例子,解释了 复合字面量 的概念。
char *choices[] = { "New Game", "Continue Game", "Exit" };
呢? - SiegeX在线C标准(草案n1256):
6.7.8 初始化
...
11 标量的初始化器必须是一个单一表达式,可以用括号括起来。对象的初始值为表达式的值(在转换后);对于简单赋值适用相同的类型约束和转换,将标量的类型视为其声明类型的未限定版本。
...
16 否则,具有聚合或联合类型的对象的初始化器应为元素或命名成员的大括号括起来的初始化器列表。
强调添加。
char **
是标量类型,不是聚合类型,因此与初始化器{"New Game", "Continue Game", "Exit"}
不兼容。相比之下,char *[]
是聚合(数组)类型。
类似地,您不能编写以下内容:
int *foo = {1, 2, 3};
因为int *
不是数组类型。
你对
char *c = "line";
并且
char c[] = "line";
有点不同;它们并不是相同的。第一种形式将字符串文字的地址复制到指针值c
中。第二种形式将数组表达式"line"
的内容复制到由c
指定的缓冲区中。