昆山做企业网站,淘宝网站基础建设 托管,客户管理怎么做,安居客官网网站文件的编译链接和预处理 程序的翻译环境和执行环境翻译环境预处理#xff08;预编译#xff09;过程编译过程汇编过程链接过程 运行环境 预处理详解预处理符号预处理指令#define#define定义标识符#define定义宏#define替换规则 #与###的使用##的使用 带有副作用的宏参数宏与函… 文件的编译链接和预处理 程序的翻译环境和执行环境翻译环境预处理预编译过程编译过程汇编过程链接过程 运行环境 预处理详解预处理符号预处理指令#define#define定义标识符#define定义宏#define替换规则 #与###的使用##的使用 带有副作用的宏参数宏与函数的对比宏的优势函数的优势宏与函数的命名约定 undef命令行定义条件编译 程序的翻译环境和执行环境
在ANSIC的任何一种实现中存在俩个不同的环境 1.翻译环境在这个环境中源代码被转换为可执行的机器指令二进制指令 2.执行环境用于执行代码
计算机只能执行二进制的指令
翻译环境 在程序编译过程
组成一个程序的每一个源文件通过编译过程分别转换为目标代码每个目标文件有链接器捆绑在一起形成一个单一而完整的可执行程序链接器同时也会引入标准C函数中任何被该程序所用到的函数而且它可以搜索程序员个人的程序库将器所需要的函数也链接到其中每一个源文件会单独被编译器处理为目标文件
预处理预编译过程
1.#include头文件的包含 2.#define定义符号的替换和删除 3.注释的删除
预处理预编译过程属于文本操作过程
编译过程
将C语言代码翻译成汇编代码
语法分析词法分析语义分析符号分析
汇编过程
1.将汇编代码翻译成二进制指令存放目标文件 2.形成符号表
链接过程
1.合并段表 2.符号表的合并和符号表的重定义
【注意】把gcc移植到windows环境编译产生的程序想在windows上运行就得按照windows的可执行程序的格式进行
运行环境
在程序执行的过程
1.程序必须载入内存中在有操作系统的环境中一般由操作系统完成在独立的环境中程序的载入必须由手工安排也可能是通过可执行代码置入只读内存完成
2.程序的执行便开始。接着便调用main函数
3.开始执行程序代码。这个时候程序将使用一个运行时堆栈stack存储函数的局部变量和返回地址。程序同时也可以使用静态static内存存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
4.终止程序。正常终止main函数也有可能是意外终止。
预处理详解
预处理符号 int main(void)
{printf(%s\n,__FILE__);printf(%s\n,__LINE__);printf(%s\n,__DATE__);printf(%s\n,__TIME__);printf(%s\n,__STDC__);printf(%s\n,__FUNCTION__);return 0;
}这些预定义符号都是语言内置
预处理指令
1.#define 2.#include 3.#pragma 4.#error 5.#line …
#define
#define定义标识符 #define name staff define 定义一个数字
#define MAX 100
#define min 5define 创建一个新名字
#define reg registerdefine 使用另外一个符号替换实现
#define do_forever for(;;)注意一般使用define定义标识符时不使用 #define定义宏
#define机制包括了一个规定允许把参数替换到文本中这种实现通常称为宏或者定义宏
宏的声明方式 #define name(parament-list) stuff 其中的parament-list是一个由逗号隔开的符号表可能出现在stuff中。
【注意】参数列表的左括号必须与name紧邻如果俩种之间由任何空白存在参数列表就会被解释为stuff的一部分
宏可以看作是一种替换
#define ADD(x,y) xyint main(void)
{int a 10;int b 20;printf(%d,ADD(a,b));return 0;
}【注意】所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
#define替换规则
在程序中扩展#define定义符号和宏时需要涉及几个步骤。
在调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果是它们首先被替换。替换文本随后被插入到程序中原来文本的位置。对于宏参数名被他们的值替换。最后再次对结果文件进行扫描看看它是否包含任何由#define定义的符号。如果是就重复上述处理过程。
注意
宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏不能出现递归。当预处理器搜索#define定义的符号的时候字符串常量的内容并不被搜索。
#与##
#的使用
将宏的参数变成对应的字符串
#define PRINTF(format,value) printf(the value of #valueisformat\n,value)int main(void)
{int i 10;PRINT(%d,i);return 0;
}##的使用
可以把位于它俩边的符号合成一个符号允许宏定义从分离的文本片段创建标识符
#define F(x,y) x##E##y
#includestdio.h
int main(void)
{printf(%f,F(2,-5));return 0;
}带有副作用的宏参数
#define ADD(x,y) xy
int main(void)
{int b a;printf(%d,ADD(b,b));return 0;
}当宏参数在宏的定义中出现超过一次的时候如果参数带有副作用那么你在使用这个宏的时候就可能出现危险导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
宏与函数的对比
宏的优势
用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于来比较的类型。宏是类型无关的。
函数的优势
每次使用宏的时候一份宏定义的代码将插入到程序中。除非宏比较短否则可能大幅度增加程序的长度。宏是没法调试的宏由于类型无关也就不够严谨宏可能会带来运算符优先级的问题导致程序出错
属 性#define定义宏函数代码长度每次使用时宏代码都会被插入到程序中。除了非常小的宏之外程序的长度会大幅度增长函数代码只出现于一个地方每次使用这个函数时都调用那个地方的同一份代码执行速度更快存在函数的调用和返回的额外开销 所以相对慢一些操作符优先级宏参数的求值是在所有周围表达式的上下文环境里除非加上括号否则邻近操作符的优先级可能会产生不可预料的后果所以建议宏在书写的时候多些括号函数参数只在函数调用的时候求值一次它的结果值传递给函数。表达式的求值结果更容易预测。带有副作用的参数参数可能被替换到宏体中的多个位置所以带有副作用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一次结果更容易控制。参数类型宏的参数与类型无关只要对参数的操作是合法的它就可以使用于任何参数类型。函数的参数是与类型有关的如果参数的类型不同就需要不同的函数即使他们执行的任务是不同的调试宏是不方便调试的函数是可以逐语句调试的递归宏是不能递归的函数是可以递归的
宏与函数的命名约定
宏的名字全部大写函数名的每个英文单词首字母大写
undef
#undef用于移除一个宏定义
#define ADD(x,y) xy
#undef ADD命令行定义
在命令行中定义符号。用于启动编译过程。
条件编译
在编译一个程序的时候如果一条语句一组语句编译或者放弃是很方便的
这种情况适合于调试型代码删除可惜保留多余
#if 常量表达式//...
#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