翻译环境就是把源代码转换为可执行的机器指令
运行环境实际用于执行的代码
编译就是把各个源文件经过编译器生成目标文件在用链接器生成可执行文件
看主要的步骤:
1.组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
2.每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库, 将其需要的函数也链接到程序中。
运行环境
*1.程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须 由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同 时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
**下面解释一下#define的用法
**
#define name stuff
例如: #define M 200
下面看一下用#define 定义的switch语句
#define CASE break;case
int main(){
int a = 2;
switch (a){
case 1:
CASE 2 : printf("爬弟\n");
CASE 3:
CASE 4:
break;
}
return 0;
}
两个对比你喜欢哪一个呢
提问:
#define 定义完后面加分号吗?
再看下面这种情况:
#define M 100;
int main(){
int a = 0;
if (1) a = M;
else
printf("爬弟\n");
return 0;
}
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)
注意: 参数列表的左括号必须与name紧邻。 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
#define MUL(x) x*x
#define MUL(x) x*x
int main(){
printf("%d\n", MUL(5));
return 0;
}
#define MUL(x) x*x
int main(){
printf("%d\n", MUL(5+1));
return 0;
}
#define MUL(x) (x)*(x)
int main(){
printf("%d\n", MUL(5+1));
return 0;
}
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程
首先来看一组代码
前面显然是两个字符串,和打印一个字符串是一样的,接下来看看这一个问题
函数的方法实现
宏的方法实现 用到了#
#define PRINTF(x) printf("the value of " #x " is %d\n",x)
int main(){
/*printf("hello"" padi\n");
printf("hello padi\n");*/
int a = 2;
PRINTF(a);
//打印 the value of a is %d
int b = 2;
PRINTF(b);
//打印 the value of b is %d
return 0;
}
带副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导 致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。 例如:
宏和函数对比
宏通常被应用于执行简单的运算。比如在两个数中找出较大的一个。
那为什么不用函数来完成这个任务? 原因有二:
用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序 的规模和速度方面更胜一筹。
更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可 以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。
当然和宏相比函数也有劣势的地方:
命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。 那我们平时的一个习惯是: 把宏名全部大写 函数名不要全部大写
un 不就是不的意思嘛,不定义,就是删除#define定义呗
条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。 比如说:
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译
例如:
#define PRINTF
int main(){
#ifdef PRINTF
printf("pa di");
#endif
return 0;
}
这个代码的意思是如果#define定义了符号PRINTF 就执行这个代码,如果没定义就不执行
1.
#if 常量表达式
//…
#endif
//常量表达式由预处理器求值。如:
#define DEBUG 1
#if DEBUG
//…
#endif
int main(){
#if 1 //真就执行 假就不执行
printf("pa di");
#endif
return 0;
}
#if 0
int main(){
printf("pa di");
return 0;
}
#endif
自行拷贝测试
2.多分支的
多个分支的条件编译
#if 常量表达式
//… #elif 常量表达式
//…
#else
//…
#endif
int main(){
#if 1-1
printf("dong dong");
#elif 3+2
printf("chao ge");
#else
printf("pa di");
#endif
return 0;
}
3.
判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
#define TEST 0
int main(){
//如果定义了,下面参与编译
#if defined(TEST)
printf("你好\n");
#endif
#ifdef TEST
printf("你好\n");
#endif
//如果没定义,下面参与编译
#ifndef PTR
printf("我好\n");
#endif
#if !defined(PTR)
printf("我好\n");
#endif
return 0;
}
头文件的包含方式:
第一种:
#include"stdio.h"
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。 如果找不到就提示编译错误。
第二种:
库文件包含
#inlcude
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
这样是不是可以说,对于库文件也可以使用 “” 的形式包含? 答案是肯定的,可以。但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。