目录
一,程序环境
1.程序的翻译环境
程序编译与链接
2. 程序的运行环境
二,预处理指令
预定义符号
1. #define
用法1:#define name stuff
用法2:#define 定义宏
易错点:
宏和函数优劣对比
2. #和##
# 的功能
##的功能
3. #undef:
4. 条件编译
5. #include
如何解决头文件多重包含?
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境,它用于实际执行代码。
程序由被人所能认识的源代码,经过翻译转化成能被机器所能识别的二进制语言。过程大致如下:
其中,注意:
- 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
- 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
- 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库, 将其需要的函数也链接到程序中。
其中的详细步骤,及各步骤的功能,如图:
当我们翻译成了机器语言后,就可以准备运行了。
1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须 由手工安排,也可能是通过可执行代码置入只读内存来完成。2. 程序的执行便开始。接着便调用 main 函数。3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈( stack ),存储函数的局部变量和返回地址。程序同时也可以使用静态(static )内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。4. 终止程序。正常终止 main 函数;也有可能是意外终止。
__FILE__ // 进行编译的源文件__LINE__ // 文件当前的行号__DATE__ // 文件被编译的日期__TIME__ // 文件被编译的时间__STDC__ // 如果编译器遵循 ANSI C ,其值为 1 ,否则未定义
如:
printf ( "file:%s line:%d\n" , __FILE__ , __LINE__ );// 第一个就会打印源文件的路径; 第二个就会打印此段代码所在的行号
这里就是我们最熟悉的#define 了,
#define k 20 // 定义个常量
下面是宏的申明方式:
#define name( parament-list ) stuff 其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。
#define text(x) 2 * x
#define SQUARE( x ) x * x // 正确的写法
#define text(x, y) x + y // 高危代码,非常容易发生错误// 比如:printf("%d", 2 * text(2, 3)); // 输出是:7 而你想要的是10,所以正确的写法应该是#define text(x, y) ((x) + (y))// 所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
1. 宏参数和 #define 定义中可以出现其他 #define 定义的变量。但是对于宏,不能出现递归。2. 当预处理器搜索 #defifine 定义的符号的时候,字符串常量的内容并不被搜索。
(注:建议宽屏浏览)
属 性 #define 定义宏 函数代码长度 每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外, 每次使 用这个函数时,都调用那个地方的同 一份代码
程序的长度会大幅度增长函数代码只出现于一个地方;执行速度 更快 存在函数的调用和返回的额外开销,所以相对慢一些操作符优先级 宏参数的求值是在所有周围表达式的上下文环境里, 函数参数只在函数调用的时候求值一次,它的结果值传递除非加上括号,否则邻近操作符的优先级可能会产生 给函数。表达式的求值结果更容易预测。不可预料的后果,所以建议宏在书写的时候多些括号。带有副作用 参数可能被替换到宏体中的多个位置,所以带有副作用的参 函数参数只在传参的时候求值一次,结果更容易控制。的参数 数求值可能会产生不可预料的结果。参数类型 宏的参数与类型无关,只要对参数的操作是合法的,它就可 函数的参数是与类型有关的,如果参 数的类型不同,就 需要不同的函数, 即使他们执行的任务是不同的。以使用于任何参数类型。调 试 宏是不方便调试的 函数是可以逐语句调试的递归 宏是不能递归的 函数是可以递归的
首先我们看看这样的代码:
char* p = "hello ""bit\n";
printf("hello ""bit\n");
printf("%s\n", p);
从结果上我们会发现都打印了 hello bit 。我们发现字符串是有自动连接的特点的。
另外一个技巧是: 使用 # ,把一个宏参数变成对应的字符串。
比如:
第一种:#define text(x) printf("I " "x" "China\n"); //参数x 需要是个字符串
第二种:#define text(x) printf("I "#x"China\n"); // x同样要是字符串
#define ADD_TO_SUM(num, value)sum##num += value;...ADD_TO_SUM(5, 10);// 作用是:给 sum5 增加 10.
#undef NAME //如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
#include
#define max 2
int main()
{
#if max == 1
printf("hehe\n"); // 不编译
#elif max == 2
printf("baba\n"); // 编译
#else
printf("gungun\n"); // 不编译
#endif // 1
return 0;
}
#include #include "text.h" // 自建头文件
有“” 与 <> 两种,其实是两种查找策略:
这样是不是可以说,对于库文件也可以使用 “” 的形式包含?答案是肯定的,可以,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。
解决办法:
#pragma once
#ifndef _text_h_ // 没定义 返回1;否则返回0,结束编译
#define _text_h_
// ....... 各种包含头文件,函数声明
int k(int x, int y);
// .......
#endif // !_text_h_