char和char有什么区别?

时间:2022-08-19 15:48:52

In C, one can use a string literal in a declaration like this:

在C语言中,可以在如下声明中使用字符串文字:

char s[] = "hello";

or like this:

或者像这样:

char *s = "hello";

So what is the difference? I want to know what actually happens in terms of storage duration, both at compile and run time.

那么区别是什么呢?我想知道在编译和运行时,在存储时间方面究竟发生了什么。

12 个解决方案

#1


475  

The difference here is that

不同之处在于。

char *s = "Hello world";

will place "Hello world" in the read-only parts of the memory, and making s a pointer to that makes any writing operation on this memory illegal.

将“Hello world”放置在内存的只读部分中,并生成一个指针,使该内存中的任何写入操作都是非法的。

While doing:

虽然做的事情:

char s[] = "Hello world";

puts the literal string in read-only memory and copies the string to newly allocated memory on the stack. Thus making

将文字字符串放在只读内存中,并将字符串复制到堆栈上新分配的内存中。从而使

s[0] = 'J';

legal.

合法的。

#2


130  

First off, in function arguments, they are exactly equivalent:

首先,在函数参数中,它们是完全等价的:

void foo(char *x);
void foo(char x[]); // exactly the same in all respects

In other contexts, char * allocates a pointer, while char [] allocates an array. Where does the string go in the former case, you ask? The compiler secretly allocates a static anonymous array to hold the string literal. So:

在其他情况下,char *分配一个指针,而char[]分配一个数组。你会问,在前一种情况下,弦在哪里?编译器秘密地分配一个静态匿名数组来保存字符串文字。所以:

char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;

Note that you must not ever attempt to modify the contents of this anonymous array via this pointer; the effects are undefined (often meaning a crash):

注意,您决不能试图通过这个指针修改这个匿名数组的内容;这些影响是没有定义的(通常意味着崩溃):

x[1] = 'O'; // BAD. DON'T DO THIS.

Using the array syntax directly allocates it into new memory. Thus modification is safe:

使用数组语法直接将它分配到新内存中。因此修改是安全的:

char x[] = "Foo";
x[1] = 'O'; // No problem.

However the array only lives as long as its contaning scope, so if you do this in a function, don't return or leak a pointer to this array - make a copy instead with strdup() or similar. If the array is allocated in global scope, of course, no problem.

但是,数组的生命周期仅与它的处理范围相同,因此如果在函数中执行此操作,则不要返回或泄漏指向该数组的指针——使用strdup()或类似的方法进行复制。如果数组在全局范围内分配,当然没有问题。

#3


59  

This declaration:

此声明:

char s[] = "hello";

Creates one object - a char array of size 6, called s, initialised with the values 'h', 'e', 'l', 'l', 'o', '\0'. Where this array is allocated in memory, and how long it lives for, depends on where the declaration appears. If the declaration is within a function, it will live until the end of the block that it is declared in, and almost certainly be allocated on the stack; if it's outside a function, it will probably be stored within an "initialised data segment" that is loaded from the executable file into writeable memory when the program is run.

创建一个对象——大小为6的char数组,名为s,初始化值为'h'、'e'、'l'、'l'、'o'、'\0'。这个数组在内存中分配的位置以及它的生存时间取决于声明出现的位置。如果该声明在一个函数中,它将一直存活到它被声明的块的末尾,并且几乎可以肯定地在堆栈上分配;如果它在函数之外,它可能存储在一个“初始化数据段”中,该数据段在程序运行时从可执行文件加载到可写内存中。

On the other hand, this declaration:

另一方面,本宣言:

char *s ="hello";

Creates two objects:

创建了两个对象:

  • a read-only array of 6 chars containing the values 'h', 'e', 'l', 'l', 'o', '\0', which has no name and has static storage duration (meaning that it lives for the entire life of the program); and
  • 一个只读数组,包含值'h'、'e'、'l'、'l'、'l'、'o'、'\0',它没有名称,并且具有静态存储时间(这意味着它在程序的整个生命周期中都存在);和
  • a variable of type pointer-to-char, called s, which is initialised with the location of the first character in that unnamed, read-only array.
  • 一种类型的pointerto -char类型的变量,称为s,它是在未命名的只读数组中的第一个字符的位置初始化的。

