字符数组与char指针在C中有什么区别?

我试图理解C语言中的指针,但我目前对以下内容感到困惑:

  • char *p = "hello"
    

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

  • char p[] = "hello"
    

    这是一个存储hello的数组。

  • 将这两个变量传递给这个函数时有什么区别?

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

    char*char[]是不同的类型,但在所有情况下都不是很明显。 这是因为数组衰减为指针,这意味着如果提供了char[]类型的表达式,其中一个类型为char* ,编译器会自动将该数组转换为指向其第一个元素的指针。

    你的示例函数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("%zun", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
        printf("%zun", 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("%zun", strlen(p)); // => 5
        printf("%zun", strlen(q)); // => 5
    
        return 0;
    }
    

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

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

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

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

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

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

  • 作为char数组的初始值设定项,如char a []的声明中那样,它指定了该数组中字符的初始值(必要时还指定其大小)。
  • 其他任何地方,它都会变成一个未命名的静态字符数组,并且这个未命名的数组可能存储在只读存储器中,因此无法修改。 在表达式上下文中,数组一次转换为一个指针(参见第6节),所以第二个声明将p初始化为指向未命名数组的第一个元素。
  • 一些编译器有一个开关控制字符串文字是否可写(用于编译旧代码),有些编译器可能有选项可以使字符串文字在形式上被当作是const char数组(为了更好地捕获错误)。

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

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

    ISO Sec。 6.1.4,Sec。 6.5.7

    基本原理 3.1.4

    H&S Sec。 2.7.4第31-2页


    C99 N1256草案

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

  • 初始化char[]

    char c[] = "abc";      
    

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

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

    所以这只是一个捷径:

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

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

  • 其他地方:它会产生:

  • 无名
  • char数组在C和C ++中字符串文字的类型是什么?
  • 与静态存储
  • 如果修改则给予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“字符串文字”中有记录

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

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

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

    例8:声明

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

    定义“普通”字符数组对象st其元素用字符串文字初始化。

    这个声明是相同的

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

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

    char *p = "abc";
    

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

    GCC 4.8 x86-64 ELF实现

    程序:

    #include <stdio.h>
    
    int main() {
        char *s = "abc";
        printf("%sn", 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
    
    链接地址: http://www.djcxy.com/p/79203.html

    上一篇: What is the difference between char array vs char pointer in C?

    下一篇: C++ Compare first and second element for all pairs in container