重组方向和实行各种,C学习笔记

自增

1. 主题素材的提议

K&Lacrosse的书中每每重申”C is not a big
language”,当时看书的时候无法知晓那句话的情致。今后自家的精通是C标准自身的限定非常的小,留给工程师的长空异常的大。那样一来C中的而有个别难题标准就从不付诸限定,就能发生局地让人吸引的地方。后日作者遇上了二个如此的难题,化解那么些主题材料部分时候看起来是“钻牛角尖”,好呢,笔者正是二个爱钻牛角尖的人。这里要求化解的难题是试行种种的主题素材,先交由多少个难题。

int i = 0, j = 0;
j = i++ + i++ + i++;

推行完这两个语句后i和j的值各是哪些?(瞧着是或不是很熟习,上次笔试考C中是或不是有其一吧)

int i=0;
int arry[10];
arry[i] = i++;

实施完上述语句后,i和数组arry中的值是稍微?

#define PRINT(x, y, z)  printf(" x = %d, y = %d, z = %d\n", (x), (y),(z))
x = y = z = 1;
++x || ++y && ++z; PRINT(x, y, z);     

输出结果是什么?(来自于C Puzzle Book Operators 1.6 )
先把那多少个难题放在这里,先商讨下,上边将会消除他们。

Logical AND (&&)

(1)后缀:与Turbo
C一样,在言辞甘休在此之前也许说分号在此以前才会实行自增。

2. 明显多少个概念:side effects, sequence point

The && operator can be understood at three different levels. At the
simplest level, when used with boolean operands, && performs the
Boolean AND operation on

(2)前缀:

2.1 side effects

C语言杰出文章 “The C Programming Language” 中对于side effects的定义:

Function calls, nested assignment statements, and increment
anddecrement
operators cause ‘side effects’ – some variable is changed as a
by-product
of the evaluation of an expression.

此地述说的side
effect能够知晓为一种“副功效”,这种成效是退换一个变量的值。
“C In Nutshell” 中关于side effects的定义:

In addition to yielding a value, the evaluation of an expression can
result in other changes in the execution environment, called side
effects. Examples of such changes include modifications of a
variable’s value, or of input or output streams.

相对于K&途乐中的定义这里运用了对于境遇的更改,那应当尤为正确。计算:side
effects 就是前后相继中的实体发生的改观,这里所说的实业常常指变量。
赋值,自增,自减表明式会时有产生side effects,函数调用表明式也会有希望爆发side
effects。

&& 操作符能够从多个范畴来精晓,最简便的贰个局面,&&操作符用在布尔操作数上,

前八个自增统一取值,后边的自增即为取值。

2.2 sequence points

sequence points: A sequence point is a point in time at which the dust
has settled and all side effects which have been seen so far are
guaranteed to be complete. The sequence points listed in the C
standard are:

  1. at the end of the evaluation of full expression ( a full expression
    is an expression statement, or any other expression which is not
    subexpression within any large expression);
  2. at the ||, &&, ?:,and comma operators; and
  3. at a function call (after the evaluation of all arguments, and just
    before the actual call).

类别点(sequence
points)是一种逻辑意义的点,它的意思在于,逻辑点前的副成效(side
effects)都在此时生效。C标准中定义的类别点总共有三类,第一类是一心表明式(full
expression);第二类是||,&&,?:和;第三类是函数调用,在享有的参数明确后、函数真正调用在此以前。

the two values: it returns true if and only if both its first operand
and its second operand are true. If one or both of these operands is
false, it returns false.

  int i=2,j;
  j=++i+(++i)+(++i);

2.3 side effects 和sequence points对于编写程序有怎样含义?

当多少个操作数都以ture的时候,才重回true。任何贰个操作数是false,再次回到都以false。

j=4+4+5;

2.3.1我的“SS1”和“SS2”原则

