给定表达式中的运算符优先级
Operator precedence in the given expressions
表达式 1:*p++;
其中 p
是一个指向整数的指针。
p
将首先递增,然后由于关联性(从右到左)采用它指向的值。对吗?
表达式 2:a=*p++;
其中 p
是一个指向整数的指针。
先取p
的值,然后先赋值给a
,然后p
因post递增而递增。对吗?
首先,让我告诉你, 结合性 和 计算顺序实际上是相关的 这里。这都是关于 operator precedence。让我们先看看定义。 (强调我的)
Precedence :在数学和计算机编程中,运算顺序(或运算符优先级) 是一组规则,反映了关于 首先执行哪些过程以评估给定数学表达式的约定。
Associativity:在编程语言中,运算符的结合性(或固定性)是属性 确定在没有括号的情况下具有相同优先级的运算符如何分组。
Order of evaluation :任何 C 运算符的操作数的求值顺序,包括顺序函数调用表达式中函数参数的求值顺序,以及任何表达式中子表达式的求值顺序是未指定的,少数情况除外。主要有两种评估方式:a) 价值计算 b) 副作用。
Post-increment的优先级高,所以先计算。
现在,值增量恰好是在 “值计算” 之后排序的操作的副作用。因此,值计算结果将是操作数 p
的不变值(这里再次由于使用 *
运算符而取消引用),然后发生增量。
引用 C11
,章节 §6.5.2.4,
The result of the postfix ++
operator is the value of the operand. As a side effect, the
value of the operand object is incremented (that is, the value 1 of the appropriate type is
added to it). See the discussions of additive operators and compound assignment for
information on constraints, types, and conversions and the effects of operations on
pointers. The value computation of the result is sequenced before the side effect of
updating the stored value of the operand. [.....]
两种情况下的求值顺序是一样的,唯一的区别是,第一种情况下,最后的值被舍弃了。
如果您使用第一个表达式 "as-is",您的编译器应该产生一个关于未使用值的警告。
表达式
*p++
相当于
*(p++)
这是由于优先级(即:后缀增量运算符的优先级高于间接运算符)
和表达式
a=*p++
与
相同的原因
a=*(p++)
在这两种情况下,表达式 p++
的计算结果为 p
。
关联性与此处无关。仅当相邻运算符具有相同的优先级时,结合性才重要。但在这种情况下,++
的优先级高于 *
,因此只有优先级重要。由于优先级,表达式等效于:
*(p++)
因为它使用post递增,p++
递增指针,但是表达式returns指针的值在它之前递增。然后间接使用该原始指针来获取值。它实际上等同于:
int *temp = p;
p = p + 1;
*temp;
第二个表达式是一样的,除了它把值赋给另一个变量,所以最后一个语句变成:
a = *temp;
v = i++;
:i
返回相等运算,然后赋值给v
。随后, i
递增(编辑:从技术上讲,它不一定按此顺序执行)。因此 v
具有 i
的旧值。我记得是这样的:++
写在最后,因此发生在最后。
v = ++i;
:i
递增,返回赋值给v
。 v
和 i
具有相同的值。
- 当您不使用返回值时,它们也会执行相同的操作(尽管在某些情况下不同的实现可能会产生不同的性能)。例如。在 for 循环中,
for(int i=0; i<n; i++)
与 for(int i=0; i<n; ++i)
相同。后者有时会自动成为首选,因为它对于某些对象来说往往更快。
*
的优先级低于 ++
,因此 *p++
与 *(p++)
相同。因此,在这种情况下,p
返回到取消引用它的 *
。然后 p
中的地址增加一个元素。 *++p
先增加 p 的地址,然后取消引用它。
v = (*p)++;
设置 v 等于 p
指向的旧值然后递增它,而 v = ++(*p);
递增 p
指向的值然后设置v
等于它。 p
中的地址不变。
示例:如果,
int a[] = {1,2};
然后
int v = *a++;
和
int v = *++a;
都会使 a
递增,但在第一种情况下 v
将为 1,在后者中将为 2。
后缀运算符的优先级高于一元运算符。
因此这个表达式
*p++
等同于表达式
*( p++ )
根据 C 标准(6.5.2.4 后缀递增和递减运算符)
2 The result of the postfix ++ operator is the value of the
operand. As a side effect, the value of the operand object is
incremented (that is, the value 1 of the appropriate type is added to
it). See the discussions of additive operators and compound assignment
for information on constraints, types, and conversions and the effects
of operations on pointers. The value computation of the result is
sequenced before the side effect of updating the stored value of the
operand.
因此 p++
产生指针的原始值 p
作为操作的结果,并且还具有增加操作数本身的副作用。
至于一元运算符 then (6.5.3.2 地址和间接运算符)
4 The unary * operator denotes indirection. If the operand points to a
function, the result is a function designator; if it points to an
object, the result is an lvalue designating the object. If the operand
has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an
invalid value has been assigned to the pointer, the behavior of the
unary * operator is undefined
所以表达式的最终结果
*( p++ )
是指针 p
指向的对象的值,它也由于副作用而递增。这个值赋值给语句
中的变量a
a=*p++;
例如如果有如下声明
char s[] = "Hello";
char *p = s;
char a;
然后在这条语句之后
a = *p++;
对象 a
将具有字符 'H'
,指针 p
将指向数组 s 的第二个字符,即字符 'e'
。
*p++;
where p
is a pointer to integer.
p
will be incremented first and then the value to which it is pointing to is taken due to associativity (right to left). Is it right?
没有。在 post 增量中,值被复制到临时值(右值),然后作为副作用增加左值。
a=*p++;
where p
is a pointer to integer.
Value of p
is taken first and then assigned to a
first then p
is incremented due to post increment. Is it right?
不,这也不正确。 p
的增量可能发生在写入 a
之前。重要的是存储在 a
中的值是使用 p
.
先前值的临时副本加载的
未指定内存获取是否发生在内存写入新值 p
之前,任何依赖于该顺序的代码都是未定义的行为。
允许以下任何序列:
- 将
p
复制到临时中,然后递增 p
,然后在临时中指示的地址处加载值,然后将加载的值存储到 a
- 将
p
复制到临时文件中,然后在临时文件中指示的地址处加载值(该值本身放置在临时文件中)然后递增 p
然后将加载的值存储到 a
- 将
p
复制到临时中,然后在临时中指示的地址处加载值,然后将加载的值存储到 a
,然后递增 p
下面是两个未定义行为的代码示例,因为它们依赖于副作用的顺序:
int a = 7;
int *p = &a;
a = (*p)++; // undefined behavior, do not do this!!
void *pv;
pv = &pv;
void *pv2;
pv2 = *(pv++); // undefined behavior, do not do this!!!
圆括号不创建序列点(或sequenced before关系,在新措辞)。带括号的代码版本与不带括号的版本一样未定义。
表达式 1:*p++;
其中 p
是一个指向整数的指针。
p
将首先递增,然后由于关联性(从右到左)采用它指向的值。对吗?
表达式 2:a=*p++;
其中 p
是一个指向整数的指针。
先取p
的值,然后先赋值给a
,然后p
因post递增而递增。对吗?
首先,让我告诉你, 结合性 和 计算顺序实际上是相关的 这里。这都是关于 operator precedence。让我们先看看定义。 (强调我的)
Precedence :在数学和计算机编程中,运算顺序(或运算符优先级) 是一组规则,反映了关于 首先执行哪些过程以评估给定数学表达式的约定。
Associativity:在编程语言中,运算符的结合性(或固定性)是属性 确定在没有括号的情况下具有相同优先级的运算符如何分组。
Order of evaluation :任何 C 运算符的操作数的求值顺序,包括顺序函数调用表达式中函数参数的求值顺序,以及任何表达式中子表达式的求值顺序是未指定的,少数情况除外。主要有两种评估方式:a) 价值计算 b) 副作用。
Post-increment的优先级高,所以先计算。
现在,值增量恰好是在 “值计算” 之后排序的操作的副作用。因此,值计算结果将是操作数 p
的不变值(这里再次由于使用 *
运算符而取消引用),然后发生增量。
引用 C11
,章节 §6.5.2.4,
The result of the postfix
++
operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it). See the discussions of additive operators and compound assignment for information on constraints, types, and conversions and the effects of operations on pointers. The value computation of the result is sequenced before the side effect of updating the stored value of the operand. [.....]
两种情况下的求值顺序是一样的,唯一的区别是,第一种情况下,最后的值被舍弃了。
如果您使用第一个表达式 "as-is",您的编译器应该产生一个关于未使用值的警告。
表达式
*p++
相当于
*(p++)
这是由于优先级(即:后缀增量运算符的优先级高于间接运算符)
和表达式
a=*p++
与
相同的原因a=*(p++)
在这两种情况下,表达式 p++
的计算结果为 p
。
关联性与此处无关。仅当相邻运算符具有相同的优先级时,结合性才重要。但在这种情况下,++
的优先级高于 *
,因此只有优先级重要。由于优先级,表达式等效于:
*(p++)
因为它使用post递增,p++
递增指针,但是表达式returns指针的值在它之前递增。然后间接使用该原始指针来获取值。它实际上等同于:
int *temp = p;
p = p + 1;
*temp;
第二个表达式是一样的,除了它把值赋给另一个变量,所以最后一个语句变成:
a = *temp;
v = i++;
:i
返回相等运算,然后赋值给v
。随后,i
递增(编辑:从技术上讲,它不一定按此顺序执行)。因此v
具有i
的旧值。我记得是这样的:++
写在最后,因此发生在最后。v = ++i;
:i
递增,返回赋值给v
。v
和i
具有相同的值。- 当您不使用返回值时,它们也会执行相同的操作(尽管在某些情况下不同的实现可能会产生不同的性能)。例如。在 for 循环中,
for(int i=0; i<n; i++)
与for(int i=0; i<n; ++i)
相同。后者有时会自动成为首选,因为它对于某些对象来说往往更快。 *
的优先级低于++
,因此*p++
与*(p++)
相同。因此,在这种情况下,p
返回到取消引用它的*
。然后p
中的地址增加一个元素。*++p
先增加 p 的地址,然后取消引用它。v = (*p)++;
设置 v 等于p
指向的旧值然后递增它,而v = ++(*p);
递增p
指向的值然后设置v
等于它。p
中的地址不变。
示例:如果,
int a[] = {1,2};
然后
int v = *a++;
和
int v = *++a;
都会使 a
递增,但在第一种情况下 v
将为 1,在后者中将为 2。
后缀运算符的优先级高于一元运算符。
因此这个表达式
*p++
等同于表达式
*( p++ )
根据 C 标准(6.5.2.4 后缀递增和递减运算符)
2 The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it). See the discussions of additive operators and compound assignment for information on constraints, types, and conversions and the effects of operations on pointers. The value computation of the result is sequenced before the side effect of updating the stored value of the operand.
因此 p++
产生指针的原始值 p
作为操作的结果,并且还具有增加操作数本身的副作用。
至于一元运算符 then (6.5.3.2 地址和间接运算符)
4 The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined
所以表达式的最终结果
*( p++ )
是指针 p
指向的对象的值,它也由于副作用而递增。这个值赋值给语句
a
a=*p++;
例如如果有如下声明
char s[] = "Hello";
char *p = s;
char a;
然后在这条语句之后
a = *p++;
对象 a
将具有字符 'H'
,指针 p
将指向数组 s 的第二个字符,即字符 'e'
。
*p++;
wherep
is a pointer to integer.
p
will be incremented first and then the value to which it is pointing to is taken due to associativity (right to left). Is it right?
没有。在 post 增量中,值被复制到临时值(右值),然后作为副作用增加左值。
a=*p++;
wherep
is a pointer to integer.Value of
p
is taken first and then assigned toa
first thenp
is incremented due to post increment. Is it right?
不,这也不正确。 p
的增量可能发生在写入 a
之前。重要的是存储在 a
中的值是使用 p
.
未指定内存获取是否发生在内存写入新值 p
之前,任何依赖于该顺序的代码都是未定义的行为。
允许以下任何序列:
- 将
p
复制到临时中,然后递增p
,然后在临时中指示的地址处加载值,然后将加载的值存储到a
- 将
p
复制到临时文件中,然后在临时文件中指示的地址处加载值(该值本身放置在临时文件中)然后递增p
然后将加载的值存储到a
- 将
p
复制到临时中,然后在临时中指示的地址处加载值,然后将加载的值存储到a
,然后递增p
下面是两个未定义行为的代码示例,因为它们依赖于副作用的顺序:
int a = 7;
int *p = &a;
a = (*p)++; // undefined behavior, do not do this!!
void *pv;
pv = &pv;
void *pv2;
pv2 = *(pv++); // undefined behavior, do not do this!!!
圆括号不创建序列点(或sequenced before关系,在新措辞)。带括号的代码版本与不带括号的版本一样未定义。