鱼C论坛

 找回密码
 立即注册
12
返回列表 发新帖
楼主: sdhu

[技术交流] C陷阱与缺陷读书笔记

[复制链接]
 楼主| 发表于 2014-5-5 12:17:12 | 显示全部楼层
本帖最后由 sdhu 于 2014-5-5 12:27 编辑

3.5 求值顺序
        前面我们讨论过运算符的优先级问题,但是,求值顺序则是完全另一码事.求值顺序是另一类规则.可以保证像下面的语句.
        if(x!=0 && y/x < small)
                printf("average < %g\n",small);
        即使当变量x为0时,也不会产生一个用"0做除数"的错误.
        
        i=0;
        while(i<tablesize && tab[i]!=x)
                i++;
        /*上面的代码和下面的代码实现的功能等效.
        for(i=0;i<tablesize;i++)
        {
                 if(tab==x)
                         break;
          }
        */
        这个循环语句的用意是,如果i等于tablesize时循环终止,就说明在表中没有发现要找的元素,而如果是其他的情况,此时i的值就是要找的元素在表中的索引.
        C语言中的某些运算符总是以一种已知的,规定的顺序来对其操作数进行求值,而另外一些则不是这样的.
        C语言中只有4个运算符(&&,||,?:,和,)存在规定的求值顺序,运算符&&和||首先对左操作数求值,只有在由左操作数无法确定整个表达式的值时才对右操作数求值.运算符?:有三个操作数: a?b:c; 操作数a首先被求值,根据a的值再去确定表达式的值是b或者c,而逗号表达式,首先 对左操作数求值,再对右操作数求值,并且表达式的值是右操作数的值.
        
        注意:分隔函数的参数的逗号并非逗号运算符.例如,x和y在函数f(x,y)中的求值顺序是未定义的,而在函数g((x,y))中却是确定的先x后y的顺序在后一个例子中,函数g只有一个参数,这个参数的值是这样求得的,先对x求值,然后x的值被"丢弃",接着求y的值并当做函数的实参.
        例如:当两个逻辑量a||b时,且a为真时则不再求b的值,而取值为真(即1);当两个逻辑量a&&b时,且a为假时则不再求b的值,而取值为假(即0)同理a&&(b++)&&c式中当a为假时,直接取值为假(即0),此时b++操作就没有进行,下一条语句中b仍为原值。
    例子:int i=0;   int j=0;
          if((++i>0)||(++j>0))
                  {
                          printf("i %d,j %d\n",i,j);
                  }
                  结果为:i 1,j 0
-------------------------------------------------------------------------------------------------------------------------------
3.8 整数溢出
        C语言中存在两类整数算术运算.有符号运算和无符号运算.在无符号算术运算中,不存在"溢出"一说,不过有"进位"的说法.
        如果算术运算符的一个操作数是有符号的整数,而另外一个是无符号的整数,那么有符号整数会被转换为无符号整数,运算结果也是无符号的. 则"溢出"就不可能发生,但是,当两个操作数都是有符号的整数的时候,"溢出"就有可能发生.而且"溢出"的结果是未定义的.即当一个运算的结果发生溢出时,作出任何假设都是不安全的.
        例如,假定a和b是两个非负整形变量,我们需要检查a+b是否会"溢出".一种正确的方式是将a和b都转换为无符号整数:
        if((unsigned)a+(unsigned)b>INT_MAX)  
            ........
        此处的INT_MAX是一个已定义的宏常量,代表可能的最大整数值,ANSCI中在头文件<limits.h>中定义了INT_MAX;
        不需要用到无符号算术运算的另一种可行的办法是:
        if(a > INT_MAX - b)
                ........
        
        当两个整数运算发生溢出的时候,这个运算就会发生错误,从这里我们就引出了大数加减法,以及大数相乘的相关的运算.
-------------------------------------------------------------------------------------------------------------------------------
3.9 为main函数提供返回值
        通常,在某些情况下,函数main的返回值并非无关紧要,大多数的C语言实现都通过函数main的返回值来告知操作系统该函数的执行是成功还是失败,典型的处理方案是:返回值为0表示函数执行成功,返回值非0则表示程序执行失败.如果正在使用一个软件管理系统,该系统关注程序被调用后执行是成功还是失败,那么很可能得到令人惊讶的结果.
        
        现在我们来看看操作系统调用main函数前后都发生了什么.(以下的内容摘自CSDN)
一种解释
    实际上,在可执行文件被加载之后,控制权立即交给由编译器插入的Start函数,它将对后面这些全局变量进行准备:
   _osver        操作系统的构件编号
      _winmajor   操作系统的主版本号
      _winminor  操作系统的次版本号
      _winver      操作系统完全版本号
      __argc        命令行参数个数
     __argv      指向参数字符串的指针数组
      _environ   指向环境变量字符串的指针数组
     Start函数初始化堆并调用main函数.mian函数返回之后,Start函数调用Exit函数结束该进程.
