在printf的顺序点 [英] Sequence Points in printf

查看:114
本文介绍了在printf的顺序点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里

我看了 的,有一个顺序点:


  

与输入/输出转换格式说明相关的操作后。例如,在EX pression 的printf(富%N%D,&安培;一,42),有<$ C后顺序点$ C>%N 打印前进行评估 42


然而,当我运行这个code

  INT your_function(int类型的,INT B){
    返回 - B;
}诠释主要(无效){
    INT I = 10;    的printf(%d个内容 - %d - %d个\\ N,我,your_function(++我,我++),I);
}

而不是我所期望的,我得到:


  

12 - 0 - 12


含义,有的的文件转换格式符创建一个序列点。 http://en.wikipedia.org 错了,还是有我只是误解的东西,或者是gcc不符合在这种情况下,(顺便说一句Visual Studio的2015年产生同样的意想不到的结果)?

编辑:

据我所知,参数 your_function 进行评估并分配给参数的顺序是不确定的。我没有问为什么我的中期是0。我问为什么其他两个术语都是12。


解决方案

由于这个问题被问,因为基于注释的讨论的<一个href=\"http://stackoverflow.com/questions/34614308/is-there-a-way-to-get-warned-about-misbehaving-designated-initializers#comment57018987_34614308\">here,我将提供一些背景:


  

首评的:操作的顺序是的不可以保证在您将参数传递给函数的顺序。有些人(错误地)假设参数将被从右到左评估,但根据标准,行为是不确定的。


在OP接受并理解这一点。在重复一个事实,即 your_function(++我,我++) UB是没有意义的。


  

在回应这一评论的:感谢您的评论我看到的printf 可以按任何顺序进行评估,但我明白,要因为的printf 参数是一个的va_list 部分。你是说参数的任何功能以任意顺序执行?


OP要求澄清,所以我阐述了一下:


  

第二个评论的:是的,这正是我在说什么。甚至要求 INT your_function(int类型的,INT B){返回 - B; } 并不能保证你通过前pressions进行评估从左到右。有没有顺序点(在此执行previous评估的所有副作用点)。以这个例子。该嵌套调用是一个序列点,所以外部调用传递 I + 1 (13)和内调用(不确定,在这种情况下的返回值-1,因为我++ I 的计算结果为12,13显然),但是没有保证,这将永远是案例


这使得pretty显然,这些类型的构造引发UB所有功能。


百科混乱

OP行情如下:


  

与输入/输出转换格式说明相关的操作后。例如,在离pression的printf(富%N%d个,&放大器;一,42)。,有%正后的序列点打印42之前被评估


然后,它适用于他的片段( prinf(%d个内容 - %d - %d个\\ N,我,your_function(++我,我++),I); )expeciting的格式说明作为序列点。结果
什么是被说提到的输入/输出转换格式说明的是%N 说明。对应的参数的必须的是指向一个无符号整数,它将被分配迄今为止打印的字符数。当然,%N 打印的剩余参数之前,必须进行评估。但是,使用被传为指针%N 在其他参数仍是危险的:它的 UB(当然,它不大,但它可以):

 的printf(富%N%* S \\ n,&安培;一,100-A,酒吧); //危险!

有一个序列点的的函数被调用,所以前pression 100-A 将会为正确的值; EM> %N 已成立&功放前进行评估。如果 A 未初始化,然后 100-A 为UB。如果 A 初始化为0,例如,前pression结果的将会的是100.整体而言,虽然,这一种code是pretty多找麻烦。把它当作的非常不好的做法的,或者更糟......结果
只要看看这些语句中的任何一个产生的输出:

  unsigned int类型A = 90;
的printf(%U%N%* S \\ N,一,&安培; A,10,酒吧); // 90条
的printf(%U \\ N,一个); // 3
的printf(富%U%N%* S \\ N,一,&安培; A,10-A,酒吧); //美孚3酒吧和LT;填充使用:10 - 3,而不是10 - 6
的printf(%U \\ N,一个); // 6

在你可以看到, N 获得重新分配的的printf ,所以你不能在参数列表中使用它的新值(因为有一个序列点)。如果你希望 N 就地你基本上期待C到跳出函数调用的重新分配,评估等参数,并跳回呼叫。这是不可能的。如果你要改变 unsigned int类型A = 90; unsigned int类型一; ,则该行为是未定义<。 / p>


关于 12的

现在,因为OP上的序列点读时,他正确地注意到这样一句话:

 的printf(%d个内容 - %d  - %d个\\ N,我,your_function(++我,我++),I);

略有不同: your_function(++我,我++) 序列点, 担保 I 将递增两次。这个函数调用序列点,因为:


  

在函数调用输入函数之前。在该参数的计算顺序没有规定,但这个顺序点意味着他们所有的副作用是完全进入该功能前


这意味着,之前的printf 被称为 your_function 以被称为(因为它的返回值是参数为的printf 通话一), I 将两次递增。< BR>
可能解释为输出的?12 - 0 - 12的,但它保证是输出

从技术上讲,虽然大多数编译器将评估 your_function(++我,我++); 先打电话,该标准将允许编译评价传递给参数的sprintf 左到右(未指定顺序毕竟)。所以,这将是一个同样有效的结果:

  10  -  0  -  12
