吐血详解~C进阶程序环境与预处理!!!淦淦淦~

文章目录

  • 程序的环境与预处理
    • 程序的翻译环境与运行环境
    • 编译与链接
  • 预处理详解
    • 预处理符号
    • #define
    • #define 定义宏(偏爱括号的宏宏)
    • #define 替换规则
    • 注意:
    • # 号
    • 带副作用的宏参数
    • 宏和函数的比较
    • 宏和函数的区别
    • #undef
    • 条件编译
    • 条件编译指令的种类
    • 文件包含

程序的环境与预处理

程序的翻译环境与运行环境

翻译环境就是把源代码转换为可执行的机器指令
运行环境实际用于执行的代码

编译与链接

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第1张图片
编译就是把各个源文件经过编译器生成目标文件在用链接器生成可执行文件
看主要的步骤:

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第2张图片
1.组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
2.每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库, 将其需要的函数也链接到程序中。

运行环境
*1.程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须 由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同 时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第3张图片

  1. 终止程序。正常终止main函数;也有可能是意外终止

预处理详解

预处理符号

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第4张图片吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第5张图片

#define

**下面解释一下#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;
}

两个对比你喜欢哪一个呢
吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第6张图片
提问:
#define 定义完后面加分号吗?

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第7张图片
再看下面这种情况:

#define M 100;
int main(){
     
	int a = 0;
	if (1)  a = M;
	else 
		printf("爬弟\n");
	return 0;
}

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第8张图片

#define 定义宏(偏爱括号的宏宏)

#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;
}

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第9张图片
警告: 这个宏存在一个问题: 观察下面的代码段:

#define MUL(x) x*x
int main(){
     
	printf("%d\n", MUL(5+1));
	return 0;
}

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第10张图片
**出现了这种问题怎末解决呢?
**
直接加括号

#define MUL(x) (x)*(x)
int main(){
     
	printf("%d\n", MUL(5+1));
	return 0;
}

#define 替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。

  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
    吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第11张图片

  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程

注意:

  1. 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

# 号

首先来看一组代码
吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第12张图片
前面显然是两个字符串,和打印一个字符串是一样的,接下来看看这一个问题
函数的方法实现
吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第13张图片
宏的方法实现 用到了#
吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第14张图片

#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;
}

带副作用的宏参数

带副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导 致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。 例如:

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第15张图片
不带副作用的宏调用
吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第16张图片

宏和函数的比较

宏和函数对比
宏通常被应用于执行简单的运算。比如在两个数中找出较大的一个。
那为什么不用函数来完成这个任务? 原因有二:

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序 的规模和速度方面更胜一筹。

  2. 吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第17张图片

  3. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可 以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。

当然和宏相比函数也有劣势的地方:

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  2. 宏是没法调试的。
  3. 宏由于类型无关,也就不够严谨。
  4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
    宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
    吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第18张图片

宏和函数的区别

吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第19张图片
命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。 那我们平时的一个习惯是: 把宏名全部大写 函数名不要全部大写

#undef

un 不就是不的意思嘛,不定义,就是删除#define定义呗
吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第20张图片

条件编译

条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。 比如说:
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译

例如:

#define PRINTF
int main(){
     
#ifdef PRINTF

	printf("pa di");
#endif
	return 0;
}

这个代码的意思是如果#define定义了符号PRINTF 就执行这个代码,如果没定义就不执行
吐血详解~C进阶程序环境与预处理!!!淦淦淦~_第21张图片

条件编译指令的种类

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
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
这样是不是可以说,对于库文件也可以使用 “” 的形式包含? 答案是肯定的,可以。但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

你可能感兴趣的