【问题标题】:What the memory difference between char *array and char array[]? [duplicate]char *array 和 char array[] 之间的内存区别是什么? [复制]
【发布时间】:2021-08-24 04:35:12
【问题描述】:

当我执行下一个代码时

int main()
{
    char tmp[] = "hello";
    printf("%lp, %lp\n", tmp, &tmp);
    return 0;
}

我得到了相同的地址。但是对于下一个代码,它们会有所不同

int main()
{
    char *tmp = "hello";
    printf("%lp, %lp\n", tmp, &tmp);
    return 0;
}

你能解释一下这些例子之间的记忆差异吗?

【问题讨论】:

  • char tmp[] = "hello" 是一个由 6 个字符组成的数组,初始化为 "hello\0"(它具有自动存储持续时间并驻留在程序堆栈中)。 char *tmp = "hello"; 是一个指针,使用位于只读内存中的字符串文字 "hello\0" 的地址初始化(通常在可执行文件的 .rodata 部分中)。 (只读,除了少数非标准实现)数组在访问时被转换为指向其第一个元素的指针。
  • @David C. Rankin Re "readonly on all but a few non-standard implementations",我怀疑 C 是否要求机器具有虚拟内存才能拥有标准执行。曾经应该始终认为内存是只读的,但我质疑内存必须是只读的才能使实现成为标准的说法。
  • @ikegami 我承认这一点。该标准不需要符合标准的实现来在只读内存中创建字符串文字。我要说的就是大多数。
  • 至少 C 标准规定修改字符串文字是 undefined behaviour
  • 虽然在 C 语言中是合法的,但您不应该将字符串文字分配给非 const char 指针,始终使用 char const* ptr = "some literal"; - 否则您几乎肯定 修改如上所述,在未来的某个时间点,即UB。能够将不可变的文字分配给 char* 指针是 C 的最初几天的遗产,当时 const 尚不存在。

标签: arrays c pointers memory


【解决方案1】:

char tmp[] = "hello"; 是一个由 6 个字符组成的数组,初始化为 "hello\0"(它具有自动存储持续时间并驻留在程序堆栈中)。

char *tmp = "hello"; 是一个指向char 的指针,它使用驻留在只读内存中的字符串文字"hello\0" 的地址进行初始化(通常在可执行文件的.rodata 部分中,除了少数实现之外的所有实现都是只读的)。

当您拥有char tmp[] = "hello"; 时,如上所述,在访问时,数组将转换为指向tmp 的第一个元素的指针。它的类型为char *。当您获取tmp(例如&tmp)的地址时,它将解析为相同的地址,但具有完全不同的类型。这将是一个 指向数组的指针 char[6]。正式类型是char (*)[6]。而且由于类型控制指针算法,当你推进指针时,用不同的类型迭代会产生不同的偏移量。前进tmp 将前进到下一个char。以tmp 的地址前进将前进到下一个 6 字符数组的开头。

当你有char *tmp = "hello"; 时,你就有一个指向char 的指针。当您获取地址时,结果是 pointer-to-pointer-to char。正式类型是char **,反映了两个间接级别。前进tmp 前进到下一个char。以tmp 的地址前进,前进到下一个指针。

【讨论】:

  • char tmp[]: Advance tmp 是不吉利的措辞,因为数组不能递增(如++tmp)。我不会描述 'string literal "hello\0"' 因为那将暗示带有 两个 尾随空字符的文字。
  • 是的,我指的是访问产生的指针。显然,您不能迭代数组本身。在数组的情况下,意图是 char *p = tmp;char (*p)[6] = &tmp;。感谢您指出这一点。
【解决方案2】:

char tmp[] = "hello";

您正在留出一个足够大的char数组 来存储字符串"hello" 并将字符串的内容复制到该数组中,这样您就可以在内存中得到它:

     +–––+
tmp: |'h'| tmp[0]
     +–––+
     |'e'| tmp[1]
     +–––+
     |'l'| tmp[2]
     +–––+
     |'l'| tmp[3]
     +–––+
     |'o'| tmp[4]
     +–––+
     | 0 | tmp[5]
     +–––+

没有与数组元素本身分开的tmp对象,因此数组的地址(tmp)与其第一个元素的地址(tmp[0])相同。

char *tmp = "hello";

您正在创建一个指向char指针,并使用字符串文字 "hello" 中第一个字符的地址 对其进行初始化,这样你就可以在内存中得到这个:

     +–––+       +–––+
tmp: |   | ––––> |'h'| tmp[0]
     +–––+       +–––+
                 |'e'| tmp[1]
                 +–––+
                 |'l'| tmp[2]
                 +–––+
                 |'l'| tmp[3]
                 +–––+
                 |'o'| tmp[4]
                 +–––+
                 | 0 | tmp[5]
                 +–––+

在这种情况下tmp与数组元素分开的对象,所以tmp的地址与tmp[0]的地址不同。

【讨论】:

    【解决方案3】:
    char a[] = "hello";
    

    char *a = "hello";
    

    存放在不同的地方。

    char a[] = "你好"

    在这种情况下,a 成为一个初始化为“hello\0”的 6 个字符的数组(存储在堆栈中)。同理:

    char a[6];
    a[0] = 'h';
    a[1] = 'e';
    a[2] = 'l';
    a[3] = 'l';
    a[4] = 'o';
    a[5] = '\0';
    

    char *a = "你好"

    检查组件(这不是所有组件,只是重要的部分):

        .file   "so.c"
        .text
        .section    .rodata
    .LC0:
        .string "hello" ////Look at this part
        .text
        .globl  main
        .type   main, @function
    main:
    .LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movq    $.LC0, -8(%rbp)
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    

    .section    .rodata
    .LC0:
        .string "hello"
    

    这是存储字符串的地方。 char a[] 存储在堆栈中,而 char *a 存储在编译器喜欢的任何位置。一般在rodata中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      • 1970-01-01
      • 2011-07-20
      • 2012-08-14
      • 2013-02-19
      • 1970-01-01
      相关资源
      最近更新 更多