数组和衰减指针之间的差异

时间:2021-02-02 02:38:31

标签: arrays c pointers assembly

在低级别上,以下是我在查看一维数组和引用相当于一维数组的指针时发现的差异列表。这是显示我发现的差异的 Compiler Explorer 以及下面的代码:

#include <stdio.h>

void function(void) {

    // 1. array has elements stored in contiguous-memory
    //    whereas ptr is single 8-byte memory address value
    int nums[] = {1,2,3};
    int* ptr_nums = &nums[0];

    // 2. compile-time sizeof is different
    //    ptr will always be 8 bytes, arr = unit_size * size
    printf("Sizeof: %zu", sizeof(nums));
    printf("Sizeof: %zu", sizeof(ptr_nums));

    // 3. assignment-to-ptr is valid, but not array
    //    by extension, arithmetic on ptr is allowed, not array
    // nums += 1;   // invalid
    ptr_nums = ptr_nums + 2;

    // 4. string-literal initialization is 'literal' rodata value
    //    for pointer, but contiguous chars in memory for array
    char name[] = "ABC"; // something like: mov $6513249, -24(%rbp)
    char* name_ptr = &name[0]; // will not create string literal
    char* name_ptr2 = "QCI"; // pointer to rodata string literal

    // 5. address-of operator
    // &array returns address of first element
    // &ptr return the address of pointer
    // (which *would not* be the same as the first element of the array if it pointed to that)
    printf("%zd", &nums);
    printf("%zd", &ptr_nums);

}

我可能遗漏了其他任何差异吗?

1 个答案:

答案 0 :(得分:3)

我对这个问题的目的感到困惑。这就像问“int 和 struct 之间有什么区别”——似乎完全是武断的,而且这个答案几乎没有用。数组不是指针。就这样。衰减不会以某种方式将它们不可分割地联系起来,它只是一种方便:它只是让您使用数组的名称代替指向数组第一个元素的指针,在许多适合指针的上下文中。

显然,这样一个“腐烂”的指针不是左值,所以你不能修改它:它是幻影。您的问题似乎更多是关于“左值和右值有何不同,我怎么知道”-您已经清楚地回答了这个问题。尝试 array += 1 并看到它失败等同于尝试 5 += 1。你不能指望其他任何东西,这毫无意义。在 C 中,数组不是左值,它是一种混蛋,因为一旦将其纳入范围,就不能使用它太多:只有数组本身的 sizeof& .对于其他一切,它会衰减为右值指针。注意:不是 to 右值的指针,因为你不能有一个。指针本身是一个右值。例如。 &(foo[1]) 首先衰减数组,因为它没有其他用途,然后像 foo 是一个指针一样进行指针算术。右值是不可变的,没有存储,即。你不能拿他们的地址等等。

再说一次:数组不是右值。数组是一个带有存储的值,但实际上可以对其进行操作的语法很少。当您尝试将数组用作指针时,C 会帮助并衰减数组,但该指针不作为可以更改的左值存在。它只是一个动态合成的右值,就像整数文字动态合成右值一样:您可以使用它们,但仅限于可以使用右值的范围。

rvalues 和 lvalues 之间的这种根本区别是语言的基础之一,如果不牢牢绝对地掌握这个概念,就很难理解 C 的含义

更令人困惑的是,数组定义语法并不总是定义数组。例如:

#include <assert.h>

void foo(int notAnArray[10]) {
  int anArray[10];
  assert(sizeof(notAnArray) != sizeof(anArray));
}

void bar(int *notAnArray) {
  int anArray[10];
  assert(sizeof(notAnArray) != sizeof(anArray));
}

C 的语义规定 foobar 是相同的(除了它们的名称):两者只是具有相同含义的不同语法。更糟糕的是,在某些情况下,第一种语法可能有一些自我记录的用途,即使它在其他方面完全是疯子。

相关问题