在printf的顺序点 [英] Sequence Points in 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 printing42
.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 becauseprintf
arguments are part of ava_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 passesi+1
(13), and the return value of the inner call (undefined, in this case -1 becausei++
,i
evaluates to 12, 13 apparently), but there's no guarantee that this will always be the caseThat 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. Ifa
is uninitialized, then100-a
is UB. Ifa
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 ofprintf
, so you can't use its new value in the argument list (because there's a sequence point). If you expectn
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 changeunsigned int a = 90;
tounsigned int a;
, then the behaviour is undefined.
Concerning the
12
'sNow 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 thati
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 theprintf
call), andi
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 tosprintf
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屋!