【问题标题】:assigning string literal vs array to char将字符串文字与数组分配给 char
【发布时间】:2021-04-27 07:22:29
【问题描述】:

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

*(d+1)

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

但是当我有这些行时

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 相比不起作用

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

【问题讨论】:

  • 你不会在char *c={'a','b','c'} 行上看到“警告:多余的东西”“警告:不好的东西”吗?
  • 我很惊讶char *c={'a','b','c'} 有效(也因为你错过了一个分号)。但是您正在尝试将数组初始化为指针。将其替换为 char c[] = {'a', 'b', 'c'};char *c = (char []) {'a', 'b', 'c'};
  • 您正在将c 初始化为'a',而不是您似乎期望的指向数组的指针。编译器应该对此发出严重警告。
  • main.c:10:13: warning: initialization of ‘char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion] 我收到了这些警告
  • @user786 正如尼尔森所说,您将文字 'a'(在 C 中是 int,而不是 char,因此在警告中提到了 int)给char *,所以它进行了隐式转换,c 持有与'a' 的 ASCII 值等效的地址。

标签: c pointers initialization scalar braced-init-list


【解决方案1】:

非常感谢@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] 已在 cmets 中指出。然而,有了另一个警告,这变得很清楚:

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)。 'a' 的十六进制 ASCII 值是多少? 61,与 GDB 指出的地址完全相同。希望您了解现在的情况:

  • 当编译器看到char *c = {'a', 'b', 'c'}; 时,它会将聚合初始值设定项视为标量初始值设定项,因为c 是一个标量变量,所以只接受'a' 并告诉你不要多放2 个字符。

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

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


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

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

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

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

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

【讨论】:

  • 想用this链接解释一个标量?
  • @Zoso 已编辑,谢谢。我很难找到一个与物理无关的东西。
  • 我的错,我应该分享 types 的 C-lang 文档,其中声明 scalar types: arithmetic types and pointer types
  • char *a = {'a', 'b', 'c'}; 不是有效的 C,因此代码甚至无法干净地编译。这个答案的大约 90% 与标准 C 无关。
  • @Lundin 啊,真可惜:\ 不过我在底部添加了一个脚注,谢谢。
【解决方案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' };

它会指向一个字符串。

【讨论】:

    猜你喜欢
    • 2012-05-03
    • 2015-06-25
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 2011-02-07
    • 2014-06-08
    • 2012-10-16
    相关资源
    最近更新 更多