程序的编译和预处理(预处理,编译,链接,宏,命令行,条件编译和文件包含)

程序的编译和预处理

ANSIC下的两种环境
1、编译环境:
源代码可以被转换为机器可执行的指令
2、执行环境
用于实际执行代码

1、编译和链接

1、源文件编译为目标代码(objec code)
2、目标文件由链接器(linker)捆绑,形成一个可执行程序
3、链接器可以引入C函数库中被该程序用到的函数,也可以搜索程序员自己写的库,将其链接到程序中
三个阶段:预处理->编译->汇编

sum.c

#include <stdio.h>
#include <stdlib.h>

int g_val = 2020;

void Print(const char *str)
{
	printf("%s\n", str);
}

test.c

#include <stdio.h>
#include <stdlib.h>

int main()
{
	extern void Print(char *str);
	extern int g_val;
	printf("%d\n", g_val);
	Print("Hello,World!");
	system("pause");
	return 0;
}
#include <stdio.h>
#include <stdlib.h>

int main()
{
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", i);
	}
	system("pause");
	return 0;
}

2、运行环境

程序的执行过程:
1、载入内存(可由操作系统完成,也可手动载入)
2、调用main函数
3、执行程序
4、终止程序

3、预处理

预定义符号:

__FILE__		//进行编译的源文件
__LINE__		//文件当前的行号
__DATE__		//文件被编译的日期
__TIME__		//文件被编译的时间
__STDC__		//若编译器遵循ANSIC 返回1 否则未定义

//example:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	printf("file:%s line:%d date:%d time:%d\n", __FILE__, __LINE__, __DATE__, __TIME__);
	system("pause");
	return 0;
}

3.1、#define定义标识符

#define MAX 1000//最大值MAX=1000
#define reg register//为register创建一个短的关键字
#define do_forever for(;;)
#define CASE break;case//写case的时候自动补全break

//若是stuff过长的时候可以换行写,在每一行的后面要加上反斜杠(续航符)
#define DEBUG_PRINT printf("file:%s line:%d date:%d time:%d\n",\
					__FILE__,\
					__LINE__,\
					__DATE__,\
					__TIME__)

在define定义标识符的时候,最好不要加‘;’
3.2、#define定义宏

在#define中存在一个机制,允许宏参数替换,这种宏称为(macro)或是定义宏(define marco)

宏的声明方式

#define name(parament-list) stuff

#define 在程序中扩展#define定义符号和宏的步骤:
1、宏允许出现传递替换,但不允许宏不能出现递归
2、替换文本随后插入到程序原来文本位置,宏参数名被他们的真实值替换
3、传递替换

3.3、#和##的区别

#作用:
把一个宏参数变成对应的字符串
##作用:
把位于##两端的符号合成为一个符号,允许宏定义从分离的文本片段创建标识符

#include <stdio.h>
#include <stdlib.h>

#define PRINT(FORMAT, VALUE)\
	printf("the value of "#VALUE" is "FORMAT"\n", VALUE);

int main()
{
	int i = 10;
	PRINT("%d", 10);
	PRINT("%d", i + 3);
	system("pause");
	return 0;
}

#include <stdio.h>
#include <stdlib.h>

//##可以把它两端的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符
#define CONS(a,b)  int(a##e##b)


int main()
{
	//printf("%d\n", CONS(2, 3));
	system("pause");
	return 0;
}

3.4、宏和函数的优缺点对比

宏的优点:
1、宏比函数在程序规模和速度上更快
2、宏时定义时与类型无关
宏的缺点:
1、宏过大的时候会增加代码长度
2、宏没有办法调试
3、宏没有参数类型,不够严谨
4、宏会带来优先级问题,导致程序出错

属 性 #define定义宏 函数
代 码 长 度 每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长 函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执 行 速 度 更快 存在函数的调用和返回的额外开销,所以相对慢一些
操 作 符 优 先 级 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。
带 有 副 作 用 的 参 数 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。 函数参数只在传参的时候求值一次,结果更容易控制。
参 数 类 型 宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。 函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的。
调 试 宏是不方便调试的 函数是可以逐语句调试的
递 归 宏是不能递归的 函数是可以递归的
#include <stdio.h>
#include <stdlib.h>

//宏的优点
//1、运行速度快
//2、宏与参数类型无关
//宏的缺点
//1、宏过大的时候会增加代码长度
//2、宏没有办法调试
//3、宏没有参数类型,不够严谨
//4、宏会带来优先级问题,导致程序出错

//宏可以做到函数做不到的事情,例如:宏的参数可以是类型

#define MAX(a, b) ((a)>(b)?a:b)

#define MALLOC(num, type)\
	(type *)malloc(num * sizeof(type))

int main()
{
	printf("max = %d\n", MAX(2, 3));
	MALLOC(10, int);//替换为(int *)malloc(10 * sizeof(int));
	system("pause");
	return 0;
}

3.5、带有副作用的宏参数

宏的副作用表现在表达式求值的时候出现的永久性

#include <stdio.h>
#include <stdlib.h>

//x+1;不带副作用
//x++;带有副作用
#define MAX(a, b) ((a)>(b)?a:b)

int main()
{
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x = %d y = %d z = %d\n", x, y, z);
	system("pause");
	return 0;
}

3.6、命名约定
宏全部大写,函数采用驼峰式
3.7、#undef的作用

#undef

移除一个宏定义

4、命令行定义

许多C的编译器提供了一种能力,允许命令行中定义符号。用于启动编译 过程

#include <stdio.h>
#include <stdlib.h>

#define ARRAY_SIZE 999

int main()
{
	int array[ARRAY_SIZE];
	for (int i = 0; i < ARRAY_SIZE; i++)
	{
		array[i] = i;
	}
	for (int i = 0; i < ARRAY_SIZE; i++)
	{
		printf("%d ", array[i]);
	}
	system("pause");
	return 0;
}

5、条件编译

顾名思义,编译一部分代码

#include <stdio.h>
#include <stdlib.h>

#define __DEBUG__ 0

int main()
{
	int arr[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
		arr[i] = i;
		#if __DEBUG__
		printf("%d\n", arr[i]);
		#endif
	}
	system("pause");
	return 0;
}

常见条件编译指令:

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

6、文件包含

包含两种
1、#include < >
2、#include " "
双引号包起来的头文件现在源文件的目录下查找,找不到再去C函数库里找
尖括号包起来的头文件直接去C函数库里找

6.1、嵌套文件包含
避免头文件多次编译

#ifndef __TEST_H_
#define __TEST_H_

#endif	//__TEST_H_

或者

#prama once
发布了117 篇原创文章 · 获赞 48 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/gp1330782530/article/details/104581401