【C语言进阶】-7-程序环境和预处理

目录

  • 1. 程序的翻译环境和执行环境
  • 2. 详解编译+链接
    • 2.1 翻译环境
    • 2.2 编译的几个阶段
    • 2.3 运行环境
  • 3.预处理详解
    • 3.1 预定义符号
    • 3.2 #define
      • 3.2.1 #define 定义标识符
      • 3.2.2 #define 定义宏
      • 3.2.3 #define 替换规则
      • 3.2.4 #和##
      • 3.2.5 带副作用的宏参数
      • 3.2.6 宏和函数对比
      • 3.2.7 命名约定
    • 3.3 #undef
    • 3.4 命令行定义
    • 3.5 条件编译
    • 3.6 文件包含
      • 3.6.1 头文件被包含的方式
      • 3.6.2 嵌套文件包含
  • 4.其他预处理指令

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

【C语言进阶】-7-程序环境和预处理_第1张图片

2. 详解编译+链接

2.1 翻译环境

举例:
【C语言进阶】-7-程序环境和预处理_第2张图片

【C语言进阶】-7-程序环境和预处理_第3张图片

ps.VS→集成开发环境IDE:编辑+编译(编译器cl.exe)+链接(链接器link.exe)+调试

2.2 编译的几个阶段

【C语言进阶】-7-程序环境和预处理_第4张图片

ps.无法解析的外部符号→链接错误

2.3 运行环境

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
  2. 程序的执行便开始。接着便调用main函数(程序的入口)
  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
  4. 终止程序。正常or意外终止。

3.预处理详解

3.1 预定义符号

i.e. 已经内定的符号

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

【C语言进阶】-7-程序环境和预处理_第5张图片

#include

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

	return 0;
}

3.2 #define

  • define的本质是替换

3.2.1 #define 定义标识符

如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)

本质上是替换所以最好别加 ;

eg

> #define MAX 100; 
> int m=MAX;
> 即 int m=100;;
#define name stuff
//name 即 stuff
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
                          __FILE__,__LINE__ ,\
                          __DATE__,__TIME__ ) 
#define MAX 100;
int a = MAX;
//即 int a = 100;

3.2.2 #define 定义宏

#define name( parament-list ) stuff

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

【C语言进阶】-7-程序环境和预处理_第6张图片

#define MAX(x,y) (x > y ? x : y)
#define MUL(x,y) x*y

int max(int x, int y)
{
	return x * y;
}

int main()
{
	int n = MAX(3 + 4, 3 + 1);
	int m = max(3 + 2, 4);

	int mul = MUL(3 + 2, 4);

	printf("n=%d\n", n);
	printf("m=%d\n", m);
	printf("mul=%d\n", mul);

	return 0;
}

  • define定义宏使用建议:
  1. 别乱加空格;
  2. 牢记替换本质;
  3. 宏体最好带足括号(整体最好也带括号)(减少操作符优先级而颠倒顺序).

宏体最好带足括号例如

#define MAX(x,y) ((x) > (y) ? (x) : (y))

3.2.3 #define 替换规则

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

图解如下
【C语言进阶】-7-程序环境和预处理_第7张图片

注意:

  1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索
    【C语言进阶】-7-程序环境和预处理_第8张图片
#define NUM 10
int main()
{
	printf("hello NUM");
	return 0;
}

3.2.4 #和##

  • #
    
  • 关于双引号引用字符串
"hello world"="hello"" world"
//输出的都是:hello world

字符串是有自动连接的特点的

  • 关于下面这串代码:冗余→如何简化?
int main()
{
	int a = 10;
	printf("the value of a is %d\n", a);

	float b = 3.1f;
	printf("the value of b is %f\n", b);

	int c = 13;
	printf("the value of c is %d\n", c);

	return 0;
}

上述代码有三处不同:
【C语言进阶】-7-程序环境和预处理_第9张图片

  • 定义宏
    【C语言进阶】-7-程序环境和预处理_第10张图片

只有当字符串作为宏参数的时候才可以把字符串放在字符串中。

  • 使用#,把一个宏参数变成对应的字符串
    【C语言进阶】-7-程序环境和预处理_第11张图片
#define PRINT(val,format) printf("the value of "#val" is "format"\n", val)

