C语言序列点总结

2013年11月21于浙大华家池

C 语言副作用:

(side effect)是指对数据对象或者文件的修改。

例如,语句 v = 99;的副作用是把 v 的值修改成 99。

C语言序列点:

(sequence point)是指程序运行中的一个特殊的时间点,在该点之前的所有副作用已经结束,并且后续的副作用还没发生,而两个序列点之间所有的表达式或代码执行顺序是未定义的。

1). 一个重要的序列点在完整表达式的结尾(即分号),所谓完整表达式,就是说这个表达式不是子表达式。而所谓的子表达式,则是指表达式中的表达式。也就是说,C 语句中由赋值、自增或者自减等引起的副作用在分号(序列点)之前必须结束。

例如:
a= ++b % 3;

这整个表达式就是一个完整表达式。这个表达式中的 ++b、3 和 ++b % 3 都是它的子表达式。

有了序列点的概念,我们下面来分析一下一个很常见的错误:
int x = 1, y;
y = x++ + x++;
这里 y = x++ + x++ 是完整表达式,而 x++ 是它的子表达式。这个完整表达式运算结束的那一点是一个序列点,int x = 1, y; 中的 ; 也是一个序列点。也就是说,x++ + x++ 位于两个序列点之间。C标准规定,在两个序列点之间,一个对象所保存的值最多只能被修改一次。但是我们清楚可以看到,上面这个例子中,x 的值在两个序列点之间被修改了两次。这显然是错误的!这段代码在不同的编译器上编译可能会导致 y 的值有所不同。比较常见的结果是 y 的值最后被修改为 2 或者 3。

2). 逗号表达式。逗号表达式会严格的按照顺序来执行并且每一个逗号分隔都是一个序列点,所以,前一个逗号表达式如果是i++,则后面的表达式可以肯定现在的值是原来的值加1(如果有溢出则另当别论)。

如:
int i = 1;
i++, i++, i++;
printf("%d\n", i);
现在的i肯定是4;

那么以下结果呢(注可以在程序编译的时候在上-Wsequence-point选项):

int a = 1, b = 3;

a = 10, a++, b++;

printf(“a = %d, %d\n”, a, b);

3). &&和||运算符。有一种短路算法,即&& 和 ||序列点必须先确定左边表达式的值。

如下:
int a = 10;
int b = 0;
if (b && a/b)
{ /* some code here */ }
其中在求b的值的时候会短路,即,a/b不会执行。因为b的值为0,但是在此处我们可以放心的使用除法。&&,||这两个运算符在使用的时候都可以当成一个序列点,如果前一个表达式的值已经可以认定这整个表达式的值为真或者为假,则后面的表达式没有必要再求值,是多余的。即如上面的a/b是多余的,不能求值,求值也会出错。它们之间的求值顺序是肯定的。

再比如:

int a,b,c;

a=b=c=1;

++a  ||  ++b  &&  ++c;问执行后a、b、c的值各是多少?

在此处虽然&&的优先级比||高,但是||, &&都是序列点,所以只有||序列点之前的执行完后才会执行&&操作。(注意C语言没有规定计算顺序,当然也不是谁的优先级高就先执行谁, 先确定优先级高的结合性)。

4). 条件运算符“? :” 在问号(?)的地方也存在一个序列点,即问号前后可以访问和改变同一个变量,并且这种访问是安全的。

例如:

int  a = 1, b = 4;

a > b ? ++a : ++b;

如果条件操作符没有序列点,语句a > b ? ++a : ++b;执行时,++a和++b会先于子表达式a > b执行; 但是,条件操作符?:的问号处有序列点,所以语句” a > b ? ++a : ++b; “执行时,问号处?左边的操作数a > b先执行,值为真时,对++a求值,不对++b求值;值为假时,正好相反。

那么请分析:

int a = 0, b= 2;

int m = a++ ? b : a ;

再分析:

int a = 1,  b = 2;

a > b ? a : a+2;

5). 函数调用时,实参表内全部参数求值结束,函数的第一条指令执行之前(注意参数分隔符“,”不是序列点);

int  a = 3;

printf(“%d, %d\n”,  a,  a++);

以上结果是未定义的

最后,在一个表达式内的求值顺序没有固定顺序,还有一个表现是,如下:
  funa() + funb() + func();
  C语言标准没有规定这三个函数谁会先执行,如果对顺序有要求,可以用临时变量来缓解。

写到这里想起一道题,原来以前我的理解和解释都是错的, 关键问题是此题三个fun函数计算顺序是未定义的

 int fun()
{
static int a = ;
return a++;
}
int main()
{
int sum = fun() - fun()*fun();
printf("sum = %d\n", sum); return ;
}

注意:

1)C 语句中由赋值、自增或者自减等引起的副作用在序列点之前必须结束

2)优先级只影响结合性,与计算顺序无关

3)复杂表达式分析:一分析结合性,二分析序列点

4)c语言为了高效性没有规定计算顺序,这样给了编译器更大的优化空间

警告:

定义序列点是为了尽量消除编译器解释表达式时的歧义,未定义情况后果程序猿自负。