正式中明确了在前叁个连串点前的副功能都会在前三个系列点后成功,可是正式尚未分明三个体系点之间的副成效生效的相继,差别的C语言完结的逐个可能两样。请留神那一点,那是具有难题时有产生的根本原因。要是三个类别点之间有凌驾五个的副作用成效在同叁个实体上,那样分化的编写翻译器发生的结果就分歧,这种气象在行业内部中称之为unspecified
。所以在其实使用中应该制止这种景况的产出,笔者把这几个尺度称为为SS1。

是否遵循了SS1原则就不会发生unspecified了吧?非也。能够思量那样一种情形:每一个实体(A)在多个连串点之间被几回接纳,独有一回对这些实体本人发生副成效,另外二次被间接的用来发出副效能效果于其余一个实体(B)。在头里虚拟的这种情形下尽管符合SS1原则,不过大家会开掘被直接用来发出副作用时,对于实体(B)发生的副效用明确会跟实体(A)有关,不过那个实体(A)在这么些行列点距离中有被副功能作用,那么大家就不能够分明这些实体(A)的值了,进而实体(B)也就不可能鲜明了。这里能够归纳为:在多少个连串点之间,假诺出现对三个实体的再三引用,並且独有一遍会对该实体产生副成效(SS1),那么富有的那一个援用都必需用来发生这么些副成效,笔者把那三个标准称为SS2。独有同不时间遵从了SS1和SS2,写出的表达式才不是unspecified类型的。

&& is often used as a conjunction to join two relational
expressions:

在意无法一贯写j=++i+++i+++i;

2.3.2 标准

The standard states that Between the previous and next sequence point
an object shall have its stored value modified at most once by the
evaluation of the expression. Furthermore, the prior value shall be
accessed only to determine the value to be stored.

能够清晰的观察标准中应用了两句话来回顾这种主题素材,那正好对应于SS1,SS2原则。

&& 操作符 平时被用在连接多个关系表达式上:

要不会报错error C2105: ‘++’ needs l-value;

2.3.3 如何技巧防止写出未定义的表明式出来?

下面给出更为具体的秘籍:
1)在二个表明式中最四只更换一个实体。
2)假设二个实体在一个表达式被退换,並且出现次数超过三次,请保管全部实体的出现都认为着发生这些“改变”。
例如: i = i+1;
3)倘诺无法遵循1),那么请确认保证更换的是例外的实业。
例如:c = *p++;
4)借使1)和2)都不能听从,那么请使用体系点将表明式分开。
例如 : (c = getchar()) != EOF && c != ‘\n’;

x == 0 && y == 0 // x,y 都以零才是回来 ture

因为++后边跟的要么+,必需求左值技能++;

3. 优先级、结合方向做了怎么事,未有做什么事?

Relational expressions always evaluate to true or false, so when used
like this, the && operator itself returns true or false.

    int i=2,j;
    j=(i++)++;

3.1 优先级、结合方向做了哪些事?

C语言中结合程序的基本单位是表明式(expression),表明式是指用操作符(operator)和操作数(operand)连接起来的姿态。C规范交付了最宗旨的操作符,通过这个操作符可以组合简单表明式,同样也得以透过复合产生复杂表达式。当三个表达式中冒出几个操作符,七个操作数的时候,操作符合操作数是怎么着结合起来的吗?优先级和重组方向正是用来缓慢解决这么些主题素材的,能够那样说,优先级和整合方向给出了二个说明式的意义,这只是表明了逐一操作符和操作数是怎么聚合起来的。

涉及表达式结果可能是true,要么是false,所以,&&用在她们中间,或然再次来到ture,只怕重回false。

如此那般也一律会报错error
C2105: ‘++’ needs l-value;

3.2 优先级、结合方向未有做什么事?