int main()
{
	int a = 10;
	printf("the value of a is %d\n", a);

	float b = 3.1f;
	printf("the value of b is %f\n", b);

	int c = 13;
	printf("the value of c is %d\n", c);

	PRINT(a, "%d");
	PRINT(b, "%f");
	PRINT(c, "%d");
	return 0;
}

  • ##
    
  • A##B ⇨ AB ; hello##world ⇨ helloworld

##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
注:这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

3.2.5 带副作用的宏参数

副作用就是表达式求值的时候出现的永久性效果。

【C语言进阶】-7-程序环境和预处理_第12张图片

#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);
	return 0;
}

3.2.6 宏和函数对比

  • 宏的优点:
    1. 应用于执行简单的运算:规模和速度更胜一筹(函数包括:调用+计算+返回,调用和返回的时间>计算的时间是得不偿失的)
    1. 函数参数必须为确定的值,宏的参数更加宽容,与类型无关
  • 宏的缺点;
    1. 每次都会插入一份
    1. 没法调试
    1. 与类型无关,不够严谨
    1. 可能带来优先级的问题容易出错
  • 宏可以做到但是函数无法做到的;
  • 例一:前面有一例代码简化的例子(参见#和##的用法)
  • 例二:#define MALLOC(num,type) (type*)malloc(num*sizeof(type)). (简单的封装)
#define MALLOC(num,type) (type*)malloc(num*sizeof(type))

int main()
{
	int* p = MALLOC(10, int);
	     //=(int*)malloc(10*sizeof(int))
	return 0;
}

3.2.7 命名约定

一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。
那我们平时的一个习惯是:把宏名全部大写;函数名不要全部大写

例外:offsetof 、getchar等

3.3 #undef

这条指令用于移除一个宏定义

3.4 命令行定义

在编译的时候定义
【C语言进阶】-7-程序环境和预处理_第13张图片

3.5 条件编译

条件编译:满足条件就编译,不满足就不编译
常见的条件编译指令:
1.判断是否被定义

#if defined(symbol)  ==  #ifdef symbol   // symbol已被定义则语句被编译
#if !defined(symbol) ==  #ifndef symbol  // symbol未被定义则语句被编译

【C语言进阶】-7-程序环境和预处理_第14张图片

#include

#define MAX 0

int main()
{
#ifdef MAX
	printf("Hello,fantasy_13_7");
#endif // MAX
#undef MAX//取消MAX的定义

#ifdef MAX
	printf("Hello,fantasy_13_7");//MAX未定义语句不编译
#endif // MAX
	return 0;
}
#if 常量表达式  //...
#endif //常量表达式由预处理器求值。

【C语言进阶】-7-程序环境和预处理_第15张图片

#define NUM 13
#define A 0
int main()
{
#if 1
	printf("Hello,world!\n");
#endif // 1
#if MAX
	printf("Hello,Marie!\n");//MAX未定义表达式为假,语句不编译
#endif // MAX
#if 0
	printf("Hello,Ada!\n")//0为假,语句不编译
#endif // 0
#if NUM
		printf("Hello!\n");
#endif //  
#if A
	printf("xxxx\n");//A为0表达式为假,语句不编译
#endif // A
	return 0;
}

3…多个分支的条件编译(只选择一个分支进入)

#if 常量表达式  //...
#elif 常量表达式  //...
#else  //...
#endif
#define NUM 13
#define A 0
int main()
{
#if 1
	printf("Hello,world!\n");

#elif MAX
	printf("Hello,Marie!\n");

#else
	printf("Hello,Ada!\n")
#endif 
	return 0;
}

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

【C语言进阶】-7-程序环境和预处理_第16张图片

3.6 文件包含

3.6.1 头文件被包含的方式

本地文件#include" "先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误。
库文件#include< >:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
查找策略不一样
ps.库文件也可以用" "但是查找效率更低

3.6.2 嵌套文件包含

文件中头文件多次被引用,造成了文件内容的重复

  • 如何高效的使用头文件?
  • 1.条件编译
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif   //__TEST_H__

【C语言进阶】-7-程序环境和预处理_第17张图片

    1. #pragma once可以避免头文件的重复使用

4.其他预处理指令

参考《C语言深度解剖》

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