//甚至
12 - 0 - 10
//和
10 - 0 - 10
//技术上,连这将是有效的
12 - 0 - 11

虽然后者输出的可能性微乎其微(这将会是非常低效)

I read here that there is a sequence point:

After the action associated with input/output conversion format specifier. For example, in the expression printf("foo %n %d", &a, 42), there is a sequence point after the %n is evaluated before printing 42.

However, when I run this code:

int your_function(int a, int b) {
    return a - b;
}

int main(void) {
    int i = 10;

    printf("%d - %d - %d\n", i, your_function(++i, ++i), i);
}

Instead of what I expect I get:

12 - 0 - 12

Meaning that there was not a sequence point created for the conversion format specifier. Is http://en.wikipedia.org wrong, or have I just misunderstood something, or is gcc non-compliant in this case (incidentally Visual Studio 2015 yields the same unexpected result)?

EDIT:

I understand that the order the arguments to your_function are evaluated and assigned to the parameters is undefined. I'm not asking about why my middle term is 0. I'm asking why the other two terms are both 12.

解决方案

Because this question was asked because of a comment-based discussion here, I'll provide some context:

first comment: The order of operations is not guaranteed to be the order in which you pass arguments to the function. Some people (wrongly) assume that the arguments will be evaluated right to left, but according to the standard, the behaviour is undefined.

The OP accepts and understands this. No point in repeating the fact that your_function(++i, ++i) is UB.

In response to that comment: Thanks to your comment I see that printf may be evaluated in any order, but I understood that to be because printf arguments are part of a va_list. Are you saying that the arguments to any function are executed in an arbitrary order?

OP asking for clarification, so I elaborated a bit:

Second comment: Yes, that's exactly what I'm saying. even calling int your_function(int a, int b) { return a - b; } does not guarantee that the expressions you pass will be evaluated left to right. There's no sequence point (a point at which all side effects of previous evaluations are performed). Take this example. The nested call is a sequence point, so the outer call passes i+1 (13), and the return value of the inner call (undefined, in this case -1 because i++, i evaluates to 12, 13 apparently), but there's no guarantee that this will always be the case

That made it pretty clear that these kinds of constructs trigger UB for all functions.


Wikipedia confusion

OP Quotes this:

After the action associated with input/output conversion format specifier. For example, in the expression printf("foo %n %d", &a, 42), there is a sequence point after the %n is evaluated before printing 42.

Then applies it to his snippet (prinf("%d - %d - %d\n", i, your_function(++i, ++i), i);) expeciting the format specifiers to serve as sequence points.
What is being referred to by saying "input/output conversion format specifier" is the %n specifier. The corresponding argument must be a pointer to an unsigned integer, and it will be assigned the number of characters printed thus far. Naturally, %n must be evaluated before the rest of the arguments are printed. However, using the pointer passed for %n in other arguments is still dangerous: it's not UB (well, it isn't, but it can be):

printf("Foo %n %*s\n", &a, 100-a, "Bar");//DANGER!!

There is a sequence point before the function is called, so the expression 100-a will be evaluated before %n has set &a to the correct value. If a is uninitialized, then 100-a is UB. If a is initialized to 0, for example, the result of the expression will be 100. On the whole, though, this kind of code is pretty much asking for trouble. Treat it as very bad practice, or worse...
Just look at the output generated by either one of these statements:

unsigned int a = 90;
printf("%u %n %*s\n",a,  &a, 10, "Bar");//90         Bar
printf("%u\n", a);//3
printf("Foo %u %n %*s\n",a, &a, 10-a, "Bar");//Foo 3      Bar < padding used: 10 - 3, not 10 - 6 
printf("%u\n", a);//6

In as you can see, n gets reassigned inside of printf, so you can't use its new value in the argument list (because there's a sequence point). If you expect n to be reassigned "in-place" you're essentially expecting C to jump out of the function call, evaluate other arguments, and jump back into the call. That's just not possible. If you were to change unsigned int a = 90; to unsigned int a;, then the behaviour is undefined.


Concerning the 12's

Now because the OP read up on sequence points, he correctly notices that this statement:

printf("%d - %d - %d\n", i, your_function(++i, ++i), i);

Is slightly different: your_function(++i, ++i) is a sequence point, and guarantees that i will be incremented twice. This function call is a sequence point because:

Before a function is entered in a function call. The order in which the arguments are evaluated is not specified, but this sequence point means that all of their side effects are complete before the function is entered

That means that, before printf is called, your_function has to be called (because its return value is one of the arguments for the printf call), and i will be incremented twice.
This could explain the output being "12 - 0 - 12", but is it guaranteed to be the output?

No

Technically, although most compilers will evaluate the your_function(++i, ++i); call first, the standard would allow a compiler to evaluate the arguments passed to sprintf left to right (the order isn't specified after all). So this would be an equally valid result:

10 - 0 - 12
//or even
12 - 0 - 10
//and
10 - 0 - 10
//technically, even this would be valid
12 - 0 - 11

Although the latter output is extremely unlikely (it'd be very inefficient)

这篇关于在printf的顺序点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