The unnamed read-only array is typically located in the "text" segment of the program, which means it is loaded from disk into read-only memory, along with the code itself. The location of the s pointer variable in memory depends on where the declaration appears (just like in the first example).

未命名的只读数组通常位于程序的“文本”段中,这意味着它与代码一起从磁盘加载到只读内存中。s指针变量在内存中的位置取决于声明出现的位置(如第一个示例所示)。

#4


51  

Given the declarations

鉴于声明

char *s0 = "hello world";
char s1[] = "hello world";

assume the following hypothetical memory map:

假设以下假设的内存映射:

                    0x01  0x02  0x03  0x04
        0x00008000: 'h'   'e'   'l'   'l'
        0x00008004: 'o'   ' '   'w'   'o'
        0x00008008: 'r'   'l'   'd'   0x00
        ...
s0:     0x00010000: 0x00  0x00  0x80  0x00
s1:     0x00010004: 'h'   'e'   'l'   'l'
        0x00010008: 'o'   ' '   'w'   'o'
        0x0001000C: 'r'   'l'   'd'   0x00

The string literal "hello world" is a 12-element array of char (const char in C++) with static storage duration, meaning that the memory for it is allocated when the program starts up and remains allocated until the program terminates. Attempting to modify the contents of a string literal invokes undefined behavior.

字符串字面上的“hello world”是一个具有静态存储持续时间的12元字符的char (const char)数组,这意味着当程序启动时,它的内存将被分配,直到程序终止时才分配。试图修改字符串文字的内容会调用未定义的行为。

The line

这条线

char *s0 = "hello world";

defines s0 as a pointer to char with auto storage duration (meaning the variable s0 only exists for the scope in which it is declared) and copies the address of the string literal (0x00008000 in this example) to it. Note that since s0 points to a string literal, it should not be used as an argument to any function that would try to modify it (e.g., strtok(), strcat(), strcpy(), etc.).

将s0定义为具有自动存储持续时间的字符(意味着变量s0只存在于声明的范围内),并将字符串文本的地址(本例中的0x00008000)复制到它。注意,由于s0指向一个字符串文字,所以它不应该用作任何试图修改它的函数的参数(例如strtok()、strcat()、strcpy()等)。

The line

这条线

char s1[] = "hello world";

defines s1 as a 12-element array of char (length is taken from the string literal) with auto storage duration and copies the contents of the literal to the array. As you can see from the memory map, we have two copies of the string "hello world"; the difference is that you can modify the string contained in s1.

将s1定义为一个12元素的char数组(从字符串文本中提取长度),并使用自动存储持续时间,并将文本的内容复制到数组中。从内存映射中可以看到,我们有两个字符串“hello world”的副本;区别在于,您可以修改s1中包含的字符串。

s0 and s1 are interchangeable in most contexts; here are the exceptions:

s0和s1在大多数上下文中是可以互换的;这是例外:

sizeof s0 == sizeof (char*)
sizeof s1 == 12

type of &s0 == char **
type of &s1 == char (*)[12] // pointer to a 12-element array of char

You can reassign the variable s0 to point to a different string literal or to another variable. You cannot reassign the variable s1 to point to a different array.

您可以重新分配变量s0来指向一个不同的字符串文字或另一个变量。不能重新分配变量s1来指向另一个数组。

#5


25  

C99 N1256 draft

C99 N1256草案

There are two completely different uses of array literals:

