首页 文章

C中的char数组与char指针有什么区别?

提问于
浏览
174

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

char *p = "hello"

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

char p[] = "hello"

这是一个存储hello的数组 .

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

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

7 回答

  • 2

    char*char[] 是不同的类型,但并非在所有情况下都立即显现出来 . 这是因为数组衰减为指针,这意味着如果提供类型 char[] 的表达式,其中类型 char* 之一是预期的,则编译器会自动将数组转换为指向其第一个元素的指针 .

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

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

    编译器假装你写了这个:

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

    让我们来看看:

    #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 []是不同的类型,它们由编译器处理不同(指针=指针类型的地址表示,数组=指针可选的数组长度,如果已知,例如,如果数组是静态分配的),细节可以在标准中找到 . 并且在运行时级别它们之间没有区别(在汇编程序中,好吧,差不多,见下文) .

    此外,C FAQ中有一个相关的question

    问:这些初始化之间有什么区别? 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秒6.1.4,Sec . 6.5.7理由二 . 3.1.4 H&S Sec . 2.7.4 pp.31-2

  • 9

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

    C99 N1256 draft

    字符串文字有两种不同的用法:

    • 初始化 char[]
    char c[] = "abc";
    

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

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

    所以这只是一个捷径:

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

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

    所以当你写:

    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”;
    定义“plain”char数组对象s和t,其元素用字符串文字初始化 . 此声明与char s [] = {'a','b','c','\ 0'}相同,
    t [] = {'a','b','c'};
    数组的内容是可修改的 . 另一方面,声明char * p =“abc”;
    使用“指向char的指针”类型定义p并将其初始化为指向类型为“array of char”且长度为4的对象,其元素使用字符串文字初始化 . 如果尝试使用p修改数组的内容,则行为是未定义 .

    GCC 4.8 x86-64 ELF implementation

    程序:

    #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
    

    结论:GCC将 char* 存储在 .rodata 部分,而不是 .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
    
  • 191

    你不能改变字符串常量的内容,这是第一个 p 指向的内容 . 第二个 p 是一个用字符串常量初始化的数组,您可以更改其内容 .

  • 5

    对于这样的情况,效果是相同的:您最终会在字符串中传递第一个字符的地址 .

    虽然声明显然不一样 .

    下面为字符串和字符指针留出内存,然后初始化指针以指向字符串中的第一个字符 .

    char *p = "hello";
    

    虽然以下内容仅为字符串留出内存 . 所以它实际上可以使用更少的内存 .

    char p[10] = "hello";
    
  • 0

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

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

    是一个真实的陈述

  • 26

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

    无论如何,C中的数组只是指向内存中调整对象的第一个对象的指针 . 唯一不同的是语义 . 虽然您可以将指针的值更改为指向内存中的其他位置,但创建后,数组始终指向同一位置 .
    同样在使用数组时,"new"和"delete"会自动为您完成 .

相关问题