文件包含

来源:互联网 发布:人工智能网络营销 编辑:程序博客网 时间:2024/04/16 12:35

——————————————————————————————————————
符号                      意义
——————————————————————————————————————
static        1.在函数内部,表示该变量的值在各个调用间一直保持延续性
  2.在函数这一级,表示该函数只对本文本可见
——————————————————————————————————————
extern  1.用于函数定义,表示全局可见(属于冗余的,默认全局可见)
  2.用于变量,表示它在其他地方定义
  3.用于函数,表示它在其他地方定义
——————————————————————————————————————

什么是声明,什么是定义?
    C语言中的函数或变量有且只有一个定义,但它可以有多个extern声明。唯一性
    规则也适用于常数定义,结构定义,类型定义,宏定义,只不过它们默认只对本
    单元可见;而函数和变量(函数外)的定义默认是全局可见的。

Linux0.11的目录:
      |-boot/
      |-fs/
      |-include/
        |-asm/
        |-linux/
        |-sys/
      |-init/
      |-kernel/
        |-blk_drv/
        |-chr_drv/
        |-math/ 
      |-lib/
      |-mm/
      |-tools/
      |-Makefile

头文件(包括)
    常数定义
    宏定义
    类型定义
    数据结构
    变量声明
    函数声明(extern inline函数算在内)
   
    头文件中包含的一切内容都只是对本编译单元可见的。如果要使用,就include
    此头文件,也可以不include而采用另外的定义。
   
    1.变量和函数使用之前都要声明。另外如果该函数只在本单元内使用,可以不用
      声明,但定义要在调用之前。如kerner/signal.c中的函数save_old定义在前,
      然后只在sys_sigaction中调用,而且save_old有限定词static。头文件一般
      包含“常数定义,宏定义,数据结构,函数声明,变量声明”。变量定义是不可
      能包含在头文件中的,因为无论函数或者变量只能定义一次。
    2.#define宏定义的作用域在源文件之内,既可以在头文件中定义,也可以在.c文件
      中定义。比如set_bit在fs/bitmap.c和fs/super.c中都有定义,而且不同,当然
      只各自在本文件中调用。
    3.结构也可以在.c文件中定义,lib/malloc.c里面就定义了一些结构供自己使用。
      在linux内核中,为了避免多一些不必要的头文件,比如fs/char_dev.c中就直接
      声明自己要调用的函数,所以函数声明也可以写在.c文件中。
    4.extern在函数声明中表示其是一个声明,而不是定义;在函数定义中表示函数是
      全局的。不过这两个地方的extern都可以省略,函数声明可以省略是因为声明没
      有函数体,而定义必须有函数体;函数定义可以省略是因为缺省就是全局可见的。
      其实这两个extern的含义不同。linus在有些函数声明前加了extern,有些没有,
      应该只是习惯性的,没有本质的区别。
    5.string.h头文件中包含了一些以inline内嵌函数的形式定义的字符串操作函数。
      下面inline中具体说明。不过函数定义是不能包含在头文件中的,否则会造成
      重复定义的错误。
    6.头文件中有些声明但没有定义的函数,是在库中定义的,当然这些库是不包含在
      内核代码中的。比如unistd.h中的系统调用都是在库里面封装的。再如sys/stat.h
      中chmod函数其实在unistd.h中也有声明,我想这是linus为了方便include头文件
      才又声明了一遍的,而且这两处的定义是一致的。
     
inline
Inlining of functions is an optimization and it really “works” only in
optimizing compilation. If you don't use -O, no function is really inline.
    一个函数的inlining是一种优化,而它只会"work"在有优化选项的编译中。如果你
没有(在GCC中)用到-O选项,任何函数都不会被inline。
When a function is both inline and static, if all calls to the function are
integrated into the caller, and the function's address is never used, then
the function's own assembler code is never referenced. In this case, GCC
does not actually output assembler code for the function, unless you specify
the option -fkeep-inline-functions. Some calls cannot be integrated for
various reasons (in particular, calls that precede the function's definition
cannot be integrated, and neither can recursive calls within the definition).
If there is a nonintegrated call, then the function is compiled to assembler
code as usual. The function must also be compiled as usual if the program
refers to its address,because that can't be inlined.
    当一个函数同时是inline和static的,如果对这个函数的所有调用都可以直接在调用
函数中展开,而且此函数的地址没有被引用(我的理解是函数指针调用),即这个函数的
汇编代码没有被引用。这种情况下,GCC实际上不生成此函数的汇编代码,除非你指定
-fkeep-inline-functions选项。某些条件下有些函数调用不能直接展开(比如,在函数
定义之前的函数调用,再如函数定义中有递归)。如果有没有被直接展开的函数调用,此
函数会按例生成汇编代码。当程序引用了此函数的地址时,这个函数也不能直接在调用中
展开,因此也按例被编译。
When an inline function is not static, then the compiler must assume that there
may be calls from other source files; since a global symbol can be defined only
once in any program, the function must not be defined in the other source files,
so the calls therein cannot be integrated. Therefore, a non-static inline
function is always compiled on its own in the usual fashion.
    当一个inline函数不是static,编译器会假定它会被其它编译单元调用,因为一个全局
