【问题标题】:Array increment operator in CC中的数组增量运算符
【发布时间】:2013-10-08 10:36:51
【问题描述】:

我不明白以下代码的结果:

#include <stdio.h>
#include <conio.h>
int main()
{
   int a[4]={1, 3, 5, 6};
   //suppose a is stored at location 2010
   printf("%d\n", a + 2);
   printf("%d", a++);
   return 0;
}

为什么第二个printf函数会产生以下错误?

error: lvalue required as increment operand

【问题讨论】:

  • This 解释了有关数组的所有内容。如果您真的想了解数组,请通读全文。
  • 您在编译该代码时是否不相信您收到的错误消息,或者您在发布之前没有费心编译它?
  • @user221458 我明白这一点,但问题并没有反映出这一点,您如何提出关于 SO 的问题很重要。
  • @ShafikYaghmour 感谢您提供信息。幸运的是,到目前为止,我提出的两个问题都没有得到差评或关闭。以后提问的时候我会注意的。
  • 下一次,请在您的问题中包含准确的(复制和粘贴)错误消息。事实上,现在更新您的问题以执行此操作是个好主意。

标签: c pointers post-increment


【解决方案1】:

数组的名称是一个常量指针,因此它总是指向该数组的第 0 个元素。它不是一个变量,所以我们也不能为它分配其他地址,我们也不能通过递增或递减来移动它。 因此

a = &var;  /*Illegal*/
a++;       /*Illegal*/
a = a-1;   /*Illegal*/

【讨论】:

    【解决方案2】:

    第 1 部分:

    数组名称是常量(不可修改的左值),您可以为数组名称添加值但不能修改它。

    表达式a + 2 不会修改a 本身,但是当您执行a++ 时,这相当于a = a + 1 尝试修改数组名称--lvalue 错误。第二个 printf 中的表达式 a++ 是错误的 - 语义相位错误的一个示例。阅读以下语言标准:

    6.3.2.1 Lvalues, arrays, and function designators

    724 可修改的左值是没有数组类型的左值, 没有不完整的类型,没有 const 限定 类型,如果是结构或联合,则没有任何成员 (包括,递归地,所有包含的任何成员或元素 聚合或联合)具有 const 限定类型。

    729 除非是 sizeof 运算符或一元 &amp; 运算符的操作数, 或者是一个用于初始化数组的字符串文字,一个表达式 具有类型“类型的数组”被转换为具有类型的表达式 指向数组初始元素的“类型指针” 对象并且不是左值

    第 2 部分:

    注意大多数表达式中的数组名称在第一个元素的地址中衰减(阅读一些exceptions where array name not decaying into a pointer to first element?@H2CO3 巧妙地回答)。

    当您执行a + 2 时,其结果是第三个元素的地址(或索引2 处的元素地址)所以a + 2&amp;a[2] 相同,它是地址而不是索引处的值。

    要打印地址,请使用%p 而不是%d,并将地址类型转换为void*,如下所示:

    printf("address (a + 2) = %p , &a[2] = %p", (void*)(a + 2), (void*)(&a[2]));
    

    要打印值你需要防御操作员*,如下:

    printf("address *(a + 2) = %d , a[2] = %d", *(a + 2), a[2]);   
    

    第 3 部分:

    假设a存储在位置2010,第一个printf函数的输出是2012吗?

    不,指针运算不同于整数运算。正如我们所知,在大多数表达式中,数组名称会衰减为第一个元素的地址所以当您执行a + 2 时,值是索引2 处的第三个元素的地址。因此,假设您的系统中的 int 大小为 4 个字节,那么根据您的假设 a 地址值为 2010,a + 2 stat 指向位置 2018。

    要了解请阅读10.2 Pointers and Arrays; Pointer ArithmeticPointer Arithmetic

    【讨论】:

    • 好的。因此 a++ 将更改存储 a 的初始内存地址,因此不允许这样做。对吗?
    • @user221458 a++ 尝试更改,但这是编译时错误,因为我说过将在语义阶段检测到。
    • @user221458 如果您了解编译器(或计算机科学)学生,那么您应该知道不正确的操作数是语义错误。
    • 我知道。它会产生错误。但是a++的意思是我说的吗?如果不考虑第二个 printf,第一个 printf 的输出会是什么?会是 2012 年吗?
    • 鉴于a是数组对象的名称,a++没有意义
    【解决方案3】:

    首先,这个程序调用undefined behavior,我有点气馁,有这么多答案,没有一个人提到这一点。在您的printf 调用中,您的参数是一个指针,但您将格式指定为预期的%dintshould be %pC99 draft standard 部分 7.19.6.1 中的 C99 draft standard fprintf 函数 printf 部分引用的格式字符串段落 9 说:

    如果转换规范无效,则行为未定义。[...]

    回到你的问题,a++ 表达式会产生错误,因为 后缀增量 要求它的 操作数 是一个可修改的左值6.5.2.4 部分中的标准草案后缀递增和递减运算符1 段说(强调我的):

    后缀递增或递减运算符的操作数应具有限定或 不合格的实数或指针类型,并且应为可修改的左值

    我们可以从集合6.3.2.1 中看到值、数组和函数指示符段落1说:

    [...]可修改的左值是不具有数组类型的左值[...]

    【讨论】:

      【解决方案4】:

      a 不是 int 类型的变量,它是一个指向整数的指针,所以要打印它你需要先取消引用它

      printf("%d\n", *(a + 2));
      

      【讨论】:

        【解决方案5】:

        我认为第一个输出将取决于整数类型在您的计算机中的表示方式。如果单个整数在内存中占用 4 个字节,则输出应为 2018,即 2010+2*4。第二个 printf 可能会导致编译错误。

        【讨论】:

          【解决方案6】:
          int a[4]={1,3,5,6}; 
          
          printf("%d\n",a++); // you should not modify array name
          
           illegal in c
          

          假设pa是整数指针

          A pointer is a variable, so pa=a and pa++ are legal. But an array name is not a variable; constructions like a=pa and a++ are illegal.

          【讨论】:

            【解决方案7】:

            数组内存地址保持不变,所以你不能改变它。这就是您在 ++ 语句中所做的事情。所以编译器会报错。

            【讨论】:

              猜你喜欢
              • 2015-08-10
              • 2015-09-25
              • 1970-01-01
              • 2011-02-16
              • 1970-01-01
              • 2016-03-18
              • 1970-01-01
              • 2012-10-17
              • 1970-01-01
              相关资源
              最近更新 更多