Peiiieee的C语言笔记(18)

程序环境和预处理

1.程序的翻译环境和执行环境

在ANSI C的任何一种实现中,存在两个不同的环境

第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令

第二种是执行环境,它用于实际执行代码

例:test.c存放的是文本信息的代码,是源文件。需要通过翻译环境变成test.exe可执行程序,里面是2进制的指令。然后通过执行环境执行代码。

2.详解编译+链接

2.1翻译环境

Peiiieee的C语言笔记(18)_第1张图片

·组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code) *在windows环境下生成的目标文件是xxx.obj,在Linux环境下生成的目标文件是xxx.o

·每个目标文件由链接器捆绑在一起,形成一个单个而完整的可执行程序

·链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中

2.2编译本身也分为几个阶段:

1.预编译(预处理)2.编译 3.汇编

1.预处理

文本操作:

(1)注释的替换(删除)

注释被替换成一个空格

(2)头文件的包含

#include <>

(3)#define符号的替换

*所有的预处理指令都是在预处理阶段处理的

2.编译

把C语言代码翻译成汇编代码:

(1)词法分析 (2)语法分析 (3)语义分析 (4)符号汇总

3.汇编

生成符号表:

把汇编代码翻译成了二进制的指令,生成了.o文件(目标文件)

2.3链接

链接目标文件和链接库生成可执行程序二进制的程序

(1)合并段表

(2)符号表的合并和重定位

3.预处理详解

3.1预定义符号

__FILE__        //进行编译的源文件

__LINK__        //文件当前的行号

__DATE__        //文件被编译的日期

__TIME__        //文件被编译的实际

__STDC__        //如果编译器遵循ANSI C,其值为1,否则未定义

这些预定义符号都是语言内置的

3.2#define
3.2.1#define定义标识符常量

语法:#define name stuff

注:不要在末尾加上分号;,这样会导致语法错误

3.2.2#define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏

语法:#define name(parament-list)  stuff

其中的parament-list是一个由都好隔开的符号表,它们可能出现在stuff中

注:参数列表的左括号必须与name紧邻。如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分

(尽量多的带上括号)

3.2.3 #和##

1.使用#,会把一个红参数变成对应的字符串

2.使用##,可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。

3.2.4 宏和函数对比
属性 #define定义宏 函数
代码长度 每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长。 函数代码只出现于一个地方。每次使用这个函数时,都调用那个地方的同一份代码。
执行速度 更快 存在函数的调用和返回的额外开销,所以相对慢一些
操作符优先级 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多写括号。 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。
带有副作用的参数 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。 函数参数只在传参的时候求值一次,结果更容易控制。
参数类型 宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。 函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的。
调试 宏是不方便调试的 函数是可以逐语句体调试的
递归 宏是不能递归的 函数是可以递归的
3.2.5命名约定

·把宏名全部大写

·函数名不要全部大写

3.3#undef

这条指令用于移除一个宏定义

#undef NAME

//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除

3.4条件编译

1.

#if   常量表达式

        //...

#endif

//常量表达式由预处理器求值

如:

#define __DEBUG__ 1

#if __DEBUG__

        //...

#endif

2.多个分支的条件编译

#if   常量表达式

        //...

#elif   常量表达式

        //...

#else

        //...

#endif

3.判断是否被定义

#if   defined(symbol)

#ifdef   symbol

 

#if   !defined(symbol)

#ifndef   symbol

4.嵌套指令

#if   defined(OS_UNIX)

        #ifdef   OPTION1

                unix_version_option1();

        #endif

        #ifdef    OPTION2

                unix_version_option2();

        #endif

#elif   defined(OS_MSDOS)

        #ifdef   OPTION2

                msdos_version_option2();

        #endif

#endif

3.5文件包含
·本地文件包含

#include "filename"

查找策略:现在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件,如果找不到就提示编译错误

·库函数包含

查找策略:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误

·嵌套文件包含

解决这个问题的办法就是条件编译

#ifndef   __TEST_H__

#define   __TEST_H__

//头文件的内容

#endif

或者:

#pragma once

你可能感兴趣的:(笔记,c语言)