数组文字有两种完全不同的用法:

  1. Initialize char[]:

    初始化char[]:

    char c[] = "abc";      
    

    This is "more magic", and described at 6.7.8/14 "Initialization":

    这是“更神奇的”,在6.7.8/14“初始化”中描述:

    An array of character type may be initialized by a character string literal, optionally enclosed in braces. Successive characters of the character string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.

    字符类型的数组可以由字符串字面量来初始化,也可以选择用括号括起来。字符串文字的连续字符(如果有空间或数组大小未知,包括终止空字符)初始化数组的元素。

    So this is just a shortcut for:

    这是一个捷径

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

    Like any other regular array, c can be modified.

    与任何其他常规数组一样,c可以被修改。

  2. Everywhere else: it generates an:

    其他地方:它产生:

    So when you write:

    所以当你写:

    char *c = "abc";
    

    This is similar to:

    这类似于:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;
    

    Note the implicit cast from char[] to char *, which is always legal.

    注意从char[]到char *的隐式转换,这始终是合法的。

    Then if you modify c[0], you also modify __unnamed, which is UB.

    然后如果你修改c[0],你也修改了__unknown,也就是UB。

    This is documented at 6.4.5 "String literals":

    这在6.4.5“字符串文字”中有记载:

    5 In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence [...]

    在翻译阶段7中,一个字节或值为0的代码被附加到每个多字节字符序列中,这些字符序列由字符串文字或文字产生。然后使用多字节字符序列初始化静态存储持续时间和长度足以包含序列的数组。对于字符串文本,数组元素具有类型char,并使用多字节字符序列的单个字节进行初始化[…]

    6 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

    如果这些数组的元素具有适当的值,那么这些数组是否不同就不得而知了。如果程序试图修改这样的数组,则该行为未定义。

6.7.8/32 "Initialization" gives a direct example:

6.7.8/32“初始化”给出了一个直接的例子:

EXAMPLE 8: The declaration

例8:声明

char s[] = "abc", t[3] = "abc";

defines "plain" char array objects s and t whose elements are initialized with character string literals.

定义“普通”字符数组对象s和t,它们的元素是用字符串常量初始化的。

This declaration is identical to

此声明与

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

The contents of the arrays are modifiable. On the other hand, the declaration

数组的内容是可以修改的。另一方面,宣言

char *p = "abc";

defines p with type "pointer to char" and initializes it to point to an object with type "array of char" with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to modify the contents of the array, the behavior is undefined.

定义具有“指向char的指针”类型的p,并将其初始化为指向具有“char数组”类型的对象,该对象的长度为4,其元素初始化为字符字符串字面量。如果尝试使用p修改数组的内容,则行为未定义。

GCC 4.8 x86-64 ELF implementation

GCC 4.8 x86-64 ELF实现

Program:

计划:

#include <stdio.h>

