【问题标题】:In C, if this isn't an address constant, what is it?在 C 中,如果这不是地址常量,它是什么?
【发布时间】:2010-09-26 16:48:38
【问题描述】:

如果numbers 不是地址常量,那么在下面的声明中究竟是什么?

int main() {
    int numbers[3] = {1,2,3};
    return 0;
}

反汇编程序显示 1、2 和 3 被动态放置在本地堆栈空间中,而不是整个数组被视为常量。因此,{1,2,3} 没有静态存储持续时间,因此根据 C99 规范,numbers 不是地址常量。

C99,第 6.6.9 节:“地址常量是空指针、指向指定静态存储持续时间对象的左值的指针或指向函数指示符的指针……”

但是,在声明后添加行 numbers++ 会导致 GCC 4.1.2 中出现以下编译错误:

error: invalid lvalue in increment

所以它是常数,但不是地址常数。有人知道C99(或类似)中这种常量的正式名称吗?

【问题讨论】:

    标签: c c99


    【解决方案1】:

    从术语上讲,您似乎在发明一些实际上并不存在的东西。

    numbers 是一个数组。它是int[3] 类型的自动对象。它不能用于在 C 中形成地址常量,因为在 C 中地址常量需要具有静态存储持续时间的对象。

    如果您的numbers 声明为静态存储持续时间,则应用于numbers 的数组到指针转换的结果将是一个地址常量。在这种情况下,numbers + 1&numbers[2] 也将是地址常量。

    您注意到{ 1, 2, 3 } 没有静态存储持续时间是对的。事实上,它根本没有存储期限。它不是一个对象,而只是一个称为聚合初始化器的语法糖。如果您希望它成为匿名对象,则必须使用 复合文字 语法:(int[]) { 1, 2, 3 },但无论如何它在上述上下文中不起作用。

    numbers++ 不会编译,因为数组到指针转换的结果不是左值。您不能将++ 应用于非左值。某事是否恒定无关紧要。

    您似乎得出了一个奇怪的结论,即如果您无法修改它,则它必须是一个常数。这是完全不正确的。在 C 术语中,constant 的属性与 modifiable 的属性几乎没有关系。术语常量指的是在编译时已知的值。在编译时未知的值永远不会被称为 constants,即使它们不可修改。这是一个例子:自动数组的地址在编译时是未知的,因此即使该地址不可修改,它仍然不是地址常量

    【讨论】:

    • 是的。数组到指针的转换不是左值这一事实是我感兴趣的。但是,我期望在语义上比“一个数组隐式地像一个指针,除了不是左值”答案在规范上更清晰显然给了。
    • @Carlo:这不是标准所说的。它说数组是“聚合类型”(就像结构类型一样) - 数组是“具有特定成员对象类型的连续分配的非空对象集”
    • @Carlo:数组通常不是“像指针”。数组仅在一组定义明确的特定上下文中“就像一个指针”。运算符 ++ 恰好是其中一种上下文。
    【解决方案2】:

    numbers 是函数main 的一个非常量、自动的数组变量。

    因为它是自动的和非常量的,它不能有静态存储。

    因为它是一个数组变量(不是你会注意到一个指针)它不能递增。

    注意你可以

    int main() {
        int numbers[3] = {1,2,3};
        int *n = numbers+1;
        n++;
        return 0;
    }
    

    【讨论】:

    • 听起来很合理,但术语“数组变量”根本不在 C99 规范中。这是准确的区别吗?
    • @Carlo:我无法按照标准回答你。只是关于语义。您不能在numbers 上使用后增量运算符,因为它是一个数组类型的变量,并且该操作未在数组上定义;并且它的存储不能存在于数据段(或任何其他部署静态存储的机制)中,因为变量是非常量和自动的。
    • @Carlo:呃……不,你的最终结论是绝对错误的。数组一个左值。数组到指针转换的结果不是左值,而是数组本身。比如声明double d,变量d是左值,但(int) d表达式的结果不是左值。数组也会发生同样的事情。 numbers 是左值,但(int *) numbers 不是左值,除了在这种情况下,(int *) 转换是隐式执行的。
    • 数组是左值,但不是可修改左值。 (6.3.2.1) 并且后缀增量的操作数必须是 modifiable 左值。 (6.5.2.4)
    • @Carlo:在表达式&numbers 中,numbers 被用作左值。我相信这是唯一可以证明数组“左值性”的情况。
    【解决方案3】:

    数组不是指针;请参阅comp.lang.c FAQ 的第 6 节。

    【讨论】:

      【解决方案4】:

      它不是地址常量的原因是因为它是堆栈上的地址,并且该位置取决于调用堆栈中它上面的内容。如果在运行时才知道它的值,那么它就不可能是一个常数。

      它也不是一个变量,这就是为什么你不能增加它。它只是一个存储位置的名称,恰好是基指针上的偏移量。 numbers[0] 是一个有效的左值,但numbers 不是。

      【讨论】:

      • 自动变量通常分配在堆栈上,但这是一个实现细节。一些嵌入式系统在链接时分配自动变量(禁止递归,或者可能递归调用会将变量复制到伪堆栈)。不幸的是,即使自动变量位于链接器确定的常量地址,AFAICT 仍然无法在 C 中利用这一事实。
      【解决方案5】:

      numbers 无法更改,因为它必须始终指向数组中的第一个元素。

      【讨论】:

      • 对,我知道这是一个常数。但是这种常量的正式名称是什么?
      • numbers 无法更改,因为它是一个数组,而不是一个指针。
      猜你喜欢
      • 2014-01-20
      • 1970-01-01
      • 1970-01-01
      • 2023-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多