C深度解析

<<C深度解析>> 
第一章 C编译、链接
基础知识铺垫
1 extern和static的作用
(1)static
    1)修饰函数
        static修饰函数时表示,该函数只在本文件有效,也就是只能被本文件的其他函数调用,
        其他文件不能调用(引用)。
        
        例子:
        a.c                           b.c
        int fun1(void)                static void fun(int stu_num)
        {                             {
            fun(100);//不能引用          ......
        }                             }
        static就像是一把锁,把函数fun的有效范围锁在了b.c,其他.c看不见这个函数,自然无法调用
        
        疑问:为什么static能锁住函数?
        答:因为static将函数名这个符号变成了“本地符号”,“本地符号”只在本文件可见,其他文件是看
        不见的,本章后续还会详细的介绍什么是本地符号,这里只是在做知识铺垫。
    2)修饰变量
        1)static修饰局部变量
        改变的是局部变量的存储位置,从栈变为静态存储区,与“本地符号”没有关系
        比如:
        int fun(int va)
        {
            int a;
            int b = 100;
            ...
        }
        a、b为“自动局部变量”,空间开辟于栈中,也许你对“自动局部变量”这个名字不熟悉,但是
        对所讲的这种情况绝对不陌生。
        
        加static修饰:
        int fun(int va)
        {
            static int a;
            static int b = 100;
            ...
        }
        
        a/b变为了“静态局部变量”,a/b的空间开辟于静态存储区,而不是栈。
        (a)如果没有初始化的话,比如a就没有初始化,a的空间在静态区中的.bss区。
        (b)如果有初始化的话,比如b就有初始化,b的空间在静态区中的.date区。
        
        2)static修饰全局变量
            与修饰函数一样,让全局变量只在本文件有效,其他文件无法引用。
            比如
            a.c 
            gVa = 200;//不能引用 
            
            b.c 
            static int gVa = 100;
            int fun(void)
            {
                ......
            }
        b.c中的gVa被static修饰后,和被static修饰的函数一样,gVa变为了本地符号,只在本文
        件有效,不能被其他文件使用。
        
        static修饰全局变量时,与全局变量的存储位置无关系,全局变量的空间本来就是在静态
        存储区,加不加static,并不能改变存储位置,static只是改变了符号的有效范围,变为
        了本地符号。
        
        大家可以看出,static这个关键字好像有点乱,没有固定套路,修饰变量和函数时,所起
        的作用不同,这个应该说是当初设计C语法时没有设计好导致的,使得static的用法有点乱,
        不过没有关系,跟着我们的课程往后走,熟悉了后,你会发现其实很简单。
(2)extern
    1)修饰函数
        extern修饰的函数,表示除了能被本文件引用外,还可以被其他的文件引用,在其他文件中
        是可见的,只不过在其他文件中引用时,需要做声明。刚好与static相反。
        a.c 
        extern void fun(int stu_num);  //声明
        
        extern int a_fun(void)
        {
            fun(100);
        }
        
        b.c
        
        extern void fun(int stu_num)
        {
            ......
        }
        事实上extern不需要明确写出,因为默认就是extern的,也就是说定义和声明可以写成如下形式
        a.c 
        void fun(int stu_num);  //声明
        
        int a_fun(void)
        {
            fun(100);
        }
        
        b.c
        
        void fun(int stu_num)
        {
            ......
        }
        
        疑问:extern到底做了什么事情?
        答:extern修饰函数时,与static修饰函数时的作用刚好相反,extern修饰函数时表示,
        函数名是一个“全局符号”,“全局符号”的特点是,除了本文件可以引用外,在其他文件中
        也是“可见”的,只不过其他文件使用时,需要做声明,注意声明也必须是extern的。
        
    2)修饰变量
        extern修饰变量时,主要用于修饰全局变量,extern修饰局部变量的情况非常少见,所以
        暂时不介绍。
        修饰全局变量时,与修饰函数一样,其实就是将全局变量名这个符号标记为“全局符号”,对
        其他文件来说也是可见的,不过同样的,其他文件引用时,需要做声明。
        
        a.c 
        extern int gva;
        gva=200; //引用
        
        
        b.c 
        extern int gva = 100;
        int fun(void)
        {
            ......
        }

第一章、C语言编译过程与内存结构
0. 内容安排

    1.IDE开发方式与源文件开发方式
    2.C程序的详细编译过程
    3.静态链接与动态链接
    4.目标文件种类
    5.可重定位目标文件
    6.可执行目标文件
    7.内存结构(存储映像)
        
