char数组与char中的C指针

我想了解C中的指针,但我目前与以下内容混淆:

  • char *p = "hello" 

    这是一个字符指针,指向字符数组,从h开始。

  •  char p[] = "hello" 

    这是一个存储hello的数组。

将这两个variables传递给这个函数有什么区别?

 void printSomething(char *p) { printf("p: %s",p); } 

我不明白他们有什么不同。

char*char[] 是不同的types ,但在所有情况下都不是立即可见的。 这是因为数组衰减成指针 ,这意味着如果提供了char*types的expression式,那么在期望char*types之一时,编译器会自动将该数组转换为指向其第一个元素的指针。

你的示例函数printSomething需要一个指针,所以如果你试图像这样传递一个数组:

 char s[10] = "hello"; printSomething(s); 

编译器假装你写这个:

 char s[10] = "hello"; printSomething(&s[0]); 

让我们来看看:

 #include <stdio.h> #include <string.h> int main() { char *p = "hello"; char q[] = "hello"; // no need to count this printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64 printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both // size_t strlen(const char *s) and we don't get any warnings here: printf("%zu\n", strlen(p)); // => 5 printf("%zu\n", strlen(q)); // => 5 return 0; } 

foo *和foo []是不同的types,编译器对它们的处理方式不同(指针=地址+表示指针的types,数组=指针+数组的可选长度,如果已知,例如,数组是静态分配的),细节可以在标准中find。 而在运行级别,它们之间没有任何区别(在汇编程序中,差不多,见下文)。

另外,在C FAQ中还有一个相关的问题 :

:这些初始化有什么区别?

 char a[] = "string literal"; char *p = "string literal"; 

如果我尝试为p [i]分配一个新的值,我的程序崩溃。

:string文字(C源中双引号string的forms术语)可以用两种稍微不同的方式使用:

  1. 作为char数组的初始化方法,如char a []的声明中那样,它指定了该数组中字符的初始值(必要时还指定其大小)。
  2. 其他任何地方,它变成一个无名的,静态的字符数组,这个未命名的数组可以存储在只读存储器中,因此不一定会被修改。 在一个expression式上下文中,数组一次被转换为一个指针(见第6节),所以第二个声明将p初始化为指向未命名数组的第一个元素。

有些编译器有一个控制string文字是否可写(用于编译旧代码)的开关,有些编译器可能会有选项使string文字在forms上被当作常量字符数组(为了更好地捕获错误)。

另见问题1.31,6.1,6.2,6.8和11.8b。

参考文献:K&R2 Sec。 5.5 p。 104

ISO Sec。 6.1.4, 6.5.7

基本原理 3.1.4

H&S Sec。 2.7.4第31-2页

我不明白他们有什么不同。

C99 N1256草案

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

  1. 初始化char[]

     char c[] = "abc"; 

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

    字符types的数组可以由string文字初始化,可选地用大括号括起来。 string文字的连续字符(包括终止空字符,如果有空间或数组未知大小)初始化数组的元素。

    所以这只是一个捷径:

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

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

  2. 其他地方:它会产生:

    • 无名
    • char数组什么是C和C ++中的string文字的types?
    • 与静态存储
    • 如果修改则给予UB

    所以当你写:

     char *c = "abc"; 

    这类似于:

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

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

    那么如果你修改c[0] ,你还要修改__unnamed ,这是UB。

    这在6.4.5“string文字”中有logging

    5在翻译阶段7,将string或文字产生的每个多字节字符序列附加一个字节或值为零的代码。 然后使用多字节字符序列来初始化静态存储持续时间和长度的数组,以便足以包含该序列。 对于string文字,数组元素的types为char,并且用多字节字符序列的单个字节进行初始化[…]

    6这些数组是否是不同的,只要它们的元素具有适当的值。 如果程序试图修改这样一个数组,行为是不确定的。

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

例8:声明

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

定义“简单的”字符数组对象st其元素用string文字初始化。

这个声明是一样的

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

数组的内容是可修改的。 另一方面,声明

 char *p = "abc"; 

定义p的types为“指向char的指针”并将其初始化为指向长度为4的types为“char的数组”的对象,其元素用string文字初始化。 如果尝试使用p来修改数组的内容,则行为是不确定的。

GCC 4.8 x86-64 ELF实现

程序:

 #include <stdio.h> int main() { 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 

结论:GCC在.rodata节中存储char*而不是.text

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

  char s[] = "abc"; 

我们获得:

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

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

但是,请注意,默认链接描述文件将.rodata.text放在同一个段中,该段执行但没有写入权限。 这可以观察到:

 readelf -l a.out 

其中包含:

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

你不能改变string常量的内容,这是第一个p指向的内容。 第二个p是一个用string常量初始化的数组,你可以改变它的内容。

对于这样的情况,效果是一样的:你最终将第一个字符的地址传递给一个string。

声明显然不尽相同。

以下为string和字符指针设置内存,然后初始化指针指向string中的第一个字符。

 char *p = "hello"; 

虽然以下内容仅用于string。 所以它实际上可以使用更less的内存。

 char p[10] = "hello"; 

据我所知,一个数组实际上是一组指针。 例如

 p[1]== *(&p+1) 

是一个真实的陈述

char p[3] = "hello" ? 应该是char p[6] = "hello"记得在C中“string”的末尾有一个'\ 0'字符

无论如何,C中的数组只是一个指向内存中调整对象的第一个对象的指针。 唯一不同的是语义。 同时你可以改变指针的值来指向内存中一个不同位置的数组,创build后总会指向同一个位置。
也使用数组时,“新”和“删除”是自动完成的。