C的知识汇总

1.C语言程序的编译过程
C的知识汇总_第1张图片
预处理阶段。预处理器(cpp)根据以字符#开头的命令,修改原始c程序。比如hello.c中的第一行的#include <stdio.h>指令告诉预处理器读取系统文件stdio.h的内容。并把它直接插入到程序文本中去。结果就得到了另一个C程序,通常是以 “.i”作为文件扩展名。
编译阶段。编译器(ccl)将文本文件 hello.i 翻译成文本文件 hello.s ,它包含一个汇编语言程序。汇编语言程序中的每条语句都以一种标准的文本格式确切地描述了一条低级机器语言指令。汇编语言是非常有用的,因为它为不同高级语言的不同编译器提供了通用输出语言。例如:c语言编译器和Fortran编译器产生的输出文件用的都是一样的汇编语言。
汇编阶段。接下来,汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成为一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。hello.o文件是一个二进制文件,它的字节编码是机器语言指令而不是字符,如果我们在文本编辑器中打开hello.o文件,呈现的将是一堆乱码。
链接阶段。请注意,我们的hello程序调用了printf函数,它是标准c库中的一个函数,每个C编译器都提供,printf函数存在与一个名为printf.o的单独的预编译目标文件中,而这个文件必须以某种方式并入到我们的hello.o程序中。链接器(ld)就负责这种并入,结果就得到hello文件,它是一个可执行目标文件(或者简称为可执行文件)。可执行文件加载到存储器后,由系统负责执行。
2.extern的作用
在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。
函数的声明extern关键词是可有可无的,因为函数本身不加修饰的话就是extern的。但是引用的时候一样是需要声明的。
而全局变量在外部使用声明时,extern关键词是必须的,如果变量无extern修饰且没有显式的初始化,同样成为变量的定义,因此此时必须加extern
(1)变量  尤其是对于变量来说。  
extern int a;//声明一个全局变量a 
int a; //定义一个全局变量a   
extern int a =0 //定义一个全局变量a 并给初值。 
int a =0;//定义一个全局变量a,并给初值,   
但是定义只能出现在一处。也就是说,不管是int a;还是extern int a=0;还是int a=0;都只能出现一次,而那个extern int a可以出现很多次。  当你要引用一个全局变量的时候,你就要声明,extern int a;这时候extern不能省略,因为省略了,就变成int a;这是一个定义,不是声明。 
(2)函数
函数,函数,对于函数也一样,也是定义和声明,定义的时候用extern,说明这个函数是可以被外部引用的,声明的时候用extern说明这是一个声明。
 但由于函数的定义和声明是有区别的,定义函数要有函数体,声明函数没有函数体,所以函数定义和声明时都可以将extern省略掉,反正其他文件也是知道这个函数是在其他地方定义的,所以不加extern也行。

extern有两个作用:
第一个当它与"C"一起连用时,如: extern "C" void fun(int a, int b); 则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的.
第二个:在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或者其他模块中使用记住它是一个声明不是定义!
也就是说B模块(编译 单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。
使用例子
在文件a的.h文件定义
#ifndef TEST1H
#define TEST1H
   extern char g_str[]; // 声明全局变量g_str
#endif
文件a的.c文件
#include "文件a.h"
   char g_str[] = "123456"; // 定义全局变量g_str
第一种方式:
文件b的.c文件
    #include "文件a.h"
    void fun2()
    {
        cout << g_str << endl;//直接使用即可
    }
第二种方式:
那么就把文件b的代码 中#include "文件a.h"去掉 换成:
    extern char g_str[];
    void fun2()
    {
        cout << g_str << endl;
    }

3. static修饰的全局变量

staticextern是一对水火不容的家伙,也就是说externstatic不能同时修饰一个变量;其次,static修 饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域 只能是本身的编译单元,也就是说它的全局只对本编译单元有效,其他编译单元则看不到它

正是因为static有以上的特性,所以一般定义static全局变量时,都把它放在原文件中而不是头文件,这样就不会给其他模块造成不必要的信息污染,同样记住这个原则吧!

1)先来介绍它的第一条也是最重要的一条:隐藏。 当我们同时编译多
个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c。 下面是a.c的内容 
char a = 'A'; // global variable 

void msg() {

 printf("Hello\n"); 

下面是main.c的内容 

int main(void) { 

extern char a; // extern variable must be declared before use 

printf("%c ", a); (void)msg(); return 0; 

}

程序的运行结果是: A Hello 你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,
所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。 如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。 

2)static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。虽然这种用法不常见,但我还是举一个例子。 

#include <stdio.h> 

int fun(void){

 static int count = 10; //事实上此赋值语句从来没有执行过 

 return count--; 

int count = 1; 

int main(void) 

printf("global\t\tlocal static\n"); 

for(; count <= 10; ++count) printf("%d\t\t%d\n", count, fun()); 

return 0; 

程序的运行结果是: global local static 1 10 2 9 3 8 4 7 5 6 6 5 7 4 8 3 9 2 10 1 

3)static的第三个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加&rsquo;\0&rsquo;太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是&rsquo;\0&rsquo;。不妨做个小实验验证一下。

#include <stdio.h> 

int a; 

int main(void) {

 int i;

 static char str[10];

 printf("integer: %d; string: (begin)%s(end)", a, str);

 return 0; 

} 程序的运行结果如下 integer: 0; string: (begin)(end) 最后对static的三条作用做一句话总结。最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0.

4. const修饰的全局常量

    const修饰的全局常量用途很广,比如软件中的错误信息字符串都是用全局常量来定义的。

const修饰的全局常量据有跟static相同的特性(const放在只读静态存储区),即它们只能作用于本编译模块中,但是const可以extern连用来声明该常量可以作用于其他编译模块中    extern const char g_str[];     然后在原文件中别忘了定义:     const char g_str[] = "123456";     所以当const单独使用时它就与static相同,(前提是都在描述全局变量,如果在函数内部就不一样)

而当与extern一起合作的时候,它的特性就跟extern的一样了!

提醒

const char* g_str = "123456" 与 const char g_str[] = "123465"是不同的, 前面那个const 修饰的是char * 而不是g_str,它的g_str并不是常量,它被看做是一个定义了的全局变量(可以被其他编译单元使用), 所以如果你像让char *g_str遵守const的全局常量的规则,最好这么定义constchar* constg_str="123456".

你可能感兴趣的:(C的知识汇总)