1. IDE开发方式与源文件开发方式
    1.1 演示
      1.1.1 IDE方式
      1.1.2 源文件开发方式
            其实就是编辑与编译分开操作的方式
            gcc helloworld.c helloworld.exe 
        (1)windows
            如果想在任意位置都可以编译我们的程序的话,我们需要将编译器所在的目录加入
            windows的环境变量,有关环境变量,我们在《Linux系统编程、网络编程》有详细介绍
            
        (2)ubuntu
            1)使用ubuntu的VI或者gedit编辑器编辑
            2)使用ubuntu自带的gcc编译器来编译
            gcc helloworld.c -o helloworld
            gcc这个编译器是GNU这个组织开发的
            
         总结:
         (a)ubuntu安装好后,会自带c编译器gcc,不过windows没有自带c编译器,所以我使用的
         是codeblocks所带的编译器
         (b)ubuntu下的安装目录有加入环境变量,因此可以在任何路径下运行gcc
    1.2 源文件开发方式
      1.2.1 什么是源文件开发方式
        其实就是编辑、编译是分开开发的方式
        
        编辑:使用单独的编辑器来编写程序
        编译:在命令执行编译程序(命令)来编译所写的c程序
      1.2.2 分开的开发方式,其实有不少麻烦之处
        (1)你需要安装两个东西,一个是编辑器,另一个是编译器
        (2)使用单独编辑器,也有缺点
            使用的编辑器不见得是专门针对程序开发的编辑器,使用起来很麻烦,你比如直接
            使用windows自带的txt文件编辑器写程序的话,老实讲用起来糟心,因为它不是为程序
            编写而生的。
            如果你觉得txt这个编辑器不好,可以下载一个你觉得比较好的其它的编辑器,就算是
            这样,编辑器相比IDE的编辑器来说,也差了一些,毕竟IDE的编辑器是专门针对程序开发的
            
        (3)自己安装编译器也很麻烦
        (4)命令行的使用也很麻烦
        
      1.2.3 IDE方式
        IDE方式其实就是将编辑器和编译器二合一后的结果
        IDE方式很好的解决了编辑和编译分开情况所带来的麻烦,怎么解决的呢?
        
        1.2.3.1 只需一次性安装IDE即可
            一次性安装好IDE后,IDE就自带了编辑器和编译器
        1.2.3.2 IDE自带的编辑器更加专业,更加人性化
            因为IDE自带编辑器,是专门适合程序开发的编辑器,比如我们在程序开发的时候,
            语法关键字的自动提示,一般的编辑器是没有的,但是IDE提供的编辑器就会有。
        
            我在写程序时往往记不住关键字,特别是C++和java这种面向对象语言的开发,类里面
            包含很多的成员,如果编辑器不提供自动提示功能,这会非常痛苦。
            
            特别是涉及到界面开发时,会进行到控件的拖拽,如果不使用IDE提供的专门的编辑器
            的话,普通编辑器根本不提供控件拖拽的功能
        1.2.3.3 不涉及编译器的选型问题
            安装某个环境(windows x86)的IDE时,IDE自带的就是这个环境的编译器,不需要
            操心编译器的选型。
            编译器并不是一个独立的程序,而是由众多程序组合在一起的一个程序集合,我们安装
            IDE时,编译器集合会被自动包含在IDE的安装目录下。
            举例:codeblocks、vc++、eclipse
            如果你觉得这个编译器不好,实际上你可以自己下载其他的编译器,然后将它包含到IDE
            的安装目录下,然后在IDE里面做设置,使用这个新的编译器即可。
            设置举例:codeblocks
            不过一般不会这么做,你要是觉得这个编译器不好,还不如干脆换一个新版本的IDE,会
            自带新的编译器。
        1.2.3.4 编译程序时,调用编译器的过程是IDE自动完成的,而且是图形化操作,不需要使用
            命令行来操作。
            比如以codeblocks为例,只需要点击编译运行的按钮即可,IDE会自动的调用编译器程序
            来编译。
            图形界面调用编译器,与我们在命令行调用编译器程序来编译是一样的,但是IDE提供
            的图形化方式显然更加方便。
    1.3 综合对比
        1.3.1 IDE是主流
            因为IDE确实更加方便,特别是像开发c++/jave/c#/python等这类面向对象语言的程序
            时,基本只能使用IDE方式来操作,因此必须使用IDE的自动提示功能。
            
        1.3.2 分开方式难道就没有用了吗
            不是的,对于底层框架类代码的开发来说,还是有用的,因为底层框架类的代码基本
            都是使用c、汇编这类语言来实现的,不涉及类成员的提示,不涉及界面控件的拖拽,完全
            可以使用这种方式来开发。
            其实就算是开发框架代码,如果使用IDE方式的话,其实更方便些,不过对于很多有能力
            的程序员来说,在开发框架代码时,有些还是喜欢使用编辑、编译分开的方式,为什么?
            
            1)他们有能力这么做
                其实编辑、编译分开的方式,并不是谁想使用就能用的,这种方式对于能力要求较高,
                比如最起码一点,命令行必须要会,但是就命令行这一点,不少开发者可能就被难住了。
            2)分开的方式更自由
                IDE做的越好就越傻瓜化,傻瓜的好处是,对程序员来说使用特别简单,但是坏处是,
                IDE帮你自动做了太多的设置,但是有些设置根本不是你想要的,所以很多大牛不喜欢
                IDE的自作主张,所以他们更喜欢自己做主,而编辑、编译分开的方式,就完全可以按照
                自己的想法来,不受IDE的干扰。
    1.4 进行Linux嵌入式开发,为什么会使用编辑、编译分开的方式呢?
        (1)因为进行Linux嵌入式开发时,所开发的大多是c程序,而且大多不涉及界面问题(因为
        好多嵌入式设备没有界面),这种情况与开发框架非常类似,所以适合使用这种情况。
        (2)ubuntu自带的vi(命令行编辑器)和gedit(图形编辑器)用起来还可以,而且安装好
        ubuntu后,会自带针对当前环境的c编译器gcc,无需自己安装,所以省去了一些麻烦。
        当前后续开发在arm上运行的嵌入式程序时,还是需要我们自己安装针对Linux、arm环境的
        编译器。
        (3)编辑、编译分开开发的方式,依赖于命令行的使用,而Linux嵌入式开发有一个最大的特点
        是,要求我们必须会Linux的命令行,所以对于Linux开发者来说,命令行的使用是小意思。
        (4)编辑、编译分开的开发方式,是本源开发方式,通过使用这种开发方式,可以更好的理解
        一些本质性的问题,在一定程度上,有助于提升大家的能力,所以我们鼓励使用分开开发的方式。
    
    1.5 如果Linux嵌入式开发也涉及到C++、java的话,使用什么开发方式呢
    
        1.5.1 嵌入式开发会用到C++/java吗?
        会,因为虽然不少Linux嵌入式产品不需要图形界面,但是有些还是要的,此时我们会使用
        C++的qt图形库或者java图形库来开发。
        其实基本使用的是C++/QT来开发的,因为java需要运行虚拟机这个东西,比较吃计算机资源。
        如果开发的是与安卓相关的嵌入式产品的话,会用到java。
        
        1.5.2 在Linux怎么进行C++/java开发
        前面说过开发C++/jave等面向对象语言的程序时,都是使用IDE来开发的。
        
        所以如果在linux嵌入式开发中,涉及到C++/java程序的开发时,我们还是需要安装相应的
        IDE来开发,使用编辑,编译分开方式,基本不可能。

2. c程序的编译        
    编译的四个过程:预处理、编译、汇编、链接
    
    四个过程中的“编译”,特指其中的某个过程,这四个过程合在一起,我们也统称为编译,所以
    “编译”二字到底指的是第二个过程,还是全部过程的统称,这个就要看说话的“语境”了
    
    其实统称的“编译”,完整的称法应该叫“编译链接”,只是简称为编译而已。
    预编译:把(.h .c)变成.i 
    编译:把.i变成.s 
    汇编:把.s变成.o
    链接:把所有的.o组合在一起,得到一个完整的可执行文件a.out
    2.1. 预编译(预处理)
    之所以叫预编译,表示为正式的编译做准备,预编译也被称为预处理。
               预处理(预编译)
    (1)**.c ------------------>**.i
              如果编译过程是一次性完成的话,.i文件只是一个过渡性文件
              
              .i被称为扩展后的c源码文件
              
              为什么还叫c源码文件呢?
                因为预处理后,只是宏定义等东西不见了,但是C源码依然还在,比如main函数,
                各种自己写的函数,依然存在,所以还是被称为c源码文件。
                
                打开.i文件后,我们是能够看得懂的,所以.i文件还是文本文件或ascii文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值