简体   繁体   English

将字符串文字与数组分配给 char

[英]assigning string literal vs array to char

I have a char pointer with string literal like this char *d="abc";我有一个带有字符串文字的char pointer ,例如char *d="abc"; and I am incrementing it like我正在增加它

*(d+1)

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

But when I have these lines但是当我有这些台词时

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

Above line throwing exception.上面的行抛出异常。 when I try to do backtrace and print *(c+1) with gdb it says $1 = 0x61 <error: Cannot access memory at address 0x61>当我尝试使用 gdb 进行回溯和打印 *(c+1) 时,它显示$1 = 0x61 <error: Cannot access memory at address 0x61>

So my question is why this does not work compare to when I assign string literal to char pointer所以我的问题是为什么这与我将字符串文字分配给char pointer时相比不起作用

same happens when I assign array of int to int pointer and increment it this way当我将int数组分配给int pointer并以这种方式递增时,也会发生同样的情况

Major thanks to @nielsen for pointing this out, it all became clear after their comment.非常感谢@nielsen 指出这一点,在他们发表评论后一切都变得清晰起来。

First of all, let's try a similar program that won't segfault:首先,让我们尝试一个不会出现段错误的类似程序:

#include <stdio.h>

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

For me, this outputs: 0x61 .对我来说,这输出: 0x61 That should ring a bell, it's the same address that GDB gave.这应该响起,它与 GDB 给出的地址相同。

More important, however, are the warnings I am getting:然而,更重要的是我收到的警告:

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] was already pointed out in the comments. initialization makes pointer from integer without a cast [-Wint-conversion]已经在评论中指出。 However, with another warning, this becomes clear:然而,随着另一个警告,这变得很清楚:

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'};

Basically, this doesn't do what you think it does.基本上,这并不像你认为的那样。 At all.完全没有。 the {} is a "scalar" initializer. {}是一个“标量”初始化程序。 From https://en.cppreference.com/w/c/language/type , here's an excerpt:https://en.cppreference.com/w/c/language/type摘录如下:

scalar types: arithmetic types and pointer types标量类型:算术类型和指针类型

A pointer happens to be a scalar type because it can only hold 1 value, which is an address.指针恰好是标量类型,因为它只能保存 1 个值,即地址。 So the compiler will only use 'a' to initialize c since c can only hold 1 value, and ignores everything else (because again, scalar ).所以编译器将只使用'a'来初始化c因为c只能保存 1 个值,并忽略其他所有内容(因为再次,标量)。 What's the ASCII value of 'a' in hex?十六进制中'a'的 ASCII 值是多少? 61, the exact same number as the address GDB pointed out. 61,与地址GDB指出的完全相同的数字。 Hopefully, you get what's going on now:希望您了解现在发生的情况:

  • When the compiler sees char *c = {'a', 'b', 'c'};当编译器看到char *c = {'a', 'b', 'c'}; , it treats the aggregate initializer as a scalar initializer because c is a scalar variable, so only takes 'a' and tells you off for putting 2 extra characters. ,它将聚合初始值设定项视为标量初始值设定项,因为c是一个标量变量,因此只需要'a'并告诉您不要放置 2 个额外的字符。

  • 'a' , an int literal, is implicitly converted to char * and it becomes an address. 'a' ,一个int文字,被隐式转换为char *并成为一个地址。 The compiler also warns you about this.编译器也会就此发出警告。

  • You try to print *(c + 1) , but since that is an invalid address/you're not allowed to touch that address, a segfault occurs.您尝试打印*(c + 1) ,但由于这是一个无效地址/您不允许触摸该地址,因此会发生段错误。


What I think you actually want to do is treat c as an array.我认为您实际上想要做的是将c视为一个数组。 To do this, you can either change c 's type into an array:为此,您可以将c的类型更改为数组:

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

Or keep c as a char * and use a compound literal:或者将c保留为char *并使用复合文字:

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

However, char *c = {'a', 'b', 'c'};但是, char *c = {'a', 'b', 'c'}; is not valid C as a brace-enclosed scalar initializer is only allowed to hold 1 expression.无效 C 作为大括号封闭的标量初始值设定项仅允许保存 1 个表达式。 Vlad's answer gives the specific quote from the standard that proves this.弗拉德的回答给出了证明这一点的标准的具体引用。 Compiling this code with -pedantic-errors will replace all the warnings mentioned here with errors.使用-pedantic-errors编译此代码会将此处提到的所有警告替换为错误。

This declaration where you forgot to place a semicolon您忘记放置分号的声明

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

is not a valid construction in C.不是 C 中的有效结构。 You may not initialize a scalar object with a braced list that contains more than one initializer.您不能使用包含多个初始化程序的花括号列表来初始化标量 object。

From the C Standard (6.7.9 Initialization)来自 C 标准(6.7.9 初始化)

11 The initializer for a scalar shall be a single expression, optionally enclosed in braces. 11标量的初始值设定项应为单个表达式,可选择用大括号括起来。 The initial value of the object is that of the expression (after conversion); object的初始值为表达式的初始值(转换后); 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.应用与简单赋值相同的类型约束和转换,将标量的类型作为其声明类型的非限定版本。

So the compiler shall issue an error message and in fact there is nothing to discuss because you have a program that shall not be successfully compiled.所以编译器会发出一条错误消息,实际上没有什么可讨论的,因为你有一个不能成功编译的程序。

You could write for example你可以写例如

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

In this case an expression with the comma operator will be used as an initializer expression.在这种情况下,带有逗号运算符的表达式将用作初始化表达式。 This initialization is equivalent to这个初始化等价于

char *c = { 'c'};

Thus the pointer c is initialized by the internal code of the character 'c' .因此指针c由字符'c'的内部代码初始化。 For example if there is used the ASCII table then the above initialization is equivalent to例如,如果使用了 ASCII 表,那么上面的初始化等价于

char *c = 99;

Again the compiler should issue a message that you are trying to initialize a pointer with an integer.编译器应该再次发出一条消息,表明您正在尝试使用 integer 初始化指针。

As the value 99 used as an address does not point to a valid object in your program then this statement由于用作地址的值99在您的程序中未指向有效的 object,因此此语句

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

invokes undefined behavior.调用未定义的行为。

Or you could use for example a compound literal to initialize the pointer c as it is shown in the demonstrative program below或者您可以使用例如复合文字来初始化指针c ,如下面的演示程序所示

#include <stdio.h>

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

In this case you will get the expected result.在这种情况下,您将获得预期的结果。 The only difference with pointers that point to string literals that is that point to strings is that in the program the pointer c does not point to a string.指向字符串字面量的指针的唯一区别是,在程序中,指针c不指向字符串。 But you could initialize it like但是你可以像这样初始化它

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

and it will point to a string.它将指向一个字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM