Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
char s[]和char*s之间有什么区别?_C_String_Char_Constants - Fatal编程技术网

char s[]和char*s之间有什么区别?

char s[]和char*s之间有什么区别?,c,string,char,constants,C,String,Char,Constants,在C中,可以在如下声明中使用字符串文字: char s[] = "hello"; char *s = "hello"; 或者像这样: char s[] = "hello"; char *s = "hello"; 那么有什么区别呢?我想知道在编译时和运行时,在存储持续时间方面实际发生了什么 这里的区别是 char *s = "Hello world"; 将“Hello world”放在内存的只读部分,并将s设为指向该内存的指针,从而使该内存上的任何写入操作都是非法的 在这样做时: cha

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

char s[] = "hello";
char *s = "hello";
或者像这样:

char s[] = "hello";
char *s = "hello";

那么有什么区别呢?我想知道在编译时和运行时,在存储持续时间方面实际发生了什么

这里的区别是

char *s = "Hello world";
“Hello world”
放在内存的只读部分,并将
s
设为指向该内存的指针,从而使该内存上的任何写入操作都是非法的

在这样做时:

char s[] = "Hello world";
将文本字符串置于只读内存中,并将字符串复制到堆栈上新分配的内存中。从而使

s[0] = 'J';
合法的

s
声明为
char
的数组,该数组的长度足以容纳初始值设定项(5+1
char
s),并通过将给定字符串文本的成员复制到数组中来初始化数组

char *s = "hello";
s
声明为指向一个或多个(在本例中为多个)
char
s的指针,并将其直接指向包含文本
“hello”
的固定(只读)位置。此声明:

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

另一方面,本宣言:

char s[] = "hello";
char *s ="hello";
创建两个对象:

  • 一个由6个
    字符组成的只读数组,其中包含
    'h','e','l','l','o','\0'
    ,该数组没有名称且具有静态存储持续时间(意味着它在程序的整个生命周期内都有效);及
  • 指向char的指针类型变量,称为
    s
    ,该变量使用该未命名只读数组中第一个字符的位置进行初始化

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

首先,在函数参数中,它们完全等效:

void foo(char *x);
void foo(char x[]); // exactly the same in all respects
在其他上下文中,
char*
分配指针,而
char[]
分配数组。你会问,在前一种情况下,字符串放在哪里?编译器秘密地分配一个静态匿名数组来保存字符串文本。因此:

char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;
请注意,您决不能试图通过此指针修改此匿名数组的内容;影响未定义(通常意味着崩溃):

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

char x[] = "Foo";
x[1] = 'O'; // No problem.
但是,数组只在其包含范围内存在,因此如果在函数中执行此操作,请不要返回或泄漏指向此数组的指针-使用
strdup()
或类似工具制作副本。如果数组是在全局范围内分配的,当然没有问题

char s[] = "Hello world";
这里,
s
是一个字符数组,如果我们愿意,可以覆盖它

char *s = "hello";
字符串文字用于在内存中该指针
s
指向的某个位置创建这些字符块。在这里,我们可以通过更改它所指向的对象来重新指定该对象,但只要它指向一个字符串文字,它所指向的字符块就不能更改。

在以下情况下:

char *x = "fred";
char x[] = "fred";
x是一个--可以分配给。但在以下情况下:

char *x = "fred";
char x[] = "fred";
x不是左值,它是右值——您不能分配给它。

给定声明

char *s0 = "hello world";
char s1[] = "hello world";
假设以下假设的内存映射:

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 将
s0
定义为指向具有自动存储持续时间的
char
的指针(意味着变量
s0
仅存在于声明它的范围内),并将字符串文本的地址(
0x00008000
在本例中)复制到它。请注意,由于
s0
指向字符串文字,因此不应将其用作任何试图修改它的函数的参数(例如,
strtok()
strcat()
strcpy()
,等等)

线路

char *s0 = "hello world";
char s1[] = "hello world";
s1
定义为具有自动存储持续时间的
char
的12元素数组(长度取自字符串文字),并将文字内容复制到数组中。从内存映射中可以看到,我们有两个字符串的副本
“hello world”
;不同之处在于,您可以修改
s1
中包含的字符串

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

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

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

这可能是一个很好的机会来指出“常量正确性”是一件“好事”。无论何时何地,只要有可能,使用“const”关键字保护您的代码,使其不受“放松”的调用方或程序员的影响,这些调用方或程序员在指针起作用时通常是最“放松”的

够戏剧性的了,下面是用“const”修饰指针时可以达到的效果。 (注意:必须从右向左读取指针声明。) 以下是玩指针时保护自己的3种不同方法:

const DBJ* p means "p points to a DBJ that is const" 
-也就是说,DBJ对象不能通过p进行更改

DBJ* const p means "p is a const pointer to a DBJ" 
-也就是说,可以通过p更改DBJ对象,但不能更改
char *c = "abc";
/* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
char s[] = "abc", t[3] = "abc";
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
char *p = "abc";
#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata
readelf -l a.out
 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata
 char s[] = "abc";
17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)
printf("%c", x[1]);     //Prints r
printf("%c", *(x + 1)); //Prints r
*(x + 1) = 'a';
char *str = "Hello";
char str[] = "Hello";
means str[0] = 'M';
char *s1 = "Hello world"; // Points to fixed character string which is not allowed to modify
char s2[] = "Hello world"; // As good as fixed array of characters in string so allowed to modify

// s1[0] = 'J'; // Illegal
s2[0] = 'J'; // Legal
printf("hello" + 2); //llo
char a[] = "hello" + 2; //error