一味依赖优先级和重组方向是力所不及鲜明一个复合表明式中对种种子表明式的求值顺序。标准中对于那点的鲜明是:
五个相邻的操作符的推行顺序由它们的事先级决定。纵然它们优先级同样,它们的试行各样由它们的结合性决定。除却,编写翻译器能够自由支配别的顺序对表明式举办求值,只要它不违反逗号,&&,||和?:操作符所施加的界定。

 

因为i++再次来到的是叁个数值
,实际不是三个可赋值的变量(左值);

4. 消除难题

1)j = i++ + i++ + i++;
以此表明式违反了SS1,不相同的编写翻译器发生的结果只怕两样。
2)arry[i] = i++;
其一表明式违反了SS2,差异的编写翻译器产生的结果大概两样。
3)x = y = z = 1;
++x || ++y && ++z; PRINT(x, y, z);
&&的预先级比|| 的前期级高,所以:
++x || ++y && ++z 等效于++x || (++y && ++z)
这边就很轻易犯错,会以为先进行++y && ++z 在施行++x || ( …
),这种观点是荒谬的,c中只对 逗号,、逻辑与 &&、
逻辑或 || 和
条件表达式规定了推行顺序,对于逻辑表明式方向是从左向右实行的。本例子中,先进行++x = 2,逻辑
或表明式被卡住,++y && ++z未有进行,最终x = 2, y = 1, z = 1;

Relational operators have higher precedence than && (and ||), so
expressions like these can safely be written without parentheses.

集结取值必须是指前七个同时必需是连在一齐的,不然每三个都以自增即为取值。

5.后记

由此本课题的求学加深了对于c的敞亮,精晓SS1,SS2原则。在其实应用中应当遵从“一条语句只做一件事的原则”。

提到运算符的早期级比逻辑运算符&& (或者||)的等级高,所以不要增多括号。

    int i=2,j;
    j=++i+i+++(++i)+(++i);

But && does not require that its operands be boolean values. Recall
that all JavaScript values are either “truthy” or “falsy.” (The falsy
values are false, null, undefined,

j=3+3+4+5;

但是 && 运算符并不供给操作数是布尔值。事实上,JS多有值中,要么是
“真值”,要么是“假值”。(假值有:false,null,undefined,0,-0,Nan,and,“”

任由int 依然float都依照那个法则。

0, -0, NaN, and “”. All other values, including all objects, are
truthy.)

(3)在乘法中的使用法则:

。剩下的值,包罗对象都以真值的界定了。)

i++*++i自动识别为++i*i++;

The second level at which && can be understood is as a Boolean AND
operator for truthy and falsy values. If both operands are truthy, the
operator returns a truthy

    int i=2,j1,j2;
    j1=i++*++i;
    i=2;
    j2=++i*i++;

理解 &&操作符的第三个规模是: 操作数 恐怕重临值
不再是布尔值,而是真、假值。假如七个操作数皆以“真值”,运算符就回到“真值”。

j1=3*3;

value. Otherwise, one or both operands must be falsy, and the operator
returns a falsy value. In JavaScript, any expression or statement that
expects a boolean value

j1==j2;

否则,只要任何一个操作数是“假值”,就运算符就回来“假值”。在JS中,那一个梦想布尔值的表明式只怕语句中,

自减也是一样的。

will work with a truthy or falsy value, so the fact that  && does
not always return true or false does not cause practical problems.

 

同一会对等的拍卖“真值”只怕“假值”。所以,事实上  && 并不是临时回来布尔值
true恐怕false,并没什么实际难题。

在printf中,后缀自增也是在言辞截至是才进行操作的。

Notice that the description above says that the operator returns “a
truthy value” or “a falsy value,” but does not specify what that value
is. 

    int i=2;
    printf("%d\n",i=i++);
    i=2;
    printf("%d  %d\n",i,i++);

值得注意的是,上面说 && 操作符重回的是
“真值”可能“假值”,不过并从未提议重临的现实性值指的是怎样的值。

结果都是2。

For that, we need to describe && at the third and final level. This
operator starts by evaluating its first operand, the expression on its
left. If the value on the left is

 

