假设我们有一个这样的程序

int main() {
    int array[3] = { 1, 2, 3 };
    int* ptr = array; // <--- Didn't have to use a "&"
    printf("%d\n", *array);
    printf("%d\n", *ptr);
    
    return 0;
}

我们预计将获得:

1
1

我的问题是

  1. 我读到了here,"数组"不是"左值".这是什么意思?
  2. "数组"只是一个内存块的名称吗?如果是,那么块的地址存储在哪里?int* ptr = array意味着"数组"块的地址必须存储在"数组"中,对吗?
  3. 它和这样的东西有什么不同?"点"不也是内存块的名称吗?
struct Point { int x; int y; };
int main() {
    struct Point point = { 1, 2 };
    struct Point* ptr = &point; // <--- Have to use a "&"
    printf("%d\n", point.x);
    printf("%d\n", ptr->x);

    return 0;
}

推荐答案

左值是不同于void的对象类型的表达式,其潜在地指定对象(可能存储值的内存块),使得该对象可以被读取或修改.LValue可以包括像x这样的变量名、像a[i]这样的数组下标表达式、像foo.bar这样的成员 Select 表达式、像*p这样的指针取消引用等等.一个很好的经验法则是,如果它可以成为=运算符的目标,那么它就是一个左值.

数组很奇怪.数组表达式是一个左值,但它是non-modifiable个左值;它指定一个对象,但它不能是赋值的目标.当您在C中声明一个数组时,如

int a[N];

您在内存中得到的内容如下所示:

   +---+
a: |   | a[0]
   +---+
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...

没有独立于单个数组元素的object,a;没有什么可以赋值的,名为ato.a表示整个数组,但C没有定义=运算符来处理整个array.

简要历史教训-C派生自一种名为B的早期语言,当您在B中声明一个数组时:

auto a[N];

你得到的是这样的东西:

   +---+
a: |   | -------------+
   +---+              |
    ...               |
   +---+              |
   |   | a[0] <-------+
   +---+
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...

在B,a,was中,存储到数组第一个元素的偏移量的单独对象.数组下标操作a[i]definedAS *(a + i)-给定存储在a中的起始地址,从该地址偏移i个字1并且解除对结果的引用.

在设计C时,Ritchie希望保留B的数组行为(a[i] == *(a + i)),但他不想保留行为所需的显式指针.相反,他创建了一条规则,即只要数组表达式不是sizeof_Alignof或一元&运算符的操作数,它就会从"T的N元素数组"类型转换为"指向T的指针",并且表达式的值是第一个元素的地址.

表达式a[i] = *(a + i)的工作方式与它在B中的工作方式相同,但a中第一个元素的地址不是storing,而是我们需要的地址(这是在转换期间完成的,而不是运行时).但这意味着您也可以将[]下标运算符与指针一起使用,因此ptr[i]也可以做同样的事情:

   +---+                            +---+
a: |   | a[0] (ptr[0]) <------ ptr: |   |
   +---+                            +---+
   |   | a[1] (ptr[1])
   +---+
   |   | a[2] (ptr[2])
   +---+
    ...

这就是为什么a不能成为赋值的目标--在大多数情况下,它会"衰减"到一个等于&a[0]的指针值,而values不能成为赋值的目标.

您不能更改某物的地址-您只能更改存储在给定地址的值.


  1. B是一种无类型的语言--一切都以单词的形式存储.

C++相关问答推荐

librsvg rsvg_handle_get_dimensions获取像素大小与浏览器中的渲染大小没有不同

C如何显示字符串数组中的第一个字母

了解一些CLIPS原语数据类型

如何将已分配的数组(运行时已知的大小)放入 struct 中?

测量ARM MCU中断延迟的问题

Clang:如何强制运行时错误的崩溃/异常由于-fsanitize=undefined

S将C语言宏定义为自身的目的是什么?(在glibc标题中看到)

Boyer Moore算法的简单版本中的未定义行为

编译器如何处理具有更复杂值的枚举?

如何在C-函数中混合使用C代码和ASM?

仅从限制指针参数声明推断非混叠

收到不兼容的指针类型警告,因为函数的返回不是空*,而是 struct 指针

Zlib:解压缩大文件导致";无效代码长度设置";错误

Realloc():中止的下一个大小无效(核心转储)

是否有单独的缓冲区用于读写库调用?

为什么会出现此错误?二进制表达式的操作数无效

C struct 中的冒泡排序

在C中定义函数指针?

如何在 C 中的 Postgres 函数的表中 for 循环

c 中符号表缺少项目