C语言序列点问题总结(大多数高等教育C语言教学课程的漏洞)的更多相关文章

  1. C 语言学习的第 02 课:C 语言的开发环境

    工欲善其事,必先利其器.不知道还是不是记得上一篇文章中说到的,计算机本身是一个数据输入及输出的设备.所以,为了将你大脑中的各种 idea 输入到电脑,且最终生成能够执行的程序,总是要预备点什么的. 通 ...

  2. 快速实现Magento多语言的设置和产品数据的多语言方法

    MagenTo默认支持多语言网店,不过要使用多语言功能,需要进行一些设置. 一.后台多语言支持(中文化) Magento登录后台时默认的是显示的是英文界面,在页面左下角选择语言为中文就会跳转为中文界面 ...

  3. 概率图模型 基于R语言 这本书中的第一个R语言程序

    概率图模型 基于R语言 这本书中的第一个R语言程序 prior <- c(working =0.99,broken =0.01) likelihood <- rbind(working = ...

  4. SharePoint2013与SharePoint2016语言切换原理以及如何使用代码进行语言切换

    1.前言 在SharePoint 2010版本,在首页面直接"选择显示语言"的菜单(如下图所示),如下图 : 在sharepoint2013和sharepoint2016并非如此. ...

  5. 趣谈编程史第2期-这个世界缺少对C语言的敬畏,你不了解的C语言科普

    这是我制作的编程语言科普系列视频的第二期,博客根据视频文案整理而成,提供给有需要的朋友阅读或使用. 视频地址:https://www.bilibili.com/video/av83627932/    ...

  6. C\C++语言重点——指针篇 | 为什么指针被誉为 C 语言灵魂?(一文让你完全搞懂指针)

    本篇文章来自小北学长的公众号,仅做学习使用,部分内容做了适当理解性修改和添加了博主的个人经历. 注:这篇文章好好看完一定会让你掌握好指针的本质! 看到标题有没有想到什么? 是的,这一篇的文章主题是「指 ...

  7. Python 语言特性:编译+解释、动态类型语言、动态语言

    1. 解释性语言和编译性语言 1.1 定义 1.2 Python 属于编译型还是解释型? 1.3 收获 2. 动态类型语言 2.1 定义 2.2 比较 2. 动态语言(动态编程语言) 3.1 定义 3 ...

  8. C语言序列点浅析

    摘要: 现行国内的C语言教材普遍不介绍序列点,这使得读者只能“死记硬背”有序列点表达式的求值顺序,不仅造成了读者对C语言知识的认知残缺不全,而且也影响了读者学习的积极性.本文总结了序列点的作用,即表达 ...

  9. C语言 序列反向互补函数

    1 static char *revers(char *s) 2 { 3 int len=strlen(s); 4 char *s2=(char *)malloc(sizeof(char)*(len+ ...

随机推荐

  1. 【CF1073C】Vasya and Robot(二分,构造)

    题意:给定长为n的机器人行走路线,每个字符代表上下左右走,可以更改将一些字符改成另外三个字符,定义花费为更改的下标max-min+1, 问从(0,0)走到(X,Y)的最小花费,无解输出-1 n< ...

  2. Netbeans 8.2启动参数含义及配置

    在manjaro linux中Netbeans8.2 + JDK 1.8 netbeans的配置文件具体在:/usr/share/netbeans/etc/netbeans.conf,需要使用root ...

  3. linux2.4内核调度

    进程调度需要兼顾3种进程:交互进程,批处理进程,实时进程,在设计一个进程调度机制时需要考虑具体问题 (1)调度时机? 答:进程在用户空间可以pause()或者让内核设置进程为睡眠状态,以此调度,调度还 ...

  4. 開關(switch)規格中,SPDT, DPDT, 3PDT, 4PDT是什麼意思?

    Reference http://blog.xuite.net/auster.lai/twblog/433393966 開關(switch)用來控制電路的開與閉,機械式的switc內部是由接點開關.彈 ...

  5. Codeforces Gym100735 E.Restore (KTU Programming Camp (Day 1) Lithuania, Birˇstonas, August 19, 2015)

    E - Restore Given a matrix A of size N * N. The rows are numbered from 0 to N-1, the columns are num ...

  6. Codeforces 777E Hanoi Factory(线段树维护DP)

    题目链接 Hanoi Factory 很容易想到这是一个DAG模型,那么状态转移方程就出来了. 但是排序的时候有个小细节:b相同时看a的值. 因为按照惯例,堆塔的时候肯定是内半径大的在下面. 因为N有 ...

  7. 某考试 T1 fair (18.5.1版)

    转化一下模型:每天可以选1也可以选0,但是任意前i天(i<=n)1的个数都必须>=0的个数,求总方案数/2^n. 然后可以发现这是一个经典题,随便推一下公式发现等于  C(n,n/2)/2 ...

  8. SecureCRT分屏显示

    [Tab右键]或者[Session Manager右键]->[Send to New Tab Group]

  9. sudo如何保持当前用户的环境变量?

    现象,我在/etc/profile里设置全局代理,然后使用命令 1.curl http://www.baidu.com  走代理 2.sudo curl http://www.baidu.com 并没 ...

  10. Ubuntu下sudo apt-get install vim 失败的解决办法

    Ubuntu下 执行命令:sudo apt-get install vim 失败 解决办法: 更新一下,命令:sudo apt-get update 再安装即可成功:sudo apt-get inst ...