【7】C语言进阶--程序的编译(预处理操作)+链接

test.c    --源文件(源程序)

经过编译生成   ****.exe文件        二进制文件

1.程序的编译和执行环境

【7】C语言进阶--程序的编译(预处理操作)+链接_第1张图片

2.详解C语言程序的编译+链接

【7】C语言进阶--程序的编译(预处理操作)+链接_第2张图片

编译处理的过程        编译器(预编译    编译     汇编)

链接处理的过程        链接

  • 组成一个程序的每个源文件都通过编译过程分别转换成目标代码(object code)
  • 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序
  • 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
  1. 预处理:预处理之后产生的结果都放在test.i文件中,(#include  头文件包含,删除注释,使用空格替换注释,预处理阶段做的为文本操作)
  2. 编译:编译完成之后就停下来,结果保存至test.c中(把C代码翻译为汇编代码)
  3. 汇编:汇编完成之后就停下来,结果保存在test.o中(形成符号表)

3.预定义符号介绍

// 预定义符号
__FILE__	//进行编译的源文件
__LINE__	// 文件当前的行号
__DATE__	// 文件被编译的日期
__TIME__	// 文件被编译的时间
__STDC__	// 如果编译器遵循ANSIC,其值为1,否则未定义
#include 

// 预定义符号
/* __FILE__ 	//进行编译的源文件
 __LINE__	  // 文件当前的行号
__DATE__	  // 文件被编译的日期
__TIME__	 // 文件被编译的时间
__STDC__	// 如果编译器遵循ANSIC,其值为1,否则未定义
*/

int main()
{
	printf("%s\n",__FILE__);
	printf("%d\n",__LINE__);
	printf("%s\n",__DATE__);
	printf("%s\n",__TIME__);
   return 0;
}

运行结果: 

 把数据写进文件txt中(详细内容看--文件操作这一节内容)

#include 

// 预定义符号
/* __FILE__ 	//进行编译的源文件
 __LINE__	  // 文件当前的行号
__DATE__	  // 文件被编译的日期
__TIME__	 // 文件被编译的时间
__STDC__	// 如果编译器遵循ANSIC,其值为1,否则未定义
*/

int main()
{
	int i = 0;
	int arr[10] ={0};
	FILE *pf = fopen("log.txt","w"); // 打开目标文件
	for (i=0;i<10;i++){
	arr[i]=i;
		fprintf(pf,"file:%s  line:%d  date:%s  time:%s i=%d\n",
			__FILE__ ,__LINE__,__DATE__,__TIME__, i)
		}
	fclose(pf);
	pf = NULL;
	for(i=0;i<10;i++){
	printf("%d",arr[i]);
	}
   return 0;
}

4.预处理指令#define

#define        可以定义标识符,也可以定义宏

#define MAX 100
#define reg register	//给register重新起个名字叫做reg
#define do_forever	for(;;)	//用更形象的符号来替换一种实现

问题:在define定义标识符的时候,要不要在最后加上分号(;)

 不要加分号,因为在后面语句中会加分号

#define替换规则

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

注意:

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

宏定义的参数是完整的代入表达式 ,宏通常被应用于执行简单的运算(比如比较两个数的大小),宏比函数在程序的规模和速度方面更胜一筹

宏定义的函数无法被调试

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

5.宏和函数的对比

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

6.命令定义

宏名全部大写,函数名不要全部大写

7.预处理指令#include

#include    // 官方库文件
#include "****.h"       // 自己写的文件

""查找的范围广,但是如果所有的头文件都用“”查找,速度会比较慢

 防止头文件被多次重复引用

方法1:传统的写法

#ifndef __TEST_H__
#define   __TEST_H__

int ADD(int x,int y);
#endif

方法2:更简洁,现在的写法,不过在老的编译器里会编译不通过,出错

#pragma once
int ADD(int x,int y);

8.预处理指令#undef

用于移除一个宏定义

9.条件编译

条件满足,进行编译

用途:调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译

#include 
#define DEBUG

int main()
{
   int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
   int i = 0;
   for (i = 0; i < 10; i++)
   {
      arr[i] = 0;
#ifdef DEBUG
      printf("%d ", arr[i]);
#endif
   }
   return 0;
}

常见的条件编译指令

1.
#if 常量表达式
//
#endif
    如:
#define __DEBUG__ 1
//
#endif

    2.多个分支的条件编译
#if 常量表达式
//
#elif 常量表达式
//
#elif 常量表达式
//
#endif

    3.判断是否被定义
#if defined(symbol)   // 这两条语句等价
#ifdef symbol

#if !defined(symbol)  // 这两条语句等价
#ifndef symbol

你可能感兴趣的:(C语言学习笔记,c语言)