程序的编译和预处理
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