【问题标题】:C - what is array name when not converted to pointer of its type?C - 未转换为其类型的指针时,数组名称是什么?
【发布时间】:2019-08-27 22:49:17
【问题描述】:

所以有一段时间我对数组名称和指针感到困惑。

我们声明int a[10]; 在路上的某个地方也有a&a

所以我知道语法是如何工作的。 a 是数组名称。当它不用作sizeof&等的操作数时,它将被转换或“衰减”,因此它返回一个指向包含数组第一个元素地址的整数的指针。

如果数组名用作sizeof& 的操作数,则其类型为int (*)[10]。所以我猜类型是不同的,因为“衰减”不会发生。

但我仍然不明白&a 是如何工作的。我的理解是它给了我whatever it was before the "decay" happened的地址。所以在指针“衰减”发生之前,它是什么以及编译器如何使用“原始”来评估&a

相比之下,如果我们声明int *p; 后来在代码中的某处有&pp...

在这种情况下,指向整数 p 的指针被赋予一个单独的指针单元及其地址,该地址的值将是我们分配给它的任何地址(或该地址预分配的垃圾值)。

a 在声明为int a[10] 时不会在内存中分配一个单独的指针单元。我听说它在寄存器%ebp 上有一个偏移量。那么编译器在评估&a 时会发生什么?指向整数的指针的“衰减”没有发生,首先没有单独的“指针”。那么编译器将a 识别为什么,当它看到一元& 运算符使用数组名称作为操作数时会做什么?

【问题讨论】:

  • 第一个代码块只能在 C 中编译,因为 C 将所有指针类型视为兼容的,并且数组的地址与第一个元素的地址相同。在 C++ 中,它是指针类型不匹配。
  • 我将删除 c++ 标签
  • @CruzJean a == &a 也是 C 中的指针类型不匹配。
  • @chux 好吧,但据我所知,C 最多只会发出警告。虽然我可能只是习惯了旧版本的 C 编译器。
  • 这是另一个要考虑的例子:如果你有int b[20][10],例如你将b作为函数参数传递,它会衰减为类型int (*)[10]。这是有道理的,因为b[0] 的类型为int [10],所以&b[0] 的类型为int (*)[10]。当然,&b 的类型为 int (*)[20][10]

标签: c arrays pointers type-conversion


【解决方案1】:

给定:

int a[10];

对象a 的类型为int[10]表达式 a,在大多数但不是所有上下文中,“衰减”为指针表达式;该表达式产生一个int* 类型的值,相当于&a[0]

但我仍然不明白&a 是如何工作的。我的理解是它给了我在“衰减”发生之前它是什么的地址。所以在指针的“衰减”发生之前,它是什么以及编译器如何使用评估&a的“原始”?

这不太正确。在&a 中,衰减根本不会发生。 a 的类型为“10 个数组 int”(int[10]),因此 &a 的类型为“指向 10 个数组的指针 int”(int(*)[10])。

这没什么特别的。对于some_type 类型的任何名称foo,表达式&foo 的类型为“指向some_type 的指针”。 (令人困惑的是,这是数组名称​​不表现异常的罕见情况之一。)

最好将“数组”和“指针”这两个词视为形容词而不是名词。因此我们可以有一个数组对象、一个数组表达式、一个数组类型等等——但只是“一个数组”是不明确的。

这个:

int a[10];

定义一个名为a 的数组对象(并分配4 * sizeof (int) 字节来保存它)。没有创建指针对象。您可以通过获取对象的地址或它的任何元素的地址来创建指针 value。这与任何其他类型的对象没有什么不同。定义some_type 类型的对象不会创建some_type* 类型的对象,但您可以通过计算对象的地址来创建some_type* 类型的值。

【讨论】:

    【解决方案2】:

    那么编译器将 a 识别为什么,当它出现时它会做什么? 看到一元 & 运算符使用数组名作为操作数了吗?

    编译器将a 识别为一个10 元素的整数数组,当它看到& 运算符时,它会返回该数组的地址。

    就像它将int i = 3; 视为一个整数,并将&i 视为该整数的地址。

    【讨论】:

    • 我有点过度思考这个问题......实际上是在使用相同的 int x = 5; 示例来考虑 int a[10];
    • 似乎不止一点点。 :) 有时它有助于远离树木并尝试看看森林。
    【解决方案3】:

    关于获取数组的地址:数组本身就是一个对象,因此它既有大小又有地址(尽管获取地址很少有用)。

    将数组转换为指向其第一个元素的指针是一种类型强制。只有当替代方案是编译错误时才会发生。

    例如,您无法将数组与指针进行比较,因此数组(隐式)被强制(强制转换)为int*(与其第一个元素),然后比较指针类型。在 C 中,您可以比较任何指针类型。 C 根本不在乎(尽管它可能会发出警告)。

    正如您所说,就类型而言,这实际上是将int*int(*)[10] 进行比较。这些将必须具有相同的地址(无论键入如何),因为数组直接保存它们的数据。所以数组的地址永远是它的第一个元素的地址。

    但是,获取数组的大小并不是错误,因此sizeof(a) 获取了整个数组的大小,因为不需要强制来使其合法。所以这和sizeof(int[10])是一样的。

    如你所说,你的另一个案例sizeof(&a) 真的是sizeof(int(*)[10])

    【讨论】:

    • 对不起,我猜我的代码块有误导性。我不是真的在问a == &a 会编译吗?但是编译器在遇到&a时在做什么
    • sizeof(++a) (我假设你的意思)是一个错误,因为a 不是左值
    • @MarkBenningfield 不,我的意思是sizeof(+a) 它强制将数组转换为指针。在这种情况下,它通常被称为衰减算子。
    • 好吧,+a 会导致 C 中出现非法操作数错误。
    • @namesake22 我想我已经编辑了答案以更好地涵盖这一点 - 数组是一个对象,因此它既有大小又有地址。
    猜你喜欢
    • 2013-07-04
    • 1970-01-01
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    • 2016-06-02
    • 1970-01-01
    • 2018-02-18
    相关资源
    最近更新 更多