指针,后增量运算符和序列点

时间:2017-08-10 11:11:36

标签: c pointers

当我运行这个玩具程序时,

#include<stdio.h>

int main(void)
{
    int arr[] = {11, 22, 33};
    int *ptr = arr;
    printf("ptr at: %p\n", (void *)ptr);
    printf("Result of expression: %d\n", *ptr++);  // line 8
    printf("ptr at: %p\n", (void *)ptr);
}

我得到的输出是:

ptr at: 0x7fffffffdbf0
Result of expression: 11
ptr at: 0x7fffffffdbf4

我对line 8上发生的事情的理解是,根据优先规则(后增量++的优先级高于取消引用*),表达式*ptr++是等效的到*(ptr++)。我Here是一个很好的解释。但是,在那个解释中没有提到序列点。

在混合中投掷序列点,我假设打印11,因为ptr仅在序列点之后递增(在所有参数之后 达到了printf已被评估)。并且,后增量运算符实际上是在序列点之后完成它(增加和写入)的工作。我的理解是否正确?

3 个答案:

答案 0 :(得分:6)

  

我假设打印11是因为ptr仅在序列点之后递增(恰好在printf的所有参数之后进行评估)

不。这与序列点无关。后增量表达式递增其参数,但表达式的值是增量之前的参数的值。

对于int*,后增量看起来很像这个函数:

int* postInc(int** p)
{
    int* tmp = *p;
    *p = *p + 1;
    return tmp;
}

*p++看起来像这样:

*postInc(&p);

答案 1 :(得分:5)

  

我对第8行发生的事情的理解是,根据优先规则(后增量++的优先级高于取消引用*),表达式* ptr ++相当于*(ptr ++)

这是正确的。

  

在混合中投掷序列点......

这与序列点本身没有任何关系,而是与postfix ++的行为有关,指定为(6.5.2.4):

  

postfix ++运算符的结果是操作数的值。作为副作用,   操作数对象的值递增   / - /
  结果的值计算在侧面之前排序   更新操作数存储值的效果。

简单地说,这意味着表达式ptr++将首先“返回”ptr的值,该值将用作表达式的结果,然后递增ptr由1。

对于整个表达式printf("Result of expression: %d\n", *ptr++);,在评估所有函数参数之后但在调用函数之前有一个序列点。再加上一些。

程序流程如下:

  1. 评估两个表达式"Result of expression: %d\n"*ptr++。这可以按任何顺序完成。
  2. 对于子表达式*ptr++,请计算其子表达式。运算符优先级指示ptr++生成一个子表达式。因此,将此表达式计算为ptr的值并存储结果。

  3. a)使用存储结果作为操作数评估*运算符。存储评估结果,传递给函数 b)将ptr增加1.
    3a)和3b)可以按任何顺序相互排序。

  4. 评估传递给printf的所有函数参数。
  5. 序列点。
  6. 执行printf。
  7. 就在printf返回之前的序列点。
  8. ;的序列点。
  9. 如您所见,序列点与*ptr++表达式的评估大多无关。序列点发挥作用的唯一时间是C指示++必须在它之前发生。但++可以发生在2)和5)之间的任何地方。

答案 2 :(得分:1)

否。使用值11是因为表达式$().ready(function() { // validate signup form on keyup and submit $.validator.addMethod("custom_number", function(value, element) { return this.optional(element) || value === "NA" || value.match(/^[0-9,\-]+$/); }, "Please enter a valid number, or 'NA'"); $("#getForm").validate({ rules: { name: "required", company: "required", email: {required: true, email: true}, number: {required: true, number: true,custom_number: true}, textarea: "required", partners: "required" }, messages: { name: "Please enter name", company: "Please enter your company name", email: "Please enter a valid email address", number: "Please enter a valid phone number", textarea: "Please enter your requirements", partners: "Partners can't be blank" } }); 的值在递增之前是ptr++的值。完全停止。这里没有序列点!

序列点确实在函数调用中输入函数之前发生,序列点的相关性是在输入函数之前必须发生ptr的增量。

以下是显示正在发生的代码:

ptr

典型输出:

#include <stdio.h>

int arr[] = {11, 22, 33};
int *ptr = arr;

int indirect(const char *fmt,int val){
    printf("peek: %p (%d)\n",ptr,*ptr);
    return printf(fmt,val);
}   

int main(void)
{
    printf("ptr at: %p\n", (void *)ptr);
    indirect("Result of expression: %d\n", *ptr++);  // line 8
    printf("ptr at: %p\n", (void *)ptr);
}

有关序列点的规则告诉您第二个必须显示ptr at: 0x2ac98105b030 peek: 0x2ac98105b034 (22) Result of expression: 11 ptr at: 0x2ac98105b034 递增,并且最后必须有ptr

如果我们在最后一行看到(22)peek: 0x2ac98105b030 (11),则会违反函数调用序列点规则。

这意味着在输入ptr at: 0x2ac98105b034后发生副作用(即ptr的增量)。

优先顺序定义评估顺序,但序列点指定必须发生副作用的点。

理解它们的一个好方法是将评估和副作用的问题分开。通过评估表达式并记下所有副作用,然后查看序列点规则,告诉您它们何时必须发生。

如果你这样做(第8行的关键步骤):

  1. 取值ptr。
  2. 注意:同时将ptr增加1(indirect())。
  3. 确定存储在步骤1中采用的int值的值。
  4. 致电ptr
  5. 步骤4是一个序列点,因此我们必须确保第4步发生在4之前。 但它可能发生在3之前。

    很容易在C中混淆,因为增量前和后增量运算符在定义中都有序列化。

    将p ++视为“取p值和副作用增量p”。 将++ p视为“将p + 1的值和副作用增量p”而不是视为“增量p并取其值”。

    printf()的第二个措辞意味着标准不保证的顺序,并且可能发生的顺序绝对不能保证。