给定表达式中的运算符优先级

时间:2017-05-25 09:26:19

标签: c pointers operator-precedence post-increment

表达式1:*p++;其中p是指向整数的指针。

p将首先递增,然后由于关联性(从右到左)获取它指向的值。是不是?

表达式2:a=*p++;其中p是指向整数的指针。

首先获取p的值,然后首先分配给a,然后p由于后增量而递增。是不是?

6 个答案:

答案 0 :(得分:17)

首先,让我告诉您, 关联性评估顺序实际上与此处相关。这完全是关于operator precedence。我们先看看定义。 (强调我的

  • Precedence :在数学和计算机编程中,操作顺序(或运算符优先级)是反映约定的规则的集合首先执行哪些程序以评估给定的数学表达式。

  • Associativity :在编程语言中,运算符的关联性(或固定性)是一个属性,用于确定运算符的运算方式在没有括号的情况下,相同的优先级被分组

  • Order of evaluation :任何C运算符的操作数的计算顺序,包括函数调用中函数参数的计算顺序表达式和任何表达式中子表达式的评估顺序都是未指定的,除了少数情况。主要有两种类型的评估:a)价值计算b)副作用。

后增量具有更高的优先级,因此将首先进行评估。

现在,恰好值增量是在“值计算”之后排序的操作的副作用。因此,值计算结果将是操作数p的未更改值(这里,再次,由于使用*运算符而被取消引用),然后,增量发生。

引用C11,章节§6.5.2.4,

  

后缀++运算符的结果是操作数的值。作为副作用,   操作数对象的值递增(即,相应类型的值为1   添加到它)。请参阅添加剂操作符和化合物分配的讨论   有关约束,类型和转换以及操作对其影响的信息   指针。 结果的值计算在副作用之前排序   更新操作数的存储值。 [.....]

两种情况下的评估顺序是相同的,唯一的区别是,在第一种情况下,最终值被丢弃。

如果您使用第一个表达式“按原样”,您的编译器应该生成有关未使用值的警告。

答案 1 :(得分:9)

后缀运算符的优先级高于一元运算符。

因此这个表达

*p++

等同于表达式

*( p++ )

根据C标准(6.5.2.4后缀增量和减量运算符)

  

2 后缀++运算符的结果是。的值   操作数即可。作为副作用,操作数对象的值是   递增(即,将相应类型的值1添加到   它)。请参阅加法运算符和复合赋值的讨论   有关约束,类型和转换以及效果的信息   对指针的操作。结果的值计算是   在更新存储值的副作用之前排序   操作数。

因此,p++产生指针p的原始值作为操作的结果,并且还具有递增操作数本身的副作用。

至于一元运算符(6.5.3.2地址和间接运算符)

  

4一元*运算符表示间接。如果操作数指向a   功能,结果是功能指示符;如果它指向一个   对象,结果是指定对象的左值。如果是操作数   有类型''指向类型'',结果类型''类型''。如果   无效值已分配给指针,行为   unary *运算符未定义

表达式的最终结果

*( p++ )

是指针p指向的对象的值,由于副作用也会递增。此值将分配给语句

中的变量a
a=*p++;

例如,如果有以下声明

char s[] = "Hello";
char *p = s;
char a;

然后在此声明之后

a = *p++;

对象a将具有字符'H',而指针p将指向数组的第二个字符{}}}。

答案 2 :(得分:4)

相关性与此无关。只有具有相同优先级的相邻运算符时,关联才有意义。但在这种情况下,++的优先级高于*,因此只有优先级才重要。由于优先级,表达式等同于:

*(p++)

由于它使用后递增,p++递增指针,但表达式在递增之前返回指针的值。然后间接使用该原始指针来获取值。它实际上相当于:

int *temp = p;
p = p + 1;
*temp;

第二个表达式是相同的,除了它将值赋给另一个变量,以便最后一个语句变为:

a = *temp;

答案 3 :(得分:3)

表达式

*p++

相当于

*(p++)

这是由于先行(即:后缀增量运算符的优先级高于间接运算符

和表达式

a=*p++

相同的原因相同
a=*(p++)

在这两种情况下,表达式p++都会计算为p

答案 4 :(得分:1)

  • v = i++;i返回到相等操作,然后分配给v。随后,i递增(编辑:从技术上讲,它不一定按此顺序执行)。因此v具有i的旧值。我记得这样:++写在最后,因此最后发生。
  • v = ++i;i递增,然后返回以分配给vvi具有相同的值。
  • 当您不使用返回的值时,它们会执行相同的操作(尽管在某些情况下,不同的实现可能会产生不同的性能)。例如。在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指向的值,然后设置{{ 1}}等于它。 v中的地址未更改。

示例:如果,

p

然后

int a[] = {1,2};

int v = *a++;

会使int v = *++a; 增加,但在第一种情况下,a将为1,而在后者中,它将为2。

答案 5 :(得分:0)

  

*p++;其中p是指向整数的指针。

     

p将首先递增,然后由于关联性(从右到左)获取它指向的值。是不是?

没有。在后增量中,将值复制到临时值(右值),然后左值增加为副作用。

  

a=*p++;其中p是指向整数的指针。

     

首先获取p的值,然后首先分配给a,然后p由于后增量而递增。是不是?

不,那也不正确。 p的增量可能在写入a之前发生。重要的是,a中存储的值是使用先前值p的临时副本加载的。

是否在没有指定新值p的内存写入之前发生内存提取,并且依赖于该顺序的任何代码都是未定义的行为。

允许任何这些序列:

  • p复制到临时THEN增量p,然后将临时THEN商店加载值中指示的地址加载到a
  • p复制到临时指示的地址的临时THEN加载值(此值本身放置在临时值中)然后加p然后将加载的值存储到a
  • p复制到临时THEN加载值,该值在临时THEN商店加载值中显示的地址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!!!

括号创建序列点(或关系之前的序列,在新的措辞中)。带括号的代码版本与没有。的版本一样未定义。