关于指针:C中的数组增量运算符

Array increment operator in C

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

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

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

error: lvalue required as increment operand


第1部分:

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

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

6.3.2.1 Lvalues, arrays, and function designators

724 A modifiable lvalue is an lvalue that does not have array type,
does not have an incomplete type, does not have a const-qualified
type, and if it is a structure or union, does not have any member
(including, recursively, any member or element of all contained
aggregates or unions) with a const-qualified type.

729 Except when it is the operand of the sizeof operator or the unary & operator,
or is a string literal used to initialize an array, an expression that
has type"array of type" is converted to an expression with type
"pointer to type" that points to the initial element of the array
object and is not an lvalue.

第2部分:

请注意,大多数表达式中的数组名称的第一个元素的地址都会衰减(请阅读某些例外,其中数组名称不会衰减为指向第一个元素的指针?可以通过@H 2 CO 3 可靠地回答)。

当您执行a + 2时,其结果是第三个元素的地址(或元素在索引2处的地址),因此a + 2&a[2]相同,这是地址不是索引处的值。

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

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

要打印值,您需要防御运算符*,如下所示:

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

第三部分:

suppose a is stored at location 2010, Is the output of first printf function 2012?

不,指针算法与整数算法不同。众所周知,在大多数表达式中,数组名称都会衰减为第一个元素的地址,因此当您执行a + 2时,该值就是索引为2的第三个元素的地址。因此,假设您系统中的int大小为4个字节,则根据a地址值为2010的假设,a + 2 stat指向位置2018。

要理解,请阅读10.2指针和数组;指针算术和指针算术。


1
2
3
4
5
6
int a[4]={1,3,5,6};

printf("%d\
"
,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.


首先,该程序会调用未定义的行为,而我不感到沮丧,因为答案如此之多,没有一个人提到这一点。在两个printf调用中,参数都是一个指针,但您将格式指定为期望的%d,而int则应为%p。 7.19.6.1部分中的C99草案标准printf部分针对格式字符串第9段引用的fprintf函数表示:

If a conversion specification is invalid, the behavior is undefined.[...]

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

The operand of the postfix increment or decrement operator shall have qualified or
unqualified real or pointer type and shall be a modifiable lvalue.

我们可以从set 6.3.2.1值,数组和函数指示符中看到,第1段说:

[...]A modifiable lvalue is an lvalue that does not have array type[...]


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


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

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

阵列内存地址保持不变,因此无法更改。这就是您在++语句中所做的。所以编译器会抛出错误。


a不是int类型的变量,它是指向整数的指针,因此要打印它,需要先取消引用它

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