int main() {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Compile and decompile:

编译和反编译:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

Output contains:

输出包含:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

Conclusion: GCC stores char* it in .rodata section, not in .text.

结论:GCC将char*存储在.rodata部分,而不是.text部分。

If we do the same for char[]:

如果我们对char[]做同样的事:

 char s[] = "abc";

we obtain:

我们获得:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

so it gets stored in the stack (relative to %rbp).

所以它被存储在堆栈中(相对于%rbp)。

Note however that the default linker script puts .rodata and .text in the same segment, which has execute but no write permission. This can be observed with:

但是请注意,默认链接器脚本将.rodata和.text放在同一段中,该段具有执行权限,但没有写入权限。这可以用以下方法来观察:

readelf -l a.out

which contains:

它包含:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata 

#6


14  

char s[] = "hello";

declares s to be an array of char which is long enough to hold the initializer (5 + 1 chars) and initializes the array by copying the members of the given string literal into the array.

声明s是一个char数组,它的长度足以容纳初始化器(5 + 1 chars),并通过将给定字符串文字的成员复制到数组中来初始化数组。

char *s = "hello";

declares s to be a pointer to one or more (in this case more) chars and points it directly at a fixed (read-only) location containing the literal "hello".

声明s是指向一个或多个(本例中更多)字符的指针,并将其直接指向包含文字“hello”的固定(只读)位置。

#7


3  

char s[] = "Hello world";

Here, s is an array of characters, which can be overwritten if we wish.

这里,s是一个字符数组,如果我们愿意,可以重写它。

char *s = "hello";

A string literal is used to create these character blocks somewhere in the memory which this pointer s is pointing to. We can here reassign the object it is pointing to by changing that, but as long as it points to a string literal the block of characters to which it points can't be changed.

字符串文本用于在指针s指向的内存中某处创建这些字符块。我们可以通过改变它所指向的对象来重新分配它,但是只要它指向一个字符串字面量它指向的字符块就不能被改变。

#8


3  

As an addition, consider that, as for read-only purposes the use of both is identical, you can access a char by indexing either with [] or *(<var> + <index>) format:

另外,考虑到出于只读的目的,这两者的使用是相同的,您可以使用[]或*( + )格式访问char:

printf("%c", x[1]);     //Prints r

And:

和:

printf("%c", *(x + 1)); //Prints r

Obviously, if you attempt to do

显然,如果你想这么做的话

*(x + 1) = 'a';

You will probably get a Segmentation Fault, as you are trying to access read-only memory.

当您试图访问只读内存时,可能会出现分段错误。

#9


3  

Just to add: you also get different values for their sizes.

补充一下:您还会得到不同大小的值。

printf("sizeof s[] = %zu\n", sizeof(s));  //6
printf("sizeof *s  = %zu\n", sizeof(s));  //4 or 8

As mentioned above, for an array '\0' will be allocated as the final element.

如上所述,对于一个数组'\0'将被分配为最终元素。

#10


2  

char *str = "Hello";

The above sets str to point to the literal value "Hello" which is hard-coded in the program's binary image, which is flagged as read-only in memory, means any change in this String literal is illegal and that would throw segmentation faults.

上面的代码将str设置为指向在程序二进制映像中硬编码的文字值“Hello”(在内存中标记为只读),这意味着该字符串文字中的任何更改都是非法的,这将导致分割错误。

char str[] = "Hello";

copies the string to newly allocated memory on the stack. Thus making any change in it is allowed and legal.

将字符串复制到堆栈上新分配的内存中。因此,任何改变都是允许的,是合法的。

means str[0] = 'M';

will change the str to "Mello".

将str变为“Mello”。

For more details, please go through the similar question:

有关详情,请浏览以下类似的问题:

Why do I get a segmentation fault when writing to a string initialized with "char *s" but not "char s[]"?

为什么在写入以“char *s”而不是“char s[]”初始化的字符串时会出现分割错误?

#11


0  

In the case of:

的情况:

char *x = "fred";

x is an lvalue -- it can be assigned to. But in the case of:

x是一个lvalue,它可以被赋值。但在以下情况下:

char x[] = "fred";

x is not an lvalue, it is an rvalue -- you cannot assign to it.

x不是lvalue,它是rvalue,你不能赋值给它。

#12


0  

In the light of comments here it should be obvious that : char * s = "hello" ; Is a bad idea, and should be used in very narrow scope.

根据这里的评论,应该很明显:char * s =“hello”;是个坏主意,应该用在非常狭窄的范围内。

This might be a good opportunity to point out that "const correctness" is a "good thing". Whenever and wherever You can, use the "const" keyword to protect your code, from "relaxed" callers or programmers, which are usually most "relaxed" when pointers come into play.

这可能是一个很好的机会来指出“const正确性”是一件“好事”。无论何时何地,您都可以使用“const”关键字来保护您的代码,不受“放松”调用者或程序员的影响,当指针开始发挥作用时,这些调用通常是最“放松”的。

Enough melodrama, here is what one can achieve when adorning pointers with "const". (Note: One has to read pointer declarations right-to-left.) Here are the 3 different ways to protect yourself when playing with pointers :

足够多的情节剧,以下是用“const”装饰指针时可以达到的效果。(注意:必须读取右到左的指针声明。)这里有三种不同的方法来保护你自己玩指针:

const DBJ* p means "p points to a DBJ that is const" 

— that is, the DBJ object can't be changed via p.

-即DBJ对象不能通过p进行修改。

DBJ* const p means "p is a const pointer to a DBJ" 

— that is, you can change the DBJ object via p, but you can't change the pointer p itself.

-也就是说,你可以通过p改变DBJ对象,但是你不能改变指针p本身。

const DBJ* const p means "p is a const pointer to a const DBJ" 

— that is, you can't change the pointer p itself, nor can you change the DBJ object via p.

-即不能改变指针p本身,也不能通过p改变DBJ对象。

The errors related to attempted const-ant mutations are caught at compile time. There is no runtime space or speed penalty for const.

在编译时捕获与尝试const-ant突变相关的错误。没有运行时空间或速度损失为const。

(Assumption is you are using C++ compiler, of course ?)

(当然,假设您使用的是c++编译器?)

--DBJ

——日本

#1


475  

The difference here is that

不同之处在于。

char *s = "Hello world";

will place "Hello world" in the read-only parts of the memory, and making s a pointer to that makes any writing operation on this memory illegal.

将“Hello world”放置在内存的只读部分中,并生成一个指针,使该内存中的任何写入操作都是非法的。

While doing:

虽然做的事情:

char s[] = "Hello world";

puts the literal string in read-only memory and copies the string to newly allocated memory on the stack. Thus making

将文字字符串放在只读内存中,并将字符串复制到堆栈上新分配的内存中。从而使

s[0] = 'J';

legal.

合法的。

#2


130  

First off, in function arguments, they are exactly equivalent:

首先,在函数参数中,它们是完全等价的:

void foo(char *x);
void foo(char x[]); // exactly the same in all respects

In other contexts, char * allocates a pointer, while char [] allocates an array. Where does the string go in the former case, you ask? The compiler secretly allocates a static anonymous array to hold the string literal. So:

在其他情况下,char *分配一个指针,而char[]分配一个数组。你会问,在前一种情况下,弦在哪里?编译器秘密地分配一个静态匿名数组来保存字符串文字。所以:

char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;

Note that you must not ever attempt to modify the contents of this anonymous array via this pointer; the effects are undefined (often meaning a crash):

注意,您决不能试图通过这个指针修改这个匿名数组的内容;这些影响是没有定义的(通常意味着崩溃):

x[1] = 'O'; // BAD. DON'T DO THIS.

Using the array syntax directly allocates it into new memory. Thus modification is safe:

使用数组语法直接将它分配到新内存中。因此修改是安全的:

char x[] = "Foo";
x[1] = 'O'; // No problem.

However the array only lives as long as its contaning scope, so if you do this in a function, don't return or leak a pointer to this array - make a copy instead with strdup() or similar. If the array is allocated in global scope, of course, no problem.

但是,数组的生命周期仅与它的处理范围相同,因此如果在函数中执行此操作,则不要返回或泄漏指向该数组的指针——使用strdup()或类似的方法进行复制。如果数组在全局范围内分配,当然没有问题。

#3


59  

This declaration:

此声明:

char s[] = "hello";

Creates one object - a char array of size 6, called s, initialised with the values 'h', 'e', 'l', 'l', 'o', '\0'. Where this array is allocated in memory, and how long it lives for, depends on where the declaration appears. If the declaration is within a function, it will live until the end of the block that it is declared in, and almost certainly be allocated on the stack; if it's outside a function, it will probably be stored within an "initialised data segment" that is loaded from the executable file into writeable memory when the program is run.

创建一个对象——大小为6的char数组,名为s,初始化值为'h'、'e'、'l'、'l'、'o'、'\0'。这个数组在内存中分配的位置以及它的生存时间取决于声明出现的位置。如果该声明在一个函数中,它将一直存活到它被声明的块的末尾,并且几乎可以肯定地在堆栈上分配;如果它在函数之外,它可能存储在一个“初始化数据段”中,该数据段在程序运行时从可执行文件加载到可写内存中。

On the other hand, this declaration:

另一方面,本宣言:

char *s ="hello";

Creates two objects:

创建了两个对象:

  • a read-only array of 6 chars containing the values 'h', 'e', 'l', 'l', 'o', '\0', which has no name and has static storage duration (meaning that it lives for the entire life of the program); and
  • 一个只读数组,包含值'h'、'e'、'l'、'l'、'l'、'o'、'\0',它没有名称,并且具有静态存储时间(这意味着它在程序的整个生命周期中都存在);和
  • a variable of type pointer-to-char, called s, which is initialised with the location of the first character in that unnamed, read-only array.
  • 一种类型的pointerto -char类型的变量,称为s,它是在未命名的只读数组中的第一个字符的位置初始化的。

The unnamed read-only array is typically located in the "text" segment of the program, which means it is loaded from disk into read-only memory, along with the code itself. The location of the s pointer variable in memory depends on where the declaration appears (just like in the first example).

未命名的只读数组通常位于程序的“文本”段中,这意味着它与代码一起从磁盘加载到只读内存中。s指针变量在内存中的位置取决于声明出现的位置(如第一个示例所示)。

#4


51  

Given the declarations

鉴于声明

char *s0 = "hello world";
char s1[] = "hello world";

assume the following hypothetical memory map:

假设以下假设的内存映射:

                    0x01  0x02  0x03  0x04
        0x00008000: 'h'   'e'   'l'   'l'
        0x00008004: 'o'   ' '   'w'   'o'
        0x00008008: 'r'   'l'   'd'   0x00
        ...
s0:     0x00010000: 0x00  0x00  0x80  0x00
s1:     0x00010004: 'h'   'e'   'l'   'l'
        0x00010008: 'o'   ' '   'w'   'o'
        0x0001000C: 'r'   'l'   'd'   0x00

The string literal "hello world" is a 12-element array of char (const char in C++) with static storage duration, meaning that the memory for it is allocated when the program starts up and remains allocated until the program terminates. Attempting to modify the contents of a string literal invokes undefined behavior.

字符串字面上的“hello world”是一个具有静态存储持续时间的12元字符的char (const char)数组,这意味着当程序启动时,它的内存将被分配,直到程序终止时才分配。试图修改字符串文字的内容会调用未定义的行为。

The line

这条线

char *s0 = "hello world";

defines s0 as a pointer to char with auto storage duration (meaning the variable s0 only exists for the scope in which it is declared) and copies the address of the string literal (0x00008000 in this example) to it. Note that since s0 points to a string literal, it should not be used as an argument to any function that would try to modify it (e.g., strtok(), strcat(), strcpy(), etc.).

将s0定义为具有自动存储持续时间的字符(意味着变量s0只存在于声明的范围内),并将字符串文本的地址(本例中的0x00008000)复制到它。注意,由于s0指向一个字符串文字,所以它不应该用作任何试图修改它的函数的参数(例如strtok()、strcat()、strcpy()等)。

The line

这条线

char s1[] = "hello world";

defines s1 as a 12-element array of char (length is taken from the string literal) with auto storage duration and copies the contents of the literal to the array. As you can see from the memory map, we have two copies of the string "hello world"; the difference is that you can modify the string contained in s1.

将s1定义为一个12元素的char数组(从字符串文本中提取长度),并使用自动存储持续时间,并将文本的内容复制到数组中。从内存映射中可以看到,我们有两个字符串“hello world”的副本;区别在于,您可以修改s1中包含的字符串。

s0 and s1 are interchangeable in most contexts; here are the exceptions:

s0和s1在大多数上下文中是可以互换的;这是例外:

sizeof s0 == sizeof (char*)
sizeof s1 == 12

type of &s0 == char **
type of &s1 == char (*)[12] // pointer to a 12-element array of char

You can reassign the variable s0 to point to a different string literal or to another variable. You cannot reassign the variable s1 to point to a different array.

您可以重新分配变量s0来指向一个不同的字符串文字或另一个变量。不能重新分配变量s1来指向另一个数组。

#5


25  

C99 N1256 draft

C99 N1256草案

There are two completely different uses of array literals:

数组文字有两种完全不同的用法:

  1. Initialize char[]:

    初始化char[]:

    char c[] = "abc";      
    

    This is "more magic", and described at 6.7.8/14 "Initialization":

    这是“更神奇的”,在6.7.8/14“初始化”中描述:

    An array of character type may be initialized by a character string literal, optionally enclosed in braces. Successive characters of the character string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.

    字符类型的数组可以由字符串字面量来初始化,也可以选择用括号括起来。字符串文字的连续字符(如果有空间或数组大小未知,包括终止空字符)初始化数组的元素。

    So this is just a shortcut for:

    这是一个捷径

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

    Like any other regular array, c can be modified.

    与任何其他常规数组一样,c可以被修改。

  2. Everywhere else: it generates an:

    其他地方:它产生:

    So when you write:

    所以当你写:

    char *c = "abc";
    

    This is similar to:

    这类似于:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;
    

    Note the implicit cast from char[] to char *, which is always legal.

    注意从char[]到char *的隐式转换,这始终是合法的。

    Then if you modify c[0], you also modify __unnamed, which is UB.

    然后如果你修改c[0],你也修改了__unknown,也就是UB。

    This is documented at 6.4.5 "String literals":

    这在6.4.5“字符串文字”中有记载:

    5 In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence [...]

    在翻译阶段7中,一个字节或值为0的代码被附加到每个多字节字符序列中,这些字符序列由字符串文字或文字产生。然后使用多字节字符序列初始化静态存储持续时间和长度足以包含序列的数组。对于字符串文本,数组元素具有类型char,并使用多字节字符序列的单个字节进行初始化[…]

    6 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

    如果这些数组的元素具有适当的值,那么这些数组是否不同就不得而知了。如果程序试图修改这样的数组,则该行为未定义。

6.7.8/32 "Initialization" gives a direct example:

6.7.8/32“初始化”给出了一个直接的例子:

EXAMPLE 8: The declaration

例8:声明

char s[] = "abc", t[3] = "abc";

defines "plain" char array objects s and t whose elements are initialized with character string literals.

定义“普通”字符数组对象s和t,它们的元素是用字符串常量初始化的。

This declaration is identical to

此声明与

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

The contents of the arrays are modifiable. On the other hand, the declaration

数组的内容是可以修改的。另一方面,宣言

char *p = "abc";

defines p with type "pointer to char" and initializes it to point to an object with type "array of char" with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to modify the contents of the array, the behavior is undefined.

定义具有“指向char的指针”类型的p,并将其初始化为指向具有“char数组”类型的对象,该对象的长度为4,其元素初始化为字符字符串字面量。如果尝试使用p修改数组的内容,则行为未定义。

GCC 4.8 x86-64 ELF implementation

GCC 4.8 x86-64 ELF实现

Program:

计划:

#include <stdio.h>

int main() {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Compile and decompile:

编译和反编译:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

Output contains:

输出包含:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

Conclusion: GCC stores char* it in .rodata section, not in .text.

结论:GCC将char*存储在.rodata部分,而不是.text部分。

If we do the same for char[]:

如果我们对char[]做同样的事:

 char s[] = "abc";

we obtain:

我们获得:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

so it gets stored in the stack (relative to %rbp).

所以它被存储在堆栈中(相对于%rbp)。

Note however that the default linker script puts .rodata and .text in the same segment, which has execute but no write permission. This can be observed with:

但是请注意,默认链接器脚本将.rodata和.text放在同一段中,该段具有执行权限,但没有写入权限。这可以用以下方法来观察:

readelf -l a.out

which contains:

它包含:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata 

#6


14  

char s[] = "hello";

declares s to be an array of char which is long enough to hold the initializer (5 + 1 chars) and initializes the array by copying the members of the given string literal into the array.

声明s是一个char数组,它的长度足以容纳初始化器(5 + 1 chars),并通过将给定字符串文字的成员复制到数组中来初始化数组。

char *s = "hello";

declares s to be a pointer to one or more (in this case more) chars and points it directly at a fixed (read-only) location containing the literal "hello".

声明s是指向一个或多个(本例中更多)字符的指针,并将其直接指向包含文字“hello”的固定(只读)位置。

#7


3  

char s[] = "Hello world";

Here, s is an array of characters, which can be overwritten if we wish.

这里,s是一个字符数组,如果我们愿意,可以重写它。

char *s = "hello";

A string literal is used to create these character blocks somewhere in the memory which this pointer s is pointing to. We can here reassign the object it is pointing to by changing that, but as long as it points to a string literal the block of characters to which it points can't be changed.

字符串文本用于在指针s指向的内存中某处创建这些字符块。我们可以通过改变它所指向的对象来重新分配它,但是只要它指向一个字符串字面量它指向的字符块就不能被改变。

#8


3  

As an addition, consider that, as for read-only purposes the use of both is identical, you can access a char by indexing either with [] or *(<var> + <index>) format:

另外,考虑到出于只读的目的,这两者的使用是相同的,您可以使用[]或*( + )格式访问char:

printf("%c", x[1]);     //Prints r

And:

和:

printf("%c", *(x + 1)); //Prints r

Obviously, if you attempt to do

显然,如果你想这么做的话

*(x + 1) = 'a';

You will probably get a Segmentation Fault, as you are trying to access read-only memory.

当您试图访问只读内存时,可能会出现分段错误。

#9


3  

Just to add: you also get different values for their sizes.

补充一下:您还会得到不同大小的值。

printf("sizeof s[] = %zu\n", sizeof(s));  //6
printf("sizeof *s  = %zu\n", sizeof(s));  //4 or 8

As mentioned above, for an array '\0' will be allocated as the final element.

如上所述,对于一个数组'\0'将被分配为最终元素。

#10


2  

char *str = "Hello";

The above sets str to point to the literal value "Hello" which is hard-coded in the program's binary image, which is flagged as read-only in memory, means any change in this String literal is illegal and that would throw segmentation faults.

上面的代码将str设置为指向在程序二进制映像中硬编码的文字值“Hello”(在内存中标记为只读),这意味着该字符串文字中的任何更改都是非法的,这将导致分割错误。

char str[] = "Hello";

copies the string to newly allocated memory on the stack. Thus making any change in it is allowed and legal.

将字符串复制到堆栈上新分配的内存中。因此,任何改变都是允许的,是合法的。

means str[0] = 'M';

will change the str to "Mello".

将str变为“Mello”。

For more details, please go through the similar question:

有关详情,请浏览以下类似的问题:

Why do I get a segmentation fault when writing to a string initialized with "char *s" but not "char s[]"?

为什么在写入以“char *s”而不是“char s[]”初始化的字符串时会出现分割错误?

#11


0  

In the case of:

的情况:

char *x = "fred";

x is an lvalue -- it can be assigned to. But in the case of:

x是一个lvalue,它可以被赋值。但在以下情况下:

char x[] = "fred";

x is not an lvalue, it is an rvalue -- you cannot assign to it.

x不是lvalue,它是rvalue,你不能赋值给它。

#12


0  

In the light of comments here it should be obvious that : char * s = "hello" ; Is a bad idea, and should be used in very narrow scope.

根据这里的评论,应该很明显:char * s =“hello”;是个坏主意,应该用在非常狭窄的范围内。

This might be a good opportunity to point out that "const correctness" is a "good thing". Whenever and wherever You can, use the "const" keyword to protect your code, from "relaxed" callers or programmers, which are usually most "relaxed" when pointers come into play.

这可能是一个很好的机会来指出“const正确性”是一件“好事”。无论何时何地,您都可以使用“const”关键字来保护您的代码,不受“放松”调用者或程序员的影响,当指针开始发挥作用时,这些调用通常是最“放松”的。

Enough melodrama, here is what one can achieve when adorning pointers with "const". (Note: One has to read pointer declarations right-to-left.) Here are the 3 different ways to protect yourself when playing with pointers :

足够多的情节剧,以下是用“const”装饰指针时可以达到的效果。(注意:必须读取右到左的指针声明。)这里有三种不同的方法来保护你自己玩指针:

const DBJ* p means "p points to a DBJ that is const" 

— that is, the DBJ object can't be changed via p.

-即DBJ对象不能通过p进行修改。

DBJ* const p means "p is a const pointer to a DBJ" 

— that is, you can change the DBJ object via p, but you can't change the pointer p itself.

-也就是说,你可以通过p改变DBJ对象,但是你不能改变指针p本身。

const DBJ* const p means "p is a const pointer to a const DBJ" 

— that is, you can't change the pointer p itself, nor can you change the DBJ object via p.

-即不能改变指针p本身,也不能通过p改变DBJ对象。

The errors related to attempted const-ant mutations are caught at compile time. There is no runtime space or speed penalty for const.

在编译时捕获与尝试const-ant突变相关的错误。没有运行时空间或速度损失为const。

(Assumption is you are using C++ compiler, of course ?)

(当然,假设您使用的是c++编译器?)

--DBJ

——日本