将字符串文字与数组分配给char
assigning string literal vs array to char
我有一个 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) 时,它说 = 0x61 <error: Cannot access memory at address 0x61>
所以我的问题是为什么这与我将字符串文字分配给 char pointer
时相比不起作用
当我将 int
的数组分配给 int pointer
并以这种方式递增它时,也会发生同样的情况
非常感谢@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,摘录如下:
scalar types: arithmetic types and pointer types
指针恰好是标量类型,因为它只能保存 1 个值,即地址。所以编译器将只使用 'a'
来初始化 c
因为 c
只能保存 1 个值,并忽略其他所有内容(因为再次,scalar) . 'a'
的十六进制 ASCII 值是多少? 61,与 GDB 指出的地址完全相同。希望您了解现在发生的事情:
当编译器看到char *c = {'a', 'b', 'c'};
时,它会将聚合初始值设定项视为标量初始值设定项,因为c
是一个标量变量,所以只取'a'
并告诉你放了 2 个额外的字符。
'a'
,一个 int
文字,被隐式转换为 char *
并成为一个地址。编译器也会对此发出警告。
您尝试打印 *(c + 1)
,但由于这是无效的 address/you 不允许触及该地址,因此发生段错误。
我认为您真正想要做的是将 c
视为一个数组。为此,您可以将 c
的类型更改为数组:
char c[] = {'a', 'b', 'c'};
或者将 c
保留为 char *
并使用复合文字:
char *c = (char []) {'a', 'b', 'c'};
但是,char *c = {'a', 'b', 'c'};
不是有效的 C,因为用大括号括起来的标量初始值设定项只允许包含 1 个表达式。 Vlad 的回答给出了证明这一点的标准的具体引用。使用 -pedantic-errors
编译此代码会将此处提到的所有警告替换为错误。
您忘记放置分号的声明
char *c={'a','b','c'};
^^^^
在 C 中不是有效的构造。您不能使用包含多个初始化器的花括号列表来初始化标量对象。
来自C标准(6.7.9初始化)
11 The initializer for a scalar shall be a single expression,
optionally enclosed in braces. The initial value of the object is
that of the expression (after conversion); the same type constraints
and conversions as for simple assignment apply, taking the type of the
scalar to be the unqualified version of its declared type.
因此编译器将发出错误消息,实际上没有什么可讨论的,因为您有一个无法成功编译的程序。
你可以这样写
char *c = { ( 'a','b','c' ) };
在这种情况下,带有逗号运算符的表达式将用作初始化表达式。这个初始化相当于
char *c = { 'c'};
因此指针c
由字符'c'
的内码初始化。例如,如果使用 ASCII table 那么上面的初始化等同于
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', '[=16=]' };
它将指向一个字符串。
我有一个 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) 时,它说 = 0x61 <error: Cannot access memory at address 0x61>
所以我的问题是为什么这与我将字符串文字分配给 char pointer
当我将 int
的数组分配给 int pointer
并以这种方式递增它时,也会发生同样的情况
非常感谢@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,摘录如下:
scalar types: arithmetic types and pointer types
指针恰好是标量类型,因为它只能保存 1 个值,即地址。所以编译器将只使用 'a'
来初始化 c
因为 c
只能保存 1 个值,并忽略其他所有内容(因为再次,scalar) . 'a'
的十六进制 ASCII 值是多少? 61,与 GDB 指出的地址完全相同。希望您了解现在发生的事情:
当编译器看到
char *c = {'a', 'b', 'c'};
时,它会将聚合初始值设定项视为标量初始值设定项,因为c
是一个标量变量,所以只取'a'
并告诉你放了 2 个额外的字符。'a'
,一个int
文字,被隐式转换为char *
并成为一个地址。编译器也会对此发出警告。您尝试打印
*(c + 1)
,但由于这是无效的 address/you 不允许触及该地址,因此发生段错误。
我认为您真正想要做的是将 c
视为一个数组。为此,您可以将 c
的类型更改为数组:
char c[] = {'a', 'b', 'c'};
或者将 c
保留为 char *
并使用复合文字:
char *c = (char []) {'a', 'b', 'c'};
但是,char *c = {'a', 'b', 'c'};
不是有效的 C,因为用大括号括起来的标量初始值设定项只允许包含 1 个表达式。 Vlad 的回答给出了证明这一点的标准的具体引用。使用 -pedantic-errors
编译此代码会将此处提到的所有警告替换为错误。
您忘记放置分号的声明
char *c={'a','b','c'};
^^^^
在 C 中不是有效的构造。您不能使用包含多个初始化器的花括号列表来初始化标量对象。
来自C标准(6.7.9初始化)
11 The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.
因此编译器将发出错误消息,实际上没有什么可讨论的,因为您有一个无法成功编译的程序。
你可以这样写
char *c = { ( 'a','b','c' ) };
在这种情况下,带有逗号运算符的表达式将用作初始化表达式。这个初始化相当于
char *c = { 'c'};
因此指针c
由字符'c'
的内码初始化。例如,如果使用 ASCII table 那么上面的初始化等同于
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', '[=16=]' };
它将指向一个字符串。