为此,大家供给讲明 && 
操作符的末段一个局面。操作符第一步是对他的率先个操作数举办求值,也正是表明式的侧边包车型客车操作数。尽管那几个左值是个“假的”

《裘宗燕:C/C++ 语言中的表明式求值》文摘:

falsy, the value of the entire expression must also be falsy, so &&
simply returns the value on the left and does not even evaluate the
expression on the right.

除此以外,想要知道变量更新的值是还是不是曾经保存到了内部存储器,必要小心顺序点在哪里;

那正是说整个表明式正是假的,所以,&&  就一贯重临那个左值,不在计算表达式右侧的值了。

C/C++语言定义(语言的仿效手册)明显概念了顺序点的定义。顺序点位于:

On the other hand, if the value on the left is truthy, then the overall
value of the expression depends on the value on the right-hand side.

1. 各类完整表明式截止时。完整表明式包涵变量起头化表明式,表明式语句,return语句的表明式,以及规格、循环和switch语句的决定表达式(for底部有三个调整表达式);

一面,假使左值是“真的”,那么一切表达式的值就凭仗于表明式侧面的值了。

2. 运算符 &&、||、?: 和逗号运算符的首先个运算对象计算之后;

If the value on the right is truthy, then the overall value must be
truthy, and if the value on the right is falsy, then the overall value
must be falsy. So when the value on

3. 函数调用中对持有实际参数和函数名表达式(需求调用的函数也说不定由此表明式描述)的求值达成以往(踏向函数体在此之前)。

万一右值是“真的”,那么一切表明式正是真的,假设右值是假的,那么任何表达式正是假的。所以,

并且C/C++并从未对持有的演算顺序实行明确,而是交由编写翻译器依照须求贯彻调度,进而获得功用越来越高的代码;

the left is truthy, the && operator evaluates and returns the value on
the right:

Java则有严苛规定表达式求值顺序,但多数程序设计语言实际上都才用了类似C/C++的规定。

假若左边的值是当真,那么 && 运算符对右侧操作数求值,并赶回所求的值:
var o = { x : 1 };
var p = null;
o && o.x // => 重返是1: o 是确实, 所以对侧边o.x求值,并回到 这一个值
p && p.x // => 重临是null: p是假的, 所以直接回到,不用再对 p.x求值

“什么人知道下边C语句给n赋什么值?”

It is important to understand that && may or may not evaluate its
right-side operand. In the code above, the variable p is set to null,
and the expression p.x would, if

m = 1; n = m++ +m++;

明白 && 操作符
有希望不对侧面的操作数求值很关键。在地点的代码中,要是变量p
是null,假使再对p.x实行求值,就能孳生TypeError错误。

不错回答是:不晓得!语言未有规定它应有算出什么样,结果完全凭仗具体系统在切实可行上下文中的具体管理。

evaluated, cause a TypeError. But the code uses && in an idiomatic
way so that p.x is evaluated only if p is truthy—not null or undefined.

中间牵涉到运算对象的求值顺序和变量修改的兑现时刻难点。

在那样的惯用情势中,操作符&&
唯有当左侧操作数是的确,才会猜想左边操作符。

 

The behavior of && is sometimes called “short circuiting,” and you
may sometimes see code that purposely exploits this behavior to
conditionally execute code.

 商酌区摘要:

所以  && 操作符 一时候被称作有 短路
的机能。你也会看到,有的时候候代码就是运用那几个特点来有条件性的实施代码。

②c99:
Annex J

 

J.1 Unspecified behavior:

For example, the following two lines of JavaScript code have equivalent
effects:

— The order in which subexpressions are evaluated and the order in which side effects take place, 

如下的例子,两行代码的功用是一致的:

except as specified for the function-call (), &&, ||, ?:, and comma operators (6.5).

if (a == b) stop(); // 调用 stop() ,只有在 a == b时发生
(a == b) && stop(); // 区别样的写法,一样的职能

