-8

如果我有一个指向此示例中变量“bs”之类的指针的指针:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char **bs = {"this", "is", "a", "test"};

    puts(bs);

    return 0;
}

puts 语句在屏幕上显示“this”。我将如何让它显示“是”等等。我虽然我可以做类似的事情:

puts(bs+1);

将数字添加到指针变量实际上有什么作用?它是指向下一个地址,还是考虑指针的类型并使用它指向的变量的大小来确定下一个变量在内存中的起始位置?

4

1 回答 1

3

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]);
}

在单独的行上打印所有四个字符串。

于 2013-04-09T13:22:08.273 回答