另一种解释
        Some of the stuff that has to happen before main():
        set up initial stack pointer
        initialize static and global data
        zero out uninitialized data
        run global constructors

        Some of this comes with the runtime library's crt0.o file or its __start() function. Some of it you need to do yourself.Crt0 is a synonym for the C runtime library. Depending on the system you're using the follwing may be incomplete, but it should give you an idea. Using newlib-1.9.0/libgloss/m68k/crt0.S as an outline, the steps are:
        1. Set stack pointer to value of __STACK if set
        2. Set the initial value of the frame pointer
        3. Clear .bss (where all the values that start at zero go)
        4. Call indirect of hardware_init_hook if set to initialize hardware
        5. Call indirect of software_init_hook if set to initialize software
        6. Add __do_global_dtors and __FINI_SECTION__ to the atexit function so destructors and other cleanup functions are called when the program exits by either returning from main, or calling exit
        7. setup the paramters for argc, argv, argp and call main
        8. call exit if main returns
第三种解释:囫囵C语言(三):谁调用了我的main?
    现在最重要的是要跟得上潮流,所以套用比较时髦的话,谁动了我的奶酪。谁调用了我的 main?不过作为计算机工作者, 我劝大家还是不要赶时髦,今天Java热,明天 .net 流行,什么时髦就学什么.我的意思是先花几年把基本功学好,等你赶时髦的时候也好事半功倍。废话不多说了。
    我们都听说过一句话:“main是C语言的入口”。我至今不明白为什么这么说。就好像如果有人说:“挣钱是泡妞”,肯定无数砖头拍过来。这句话应该是“挣钱是泡妞的一个条件,只不过这个条件特别重要”。那么上面那句话应该是 “main是C语言中一个符号,只不过这个符号比较特别。”
    我们看下面的例子:
    
    /* file name test00.c */
    int main(int argc, char* argv)
    {
             return 0;
    }
    编译链接它:
    cc test00.c -o test.exe
    会生成 test.exe
    
    但是我们加上这个选项: -nostdlib (不链接标准库)
    cc test00.c -nostdlib -o test.exe
    链接器会报错:
    undefined symbol: __start
    
    也就是说:
    1. 编译器缺省是找 __start 符号,而不是main
    2. __start 这个符号是程序的起始点
    3. main是被标准库调用的一个符号
    
    再来思考一个问题:
    我们写程序,比如一个模块,通常要有initialize和de-initialize(去初始化),但是我们写C程序的时候为什么有些模块没有这两个过程呢?比如我们程序从 main 开始就可以 malloc,free,但是我们在 main 里面却没有初始化堆。
             再比如在 main 里面可以直接 printf,可是我们并没有打开标准输出文件啊。
             (不知道什么是 stdin,stdout,stderr 以及 printf 和 stdout 关系的群众请先看看 C 语言中文件的概念)。
    聪明的人民群众会想,一定是在 main 之前干了些什么。使这些函数可以直接调用而不用初始化。通常,我们会在编译器的环境中找到一个名字类似于 crt0.o 的文件,这个文件中包含了我们刚才所说的 __start 符号。
              crt 大概是 C Runtime 的缩写,请大家帮助确认一下。)
    那么真正的 crt0.s 是什么样子呢?下面我们给出部分伪代码:
    ///////////////////////////////////////////////////////
    section .text:
    __start:
    
     :
     init stack;      //初始化栈顶指针
     init heap;       //初始化堆
     open stdin;      //打开标准输入,输出和标准错误文件
     open stdout;
     open stderr;
     :
     push argv;
     push argc;
     call _main; (调用 main)
     :
     destory heap;
     close stdin;
     close stdout;
     close stderr;
     :
     call __exit;
    ////////////////////////////////////////////////////
    实际上可能还有很多初始化工作,因为都是和操作系统相关的,笔者就不一一列出了。
    注意:
    1. 不同的编译器,不一定缺省得符号都是 __start。
    2. 汇编里面的 _main 就是 C 语言里面的 main,是因为汇编器和C编译器对符号的命名有差异(通常是差一个下划线'_')。
总结:
main函数执行之前,主要就是初始化系统相关资源:
        1.设置栈指针
        2.初始化static静态和global全局变量,即data段的内容
        3.将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容
        4.运行全局构造器,估计是C++中构造函数之类的吧
        5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数
        
[/i]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-5-5 12:27:58 | 显示全部楼层
第三章读书笔记更新完毕
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-5 13:20:30 | 显示全部楼层
好东西!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-5 13:31:51 | 显示全部楼层
谢谢楼主!!!!!!!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-5-6 00:08:56 | 显示全部楼层
今天受打击了   唉唉   ACM  果然不是那么好水的啊   
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-5-6 00:10:10 | 显示全部楼层
还是打算开始学算法吧    算法啊   真烦  !!!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-5-6 00:11:16 | 显示全部楼层
就差一分了   水水等级升鱼I   还是要高兴的   
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-6 08:26:24 | 显示全部楼层
谢谢楼主的总结
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-6 08:45:07 | 显示全部楼层
学习................
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-6 09:15:05 | 显示全部楼层
看看隐藏的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-6 09:16:30 | 显示全部楼层
看了几行,好像很深奥
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-6 21:25:27 | 显示全部楼层
这个可以有..............
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-14 14:41:12 | 显示全部楼层
感谢楼主分享
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-15 08:53:51 | 显示全部楼层
不管你信不信,反正我是信了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-24 08:37

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表