4

我有一个char pointer这样的字符串文字char *d="abc";,我正在增加它

*(d+1)

b如果我这样做,我会得到类似的东西printf("%c",*(d+1))

但是当我有这些台词时

char *c={'a','b','c'}
printf("%c\n",*(c+1)); /// CAUSES SegFault

上面的行抛出异常。当我尝试使用 gdb 进行回溯和打印 *(c+1) 时,它说$1 = 0x61 <error: Cannot access memory at address 0x61>

所以我的问题是为什么这与我将字符串文字分配给时相比不起作用char pointer

当我将数组分配intint pointer并以这种方式递增时,也会发生同样的情况

4

2 回答 2

3

非常感谢@nielsen 指出这一点,在他们发表评论后一切都变得清晰起来。

首先,让我们尝试一个不会出现段错误的类似程序:

#include <stdio.h>

int main()
{
    char *a = {'a', 'b', 'c'};
    printf("%p\n", (void *) a);
}

对我来说,这输出:0x61. 那应该敲响警钟,这与 GDB 给出的地址相同。

然而,更重要的是我收到的警告:

main.c:5:16: warning: initialization makes pointer from integer without a cast [-Wint-conversion]                                                           
     char *a = {'a', 'b', 'c'};                                                                                                                             
                ^~~                                                                                                                                         
main.c:5:16: note: (near initialization for ‘a’)                                                                                                            
main.c:5:21: warning: excess elements in scalar initializer                                                                                                 
     char *a = {'a', 'b', 'c'};

main.c:5:16: warning: initialization makes pointer from integer without a cast [-Wint-conversion]                                                           
     char *a = {'a', 'b', 'c'};                                                                                                                             
                ^~~                                                                                                                                         
main.c:5:16: note: (near initialization for ‘a’)                                                                                                            
main.c:5:21: warning: excess elements in scalar initializer                                                                                                 
     char *a = {'a', 'b', 'c'};

main.c:5:21: warning: excess elements in scalar initializer                                                                                                 
     char *a = {'a', 'b', 'c'};                                                                                                                             
                     ^~~                                                                                                                                    
main.c:5:21: note: (near initialization for ‘a’)                                                                                                            
main.c:5:26: warning: excess elements in scalar initializer                                                                                                 
     char *a = {'a', 'b', 'c'};                                                                                                                             
                          ^~~                                                                                                                               
main.c:5:26: note: (near initialization for ‘a’)

initialization makes pointer from integer without a cast [-Wint-conversion]已经在评论中指出。然而,随着另一个警告,这变得很清楚:

main.c:5:21: warning: excess elements in scalar initializer                                                                                                 
     char *a = {'a', 'b', 'c'};                                                                                                                             
                     ^~~                                                                                                                                    
main.c:5:21: note: (near initialization for ‘a’)                                                                                                            
main.c:5:26: warning: excess elements in scalar initializer                                                                                                 
     char *a = {'a', 'b', 'c'};

基本上,这并不像你认为的那样。完全没有。这{}是一个“标量”初始化程序。来自https://en.cppreference.com/w/c/language/type,摘录如下:

标量类型:算术类型和指针类型

指针恰好是标量类型,因为它只能保存 1 个值,即地址。因此编译器将仅用于'a'初始化c,因为c只能保存 1 个值,而忽略其他所有内容(因为又是scalar)。十六进制的 ASCII 值是'a'多少?61,与 GDB 指出的地址完全相同。希望您了解现在发生的情况:

  • 当编译器看到char *c = {'a', 'b', 'c'};时,它会将聚合初始值设定项视为标量初始值设定项,因为c它是一个标量变量,因此只需要'a'并告诉您放置 2 个额外字符。

  • 'a',一个int文字,被隐式转换为char *并成为一个地址。编译器也会就此发出警告。

  • 您尝试打印*(c + 1),但由于这是一个无效地址/您不允许触摸该地址,因此会发生段错误。


我认为您真正想要做的是将c视为一个数组。为此,您可以将c's 的类型更改为数组:

char c[] = {'a', 'b', 'c'};

或保留c为 achar *并使用复合文字:

char *c = (char []) {'a', 'b', 'c'};

但是,char *c = {'a', 'b', 'c'};C 是无效的,因为大括号封闭的标量初始值设定项只允许保存 1 个表达式。弗拉德的回答给出了证明这一点的标准的具体引用。编译此代码-pedantic-errors会将此处提到的所有警告替换为错误。

于 2021-04-27T08:01:35.180 回答
2

您忘记放置分号的声明

char *c={'a','b','c'};
                   ^^^^

不是 C 中的有效构造。您不能使用包含多个初始值设定项的花括号列表来初始化标量对象。

来自 C 标准(6.7.9 初始化)

11标量的初始值设定项应为单个表达式,可选择用大括号括起来。对象的初始值是表达式的初始值(转换后);应用与简单赋值相同的类型约束和转换,将标量的类型作为其声明类型的非限定版本。

所以编译器会发出一条错误消息,实际上没有什么可讨论的,因为你有一个不能成功编译的程序。

你可以写例如

char *c = { ( 'a','b','c' ) };

在这种情况下,带有逗号运算符的表达式将用作初始化表达式。这个初始化等价于

char *c = { 'c'};

因此指针c由字符的内部代码初始化'c'。例如,如果使用了 ASCII 表,那么上面的初始化等价于

char *c = 99;

编译器应该再次发出一条消息,表明您正在尝试使用整数初始化指针。

由于99用作地址的值未指向程序中的有效对象,因此此语句

printf("%c\n",*(c+1));

调用未定义的行为。

或者您可以使用例如复合文字来初始化指针c,如下面的演示程序所示

#include <stdio.h>

int main(void) 
{
    char *c = ( char [] ){ 'a', 'b', 'c' };
    printf( "%c\n", *(c+1) );
    
    return 0;
}

在这种情况下,您将获得预期的结果。指向字符串字面量的指针与指向字符串的指针的唯一区别在于,在程序中指针c不指向字符串。但是你可以像这样初始化它

    char *c = ( char [] ){ 'a', 'b', 'c', '\0' };

它将指向一个字符串。

于 2021-04-27T08:31:59.093 回答