— The order in which the function designator(操作数提示符), arguments(实参), and subexpressions(子表明式)
within the arguments are evaluated in a function call (6.5.2.2).

In general, you must be careful whenever you write an expression with
side effects (assignments, increments, decrements, or function
invocations) on the right-hand

— The order in which the operands of an assignment operator(赋值运算符的操作数)
are evaluated(被求值)
(6.5.16).
……..

普普通通,必需小心所写的表明式对于 &&运算符左边的操作数是还是不是有副效率,

J.2 Undefined behavior
— Between two sequence points(序列点/顺序点), an object is modified(修正)
more than once, or is modified

side of  &&. Whether those side effects occur depends on the value
of the left-hand side.

and the prior value is read other than(除了)
to determine the value to be stored (6.5).
……

这都在于左边包车型大巴操作数。

 

④c++0x末了草案(FDIS )
ISO/IEC FDIS 14882 
N3290:
1.9 Program execution(执行)

15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual 

expressions are unsequenced.
[
Note: In an expression that is evaluated more than once during the execution 

of a program,unsequenced and indeterminately sequenced evaluations
of its subexpressions need not be performed

consistently(一贯地)in
different evaluations.
—end note ] The value computations of the operands of 

an operator are sequenced before the value computation of the result of the operator.
If a side effect on a

 scalar object(标量对象)is unsequenced
relative to either another
side effect on the same scalar object or

a value computation using the value of the same scalar object, the behavior is undefined.

[ Example:
void f(int, int);

void g(int i, int* v) {

i = v[i++]; // the behavior is undefined

i = 7, i++, i++; // i becomes 9

i = i++ + 1; // the behavior is undefined

i = i + 1; // the value of i is incremented(递增)

f(i = -1, i = -1); // the behavior is undefined
}
—end example ]

When calling a function (whether or not the function is inline(内联)[内联函数传承了宏定义的长处,也解决了它的隐疾]), 

every value computation and side effect
associated with any argument expression,
or with the postfix expression

designating(把…定名为)
the called function, is sequenced before execution of every

 expression or statement
in the body of the called function. [ Note: Value
computations and side effects

 associated with different argument expressions are 

unsequenced. —end note.]

Every evaluation in the calling function (including other function calls) that is not otherwise specifically 

sequenced before or after the execution of the body

 of the called function is
indeterminately sequenced with
respect to the execution of the called function.9 Several 

contexts in C++ cause evaluation of a
function call,even though no corresponding function call syntax appears i

n the translation unit. 

[ Example: Evaluation of a new expression invokes(借助)
one or more allocation and constructor functions; see 5.3.4. 

For another example,invocation of a conversion function (12.3.2) can arise in
contexts(上下文)in which no function call

 syntax appears.—end example ] The sequencing constraints(约束)
on the execution of the called function (as described above)

are features of the function calls as evaluated, whatever the syntax of the expression that calls the function
might be.

 

总计:尽量不要写轻便产生误会的表明式,因为运算顺序本人就从不严谨规定,所以不用迷信编写翻译器,不要迷信施行结果,有个别职业实在是无可奈何下结论的。所以以前线总指挥部结的演算规律,

也只适合考试的时候看看,要真正主宰规律,依旧须求用VC时按Alt+8、Alt+6和Alt+5,开汇编窗口、内部存款和储蓄器窗口和寄放器窗口看每句C对应的汇编、单步试行并洞察相应内部存款和储蓄器和

贮存器变化(Linux或Unix下得以在用GDB调节和测量检验时,看每句C对应的汇编并单步施行考查相应内存和寄放器变化)。

同不常候有的时候老师说的事物也是不正确的,面前遭遇怎么着难点,都急需有一种刨根问底的神气。

 

参考:http://blog.csdn.net/chenbang110/article/details/9192595

参考:http://bbs.csdn.net/topics/370153775

 

相关文章