符号在一个程序中只能定义一次,所以这个函数不会在其他编译单元中被定义,所以那里的
函数调用就不能被直接展开。因此,非static的inline函数都会按例被编译。
If you specify both inline and extern in the function definition, then the
definition is used only for inlining. In no case is the function compiled on its
own, not even if you refer to its address explicitly. Such an address becomes an
external reference, as if you had only declared the function, and had not
defined it.
    当一个函数是inline和extern的,这个函数定义只会在调用中展开,而不会被编译,即使
你明确引用它的地址。这个地址会成为一个外部引用,就好像你只是声明了这个函数,却没有
定义它。
This combination of inline and extern has almost the effect of a macro. The way to
use it is to put a function definition in a header file with these keywords, and
put another copy of the definition (lacking inline and extern) in a library file.
The definition in the header file will cause most calls to the function to be
inlined. If any uses of the function remain, they will refer to the single copy
in the library.
    inline和extern的函数基本上和一个宏定义的作用相当。使用的方法是在头文件中用这
两个关键字定义,而把另外一份相同定义却没有inline和extern关键字的copy放在库中。头文
件中的定义会使大部分的函数调用直接展开。如果还存在(通常开销)的函数调用,它就会使
用库中的定义。

    上面是GCC的说明文档,随GCC免费发布。归结起来有以下几点:
    1.inline是一个编译优化选项,需要满足一定的条件才会达到优化的目的,即直接在调用
      时展开,而省去调用函数的开销。
    2.static inline表明这个函数只在本编译单元中可见。inline或者extern inline都意味
      着此函数是全局可见的;不同的是inline限定的函数在定义的编译单元中被直接展开,
      而在其它调用关系的编译单元中被直接函数调用,而extern inline限定的函数在所有的
      调用中都被直接展开,相当于宏展开,强行的函数调用无效,除非定义一个非extern
      inline限定的版本放在库中以供调用,所以这个extern表明这只是个声明。
   
    在linux0.11中有两种使用方法:
    1.init/main.c中定义fork,pause等函数用了static inline,因为只在本单元中使用,限制
      只在本单元中可见。
    2.include/string.h中的字符串操作函数都用了extern inline,而在使用时包含string.h头
      文件。这里的定义也相当于声明,并没有在其它地方又声明,也不会因为多个文件中包含
      string.h头文件而引起重复定义的错误。我试了一下,在linux0.11中将string.h中的
      函数strcpy前的限定词extern去掉的话,编译会引起重复定义的错误(注意修改string.h
      头文件后要进入与string.h有依赖关系的目录执行make,如fs/,因为最外层的makefile并
      不依赖于string.h,会提示nothing can do)。
     
条件编译
    1.#ifndef
      #else           /******可选******/
      #endif
     
      (1)头文件中包含一些常用的常数定义时,为防止包含多个头文件时重复定义,采用条件编译
         即没有定义时才定义
         #ifndef NULL
         #define NULL ( (void *)0)
         #endif
      (2)最常用的为防止包含多个头文件时,把一个头文件包含了两次,造成重复定义
         #ifndef _FILENAME_H
         #define _FILENAME_H
         #endif /* _FILENAME_H */
        
    2.#ifdef
      #else           /******可选******/
      #endif
    
      (1)决定程序的某一部分参加编译与否
         #ifdef __LIBRARY__
         #endif   /****__LIBRARY__****/
         需要这一部分代码的之前#define __LIBRARY__
        
    3.#if defined
      #elif defined
      #else
      #endif
     
      单操作数运算符defined可以使用在#if或#elif伪指令中.
      #if  defined  _SYMBOL_ 等效于#ifdef   _SYMBOL_
      #if !defined  _SYMBOL_ 等效于#ifndef  _SYMBOL
        
      (1)有多项选择的时候使用很方便
         #if defined(vax) || defined(hp300) || defined(pyr)
         #define SEGMENT_SIZE PAGE_SIZE
         #endif
        
      (2)判断表达式
         #ifdef MAJOR_NR
           #if (MAJOR_NR == 1)
           #elif (MAJOR_NR == 2)
           #elif (MAJOR_NR == 3)
           #elif
         #elif
        
      (3)可以注释掉大段的代码,/*...*/不能注释掉本身包含*/的代码,因为/**/不能嵌套
         #if 0
         #endif

原创粉丝点击