C语言:编译过程&预处理命令


预处理----编译----汇编----链接

预处理.h .c预处理后成.i
编译.i编译后生成汇编文件.s
汇编.s汇编后生成目标文件.o
链接.o链接生成可执行文件.exe

预处理

  1. 展开我们所包含的头文件
  2. 注释删除
  3. 有宏的地方进行替换,并删除宏
  4. 不进行语法检查c->i

编译

将c语言代码翻译成汇编代码

  1. 进行语法,词法,语义分析
  2. 符号汇总

汇编

将汇编代码转换成了二进制指令 o文件此已经成为了二进制文件,但此时还不能运行

  1. 合并段表
  2. 符号表的合并和重定位

链接

将各自不同功能模块的.o文件集合到一起称为.exe文件,如果一个函数没有实现,预处理和编译阶段,系统不会报错,只有在链接的时候,才会报错

  1. 合并段表
  2. 符号表的合并和重定位

当一个工程中有多个.c和.h文件,经过预处理和编译后,会形成相应的.o文件,只有这些.o文件经过链接后,才会生成一个.exe文件

预定义符号

本身系统就定义好的符号,我们自己定义的不是预定义符号,叫定义符号

预处理指令:
以#开头,当编译器识别到该指令时,会在编译前对源代码做一些准备工作,即预处理

//__FILE__     //进行编译的源文件的路径
//__LINE__     //文件当前的行号
//__DATE__     //文件被编译的日期
//__TIME__     //文件被编译的时间
//__STDC__     //如果编译器遵循ANSI c(c标准),其值为1,否则未定义

#include<stdio.h>
int main ()
{
	printf("%s\n",__FILE__);
	printf("%d\n",__LINE__);
	printf("%s\n",__DATE__);
	printf("%s\n",__TIME__);
	return 0;
}

预处理指令:预处理阶段所用到的指令,都以#开头

1.#define定义标识符:

#define MAX 1000
int main ()
{
	int a=MAX;
	printf("%d\n",a);//输出的是1000
	return 0;
}

2.#define 定义宏

#define 机制包括了一个规定,允许把参数替换到文本中;
这种实现通常称为宏(macro)或定义宏(define macro)在这里插入图片描述

声明宏的方式

#define name(parament-list) stuff 
//将括号中的参数(括号里为参数)替换到内容(stuff)里去
//parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中

注意: 参数列表的左括号必须与name紧邻。 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分

1#define ADD(a,b) a+b
#define MUL(a,b) a*b
int main ()
{
	int num=ADD(3,5)*MUL(2,7);//替换后num=3+5*2*7
//预处理器在程序中找到宏的示实例后,就会用替换体替换宏。从宏变成最终替换文本的过程称为 ‘宏展开’ 。
	printf("%d\n" ,num);      // 输出的结果是73,而不是112,
	retuen 0;
}2.
#define DOUBLE(X) X+Y
int main ()
{
	int a=5;
	int ret=10*DOUBLE(a);
	printf("%d\n",a);//输出是55,不是100
}

注:

  1. 一定要注意宏在替换时加括号,才不会因操作符的优先级改变最后结果;
  2. #define定义标识符时,后面最好不要加 “ ; ”

#define 替换规则

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

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:

  1. 宏参数和#define 定义中可以出现其他#define定义的变量。对于宏,可以调用但是不能出现递归。
  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索
printf("MAX=%d\n",MAX);//第一个MAX不会被替换,属于字符串常量

#:

(不是#define前的#)作用:将宏参数直接转换成字符串插入到字符串中

void print(int a)
{
	printf("the value of a is %d\n",a);
}
int main()
{
	int a=10;
	int b=20;
	print(a);
	//输出the value of a is 10
	print(b);
	//输出the value of a is 20,并不是the value of b is 20
	return 0;
}

三者输出都是hello word
两个字符串在一起时,会被当成一个字符串

int main()
{
printf("hello word");
printf("hello" " word");
printf("he" "llo word");
}

在x前加#,x不会替换成10,#x会变成"a";
也就是说:#x表示表达的内容就是其所对应的字符串(变成了"a")。

#define PRINT(X) printf("the value of" #X "is %d\n",X);
int main ()
{
	int a=10;
	int b=20;
	PRINT(a);//the value of a is 10
	PRINT(b);//the value of b is 20
	return 0;
}

##:

作用:可以把位于它两边的符号合成一个符号;它允许宏定义从分离的文本片段创建标识符。

#define CAT(X,Y) X##Y
int main ()
{
	int ab=1;
	printf("%d\n",CAT(a,b));//输出是1
	              //a##b
	              //ab
	              //相当于变量ab
	return 0;
}

#undef

作用:移除一个宏定义。
宏的作用域在#define和#undef之间

命令行定义

Linux环境下,arr[n],在编译的过程中,命令行设置一个参数,可随时改变n的值

条件编译

条件编译:需要的时候放开,不需要的时候删除,这也是预处理指令中的一种

(1).#ifdef与#endif

#include<stdio.h>
#define DEBUG  //定义DEBUG
int main ()
{
	int arr[0]={0,1,2,3,4};
	int i=0;
	for(i=0;i<5;i++)
	{
		arr[i]=0;
#ifdef DEBUG  //如果DEBUG被定义,下一条语句参与编译
		printf("%d\n",arr[i]);
#endif
	}
	return 0;
}


#ifndef//如果没有被定义就执行下语句

(2).#if

#if 常量表达式
 //... 
#endif 
 //常量表达式由预处理器求值。
如:

#define __DEBUG__ 1 
#if __DEBUG__ 
 //.. 
#endif 

(3).多个分支的条件编译

同if…else…


#if 常量表达式
 //... 
#elif 常量表达式
 //... 
#else 
 //... 
#endif 

(4).判断是否被定义

#include <stdio.h>
#define DEBUG 0

int main ()
{
#if define (DEBUG)   //即使DEBUG值为0,但是它已经被定义过了,所以下一条语句可以执行
	printf("hehe\n");
#endif
	return 0;
}

(5).嵌套指令


#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 
#end

#include包含的头文件

头文件的包含其实就是将.h文件下的内容,拷贝至,要引用它的文件下;每包含一次就拷贝一次

< >引库函数的头文件
" "引自定义的头文件

两者查找策略也不同:

< >:是直接在标准路径下查找, 如果找不到就提示编译错误。
" ":先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件, 如果找不到就提示编译错误。

避免头文件的重复引入

#ifndef __TEST_H__ 
#define __TEST_H__ 
//头文件的内容
#endif //__TEST_H__ 



或者:
#pragma once 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值