目录:1.编译链接的基本过程
2.预处理指令
一.编译链接的最基本过程
源文件--->编译--->链接--->运行
编译(预编译--->编译--->汇编)
预处理(所有的预测处理指令都在预处理阶段处理):
(文本操作)
1.注释的替换(删除),注释被替换成一个空格
2.头文件的包含
3.#define符号的替换
编译(把C语言代码翻译成汇编代码):
1.词法分析
2.语法分析
3.语义分析
4.符号汇总
汇编(把汇编代码翻译成了二进制的指令 生成了【.o文件(目标文件)】)
1.生成符号表
链接:链接目标文件和链接库生成可执行程序二进制的程序
1.合并段表
2.符号表的合并和重定位
二.预处理指令 #define
1.#define定义常量(标识符) (注:非常个性化,几乎什么都可以定义,
例如函数#define CASE break; case )
格式: #define name stuff
例: #define MAX 100
后面尽量不加分号,加了之后 MAX会被替换成1000; ,在后续程序中可能会出现bug
2.#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义
宏(define macro)。
语法:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。
这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中
例如:
#define PRINT(n,format) printf(" the value of "#n" is "format"\n " ,n)
技巧: 使用 # , 把一个宏参数变成对应的字符串
##的作用: 可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。
在用于对数值表达式进行求值的宏定义都应该向下面示例那样加上括号,避免在使用宏时由于参数的操作符或临近操作符之间不可预料的相互作用
例如: #define ADD(x,y) ((x)+(y))
宏的部分应用:
宏通常被用于执行简单的运算。
比如在两个数中找出较大的一个。
注:
宏有时候可以做到函数做不到的事情。
比如:宏的参数可以出现类型,但是函数不行
命名约定:
一般来讲函数的宏的使用和宏的使用语法很类似,因此语言本身无法帮我们区分二者
一 般的习惯是:1. 把宏名全部大写
2.函数名不要全部大写
#undef : 这条指令用于移除一个宏定义
例如: #undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除
命令行定义:
许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。
条件编译:
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
例如:调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。
常见的条件编译指令:
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
头文件包含的2种形式:
1.包含本地文件(自己的. h文件)
例如: #include"test.h"
2.包含 标准库的头文件
例如: #include<stdio.h>
嵌套文件包含:
在分模块编程的时候,最终的程序可能会出现多份相同的内容
如何解决这个问题:
条件编译: 每个头文件开头写:
#ifndef __TEST__H__
#define__TEST__H__
//头文件的内容
#endif //___TES__H__
或者:
#pragma once //避免头文件的重复引入