K&R的书中一再强调"C is not a big language",当时看书的时候无法理解这句话的意思。现在我的理解是C标准本身的限制比较小,留给程序员的空间较大。这样一来C中的而有些问题标准就没有给出限定,就会产生一些让人迷惑的地方。今天我遇到了一个这样的问题,解决这些问题有的时候看起来是“钻牛角尖”,好吧,我就是一个爱钻牛角尖的人。这里需要解决的问题是执行顺序的问题,先给出几个问题。
执行完这两个语句后i和j的值各是什么?(看着是不是很熟悉,上次笔试考C中是不是有这个呢)
执行完上述语句后,i和数组arry中的值是多少?
输出结果是什么?(来自于C Puzzle Book Operators 16 )
先把这几个问题放在这里,先思考下,下面将会解决他们。
C语言经典著作 “The C Programming Language” 中对于side effects的定义:
这里述说的side effect可以理解为一种“副作用”,这种作用是改变一个变量的值。
“C In Nutshell” 中关于side effects的定义:
相对于K&R中的定义这里使用了对于环境的改变,这应该更加准确。总结:side effects 就是程序中的实体产生的改变,这里所说的实体通常指变量。
赋值,自增,自减表达式会产生side effects,函数调用表达式也有可能产生side effects。
序列点(sequence points)是一种逻辑意义的点,它的意义在于,逻辑点前的副作用(side effects)都在这时生效。C标准中定义的序列点总共有三类,第一类是完全表达式(full expression);第二类是||,&&,:和;第三类是函数调用,在所有的参数确定后、函数真正调用之前。
标准中规定了在前一个序列点前的副作用都会在前一个序列点后完成,但是标准没有规定两个序列点之间的副作用生效的顺序,不同的C语言实现的顺序可能不同。请注意这一点,这是所有问题产生的根本原因。如果两个序列点之间有超过两个的副作用作用在同一个实体上,这样不同的编译器产生的结果就不同,这种情况在标准中称为unspecified 。所以在实际应用中应该避免这种情况的出现,我把这一个原则称为为SS1。
是不是遵守了SS1原则就不会产生unspecified了呢?非也。可以设想这样一种情况:每一个实体(A)在两个序列点之间被两次使用,只有一次对这个实体本身产生副作用,另外一次被间接的用来产生副作用作用于另外一个实体(B)。在前面设想的这种情况下虽然符合SS1原则,但是我们会发现被间接用来产生副作用时,对于实体(B)产生的副作用肯定会跟实体(A)有关,但是这个实体(A)在这个序列点区间中有被副作用作用,那么我们就无法确定这个实体(A)的值了,从而实体(B)也就无法确定了。这里可以归纳为:在两个序列点之间,如果出现对一个实体的多次引用,并且只有一次会对该实体产生副作用(SS1),那么所有的这些引用都必须用来产生这个副作用 ,我把这一个原则称为SS2。只有同时遵守了SS1和SS2,写出的表达式才不是unspecified类型的。
可以清晰的看到标准中使用了两句话来概括这种问题,这正好对应于SS1,SS2原则。
下面给出更为具体的方法:
1)在一个表达式中最多只改变一个实体。
2)如果一个实体在一个表达式被改变,并且出现次数大于一次,请保证所有实体的出现都是为了产生这个“改变”。
例如: i = i+1;
3)如果不能遵守1),那么请保证改变的是不同的实体。
例如:c = p++;
4)如果1)和2)都不能遵守,那么请使用序列点将表达式分开。
例如 : (c = getchar()) != EOF && c != ‘\n’;
C语言中组成程序的基本单位是表达式(expression),表达式是指用 *** 作符(operator)和 *** 作数(operand)连接起来的式子。C标准给出了最基本的 *** 作符,通过这些 *** 作符可以组成简单表达式,同样也可以通过复合产生复杂表达式。当一个表达式中出现多个 *** 作符,多个 *** 作数的时候, *** 作符合 *** 作数是如何组合起来的呢?优先级和结合方向就是用来解决这个问题的,可以这么说,优先级和结合方向给出了一个表达式的含义,这只是说明了各个 *** 作符和 *** 作数是怎么聚合起来的。
仅仅依靠优先级和结合方向是无法确定一个复合表达式中对各个子表达式的求值顺序。标准中对于这点的规定是:
两个相邻的 *** 作符的执行顺序由它们的优先级决定。如果它们优先级相同,它们的执行顺序由它们的结合性决定。除此之外,编译器可以自由决定任何顺序对表达式进行求值,只要它不违反逗号,&&,||和?: *** 作符所施加的限制。
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;
通过本课题的学习加深了对于c的理解,理解SS1,SS2原则。在实际应用中应该遵守“一条语句只做一件事的原则”。
int x=1;
x=(x=1+2,x2);
赋值符号“=”优先级别比“,”逗号表达式要 高
则
(x=1+2,x2)=>6;
x=6
结果为x=6
可以放在後面,但是main使用前要做声明,即如下:
main() /主函数/
{
int max(int x, int y);
int a,b,c;/申明部分,定义变量/
scanf("%d,%d",&a,&b);/输入变量a和b的值/
c=max(a,b);/调用max函数,将得到的值赋给c/
printf("max=%d\n",c);/输出c的值/
}
纯手打的,望采纳。
一个C语言程序总是从main()主函数开始执行。main是相对来说的,如同音学理论之主调于泛音,泛音即程序中的除main之外的其他函数,迎合人们的思考方式而生成的而非必定的模式。有主有次,执行起来条清缕析,既可将程序模块化又实现了一个闭合的整体。
main 函数在程序启动中完成对具有静态存储期的非局部对象的初始化之后被调用的。它是程序在有宿主 (hosted)环境(亦即有 *** 作系统)中所指定的入口点。自立程序(启动加载器, *** 作系统内核,等等)的入口点则由实现定义的。
主函数的两个形参形式中的形参,允许从执行环境中传递任意的多字节字符串(它们通常被称为命令行参数),各个指针 argv[1] argv[argc-1] 指向每个这些字符串的第一个字符。argv[0] 是指向一个表示用于执行该程序自身的名字的空结尾多字节字符串(或者当执行环境不支持时,为空字符串 "")的开头字符的指针。
这些字符串是可以改动的,虽然对它们的改动并不会被传回给执行环境:比如可以用 std::strtok 来使用它们。由 argv 所指向的数组的大小至少为 argc+1,其最后一个元素 argv[argc] 保证为一个空指针。
扩展资料
main 函数的返回值类型必须是 int ,这样返回值才能传递给程序的激活者(如 *** 作系统)。
如果 main 函数的最后没有写 return 语句的话,C99 规定编译器要自动在生成的目标文件中(如 exe 文件)加入return 0; ,表示程序正常退出。不过,建议在main函数的最后加上return 语句,虽然没有这个必要,但这是一个好的习惯。
也就是说在最新的C语言标准中强制要求main函数的返回值类型为int,main函数的返回值是传递给 *** 作系统,让 *** 作系统判断程序的执行情况(是正常结束还是出现异常)。
c语言是顺序执行语言,意思就是按代码出现的顺序一句一句执行,所以
double qs,bl,q;
q=qsbl;
这是qs跟bl还没初始化,一般就是一个很大的随机数,因此此时计算得到的q就是一个很大很大的数了,当你把这句代码放后面:
printf("请输入倍率 \n");
scanf("%lf",&bl);
q=qsbl;
因为前面已经给qs跟bl读入了指定的值,所以这时候再去计算q,就是正确的结果了
以上就是关于【C语言】优先级,结合方向和执行顺序全部的内容,包括:【C语言】优先级,结合方向和执行顺序、c语言执行顺序 求教、c程序中的 函数执行顺序等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)