puts 语句在屏幕上显示“this”。
这只是(坏)运气,如果编译器根本不拒绝编译它,代码会调用未定义的行为。
clang(默认情况下)对代码产生的警告是
$ clang badpoint.c
badpoint.c:6:18: warning: incompatible pointer types initializing 'char **' with an
expression of type 'char [5]' [-Wincompatible-pointer-types]
char **bs = {"this", "is", "a", "test"};
^~~~~~
badpoint.c:6:26: warning: excess elements in scalar initializer
char **bs = {"this", "is", "a", "test"};
^~~~
badpoint.c:8:10: warning: incompatible pointer types passing 'char **' to parameter of
type 'const char *'dereference with * [-Wincompatible-pointer-types]
puts(bs);
^~
*
/usr/include/stdio.h:688:32: note: passing argument to parameter '__s' here
extern int puts (__const char *__s);
^
3 warnings generated.
gcc 为每个多余的初始化器生成一个警告,而 clang 只为三个中的第一个给出一个警告,否则来自 gcc 的警告(也是默认情况下)信息量少一些,但相同。
发生什么了?
您正在尝试初始化一个标量 ( char**
),并提供一个包含四个初始化程序s{"this", "is", "a", "test"}
的大括号括起来的初始化程序列表。每 6.7.9 (11)
标量的初始值设定项应为单个表达式,可选择用大括号括起来。对象的初始值是表达式的初始值(转换后);应用与简单赋值相同的类型约束和转换,将标量的类型作为其声明类型的非限定版本。
这违反了“应”要求,因此会调用未定义的行为。
如果编译器没有在那里终止翻译,它可能只是忽略多余的初始化程序 - clang 和 gcc 都这样做 - 并将声明视为
char **bs = "this";
这会导致有关从不兼容类型初始化的警告,因为它试图char**
用 a初始化 a char[5]
,但根据 6.5.16.1,初始化程序的类型必须与 兼容char**
。
然而,如果编译器成功地翻译了程序,它很可能导致在字符串文字中bs
包含第一个地址(数组被转换为指向赋值运算符右侧第一个元素的指针)。char
"this"
然后打电话
puts(bs)
char**
将错误类型 ( )的指针传递给puts
,这是违反约束的,需要来自编译器的诊断消息(并使程序无效)。
但是指针恰好包含正确的地址,并且未定义的行为表现为程序打印"this"
。
我将如何让它显示“是”等等
通过修正程序。实际上,字符串"is"
、"a"
和"test"
甚至不会出现在 gcc 或 clang 生成的目标文件中。
纠正它的一种方法是将声明更改为
char *bs[] = {"this", "is", "a", "test"};
所以这bs
是一个由四个组成的数组char*
,指向四个字符串的(各自的第一个元素)。
然后你必须调整调用puts
,取消引用或下标bs
,
puts(bs[0]);
打印"this"
;
for(int i = 0; i < 4; ++i) {
puts(bs[i]);
}
在单独的行上打印所有四个字符串。