返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言进阶教程之预处理
  • 551
分享到

C语言进阶教程之预处理

摘要

目录一.代码运行是的两种环境二.翻译环境三.预定义符号四.#define1.define  定义宏2.带有副作用的宏参数五.#define定义宏 与函数对比六.预处理指令七

一.代码运行是的两种环境

1.翻译环境,在这个环境中源代码被转换为可执行的机器指令。
2.执行环境,它用于实际执行代码

下面主要讲解翻译环境。

二.翻译环境

从.c 文件到 .exe 文件需要经过编译器的翻译,而翻译又分为 编译和链接两个部分

编译又分为三个部分:

1.预编译:又叫预处理,在这个部分主要完成头文件的包含,#define的替换,注释的删除;

2.编译:主要完成语法分析,词法分析,词义分析,符号汇总(符号包括全局性的变量和函数),生成汇编代码;

3.汇编:生成二进制指令,形成符号表(符号表是由符号和其地址组成的);

链接:合并段表,合并符号表(在这个阶段会发现未定义的函数)。

见下图:

三.预定义符号

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

四.#define

1.define  定义宏

宏的申明方式:

#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。

注意 name 需与后面的括号紧密相连,不可以有空格,如果有任何空白存在,参数列表就会被解释为stuff的一部分。

注意当我们定义宏的时候,不要吝啬括号!

来看下面一个例子:

#define MOD(x,y) x*y
 
int main()
{
	int m = MOD(2+3,2);
	printf("%d\n", m);
	return 0;
}

对初学者来说,这段代码的答案很容易被认为式10,但事实并非如此,因为宏是在预处理阶段先替换掉,然后在进行计算,所以在没有括号的情况下,替换后是这样的:2+3*2=8;所以若是想要得到10这个结果,就要加上括号,即:

#define MOD(x,y) ((x)*(y))

2.带有副作用的宏参数

我们知像是前置++ ,后置++这种的运算符是会改变操作数的值属性的,那它如果应用到#define 定义的宏中会是怎么样呢?

我们来看下面这个例子:

#define MAX(x,y) ((x)>(y)?(x):(y))
 
int main()
{
	int a = 4;
	int b = 6;
	int m = MAX(a++, b++);
	printf("m=%d\n", m);
	printf("a=%d b=%d\n", a, b);
	return 0;
}

最后的答案会是多少呢?

首先完成宏参数的替换:((a++)>(b++)?(a++):(b++))

后置++是先使用后++,因为4<6,所以执行后面的 b++,经过前面的++,此时a=5,b=7,所以先把7赋给m,然后b++,得到b=8;

即m=7  a=5  b=8

总结: 

1.#define 定义的符号需要先原封不动的替换掉,所以建议在#define 后面不加 ' ; ' ;

2.#define 定义的宏不要吝啬括号,以免出现出乎意料的结果;

3.避免使用带有副作用的运算符。

五.#define定义宏 与函数对比

六.预处理指令

所有的预处理指令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理指令应从第一列开始。下面列出了所有重要的预处理指令:

七.条件编译

可以实现将一条语句(一组语句)编译或者放弃。

常见的条件编译指令:

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

 例:

int main()
{
#if 1    //如果这个常量表达式为真,则执行后面的语句,反之则不执行
	printf("haha\n");
#endif
	return 0;
}

运行结果:

2.多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式 (注意这里是 elif ,而不是else if )
//...
#else
//...
#endif 

例:

#define M 10
 
int main()
{
#if M==5
	printf("mafumafu\n");
#elif M==10
	printf("Eve\n");
#elif M==7
	printf("Sou\n");
#elif M==2
	printf("amatsuki\n");
#else 
	printf("soraru");
#endif
	return 0;
}

运行结果:

3.嵌套指令 

        #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

八.头文件包含的方式

1.  双引号式 #include "test.h"  :先在源文件所在目录下查找,如果该头文件未找到,编译器                                                     就像查找库函数头文件一样在标准位置查找头文件。
                                                 如果找不到就提示编译错误。

2.尖括号式 #include <stdio.h>: 查找头文件直接去标准路径下去查找,如果找不到就提示编                                                     译错误。

所以说库里的头文件也可以用 双引号 包含 ,但并不建议这样做,因为双引号包含没有尖括号包含的查找的快。

嵌套文件包含

 comm.h和comm.c是公共模块。
test1.h和test1.c使用了公共模块。
test2.h和test2.c使用了公共模块。
test.h和test.c使用了test1模块和test2模块。
这样最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复。

如何防止这种问题出现?

有两种解决方式:

1.利用条件编译指令

#ifndef __TEST_H__   //如果没有定义  TEST_H__  则执行下一句代码  定义 __TEST_H__
#define __TEST_H__
#endif

2.利用预处理指令  #pragma   once

《高质量C/C++编程指南》中的两个问题

1. 头文件中的 ifndef/define/endif是干什么用的?

   防止头文件的重复引用。
2. #include <filename.h> 和 #include "filename.h"有什么区别?

   文件的查找策略不同。

总结

到此这篇关于C语言进阶教程之预处理的文章就介绍到这了,更多相关C语言预处理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言进阶教程之预处理

本文链接: https://lsjlt.com/news/195508.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C语言进阶教程之预处理
    目录一.代码运行是的两种环境二.翻译环境三.预定义符号四.#define1.define  定义宏2.带有副作用的宏参数五.#define定义宏 与函数对比六.预处理指令七...
    99+
    2023-02-10
    什么是预处理 c语言编译预处理命令 c语言预处理指令作用
  • C语言进阶教程之函数指针详解
    目录一、函数指针1.概念1.2函数指针的使用方法1.3练习巩固1.4小结一下二、阅读两段有趣的代码1.( *(void( *)( ))0 )( )2.void (* signal(i...
    99+
    2024-04-02
  • C语言进阶教程之循环语句缺陷详析
    目录前言1 循环语句的三要素2 使用不同循环语句实现六种排列组合2.1 第一种排列(ABC)2.2 第二种排列(ACB)2.3 第三种排列(BCA)2.4 第四种排列(CBA)2.5...
    99+
    2024-04-02
  • C语言进阶教程之字符串&内存函数
    目录前言:一、求字符串长度strlenstrlen函数的模拟实现二、长度不受限制的字符串函数strcpystrcpy函数的模拟实现strcatstrcat函数的模拟实现strcmps...
    99+
    2024-04-02
  • 详解C语言之预处理(上)
    目录程序的翻译环境编译预编译:编译:汇编:链接合并段表:#define的用法1.#define定义标识符,例如2.#define定义宏  3.#define实现将...
    99+
    2024-04-02
  • 详解C语言之预处理(下)
    目录#define定义宏带副作用的宏参数#define定义宏的优点#define定义宏劣势预处理预定义符号预处理指令条件编译1.调试性代码2.防止重复的头文件多次编译总结#defin...
    99+
    2024-04-02
  • C语言进阶教程之字符函数&字符串函数
    目录1、strlen1.1、三种模拟实现2、长度不受限制的字符串函数2.1、strcpy2.1.1、模拟实现2.2、strcat2.2.1、模拟实现2.3、strcmp2.3.1、模...
    99+
    2024-04-02
  • C语言进阶栈帧示例详解教程
    目录正片开始栈有什么用?寄存器main函数创建局部变量创建函数部分形参与实参正片开始 今天来讲讲我对栈帧创建与销毁的拙见。理解什么是栈帧首先知道什么是栈: 在数据结构中, 栈是限定仅...
    99+
    2024-04-02
  • C语言进阶学习之指针
    目录1.指针概念回顾2.字符指针3.数组指针和指针数组3.1数组指针的含义3.2&数组名vs数组名3.3数组指针4.数组传参和指针传参4.1一维数组传参4.2二维数组传参4....
    99+
    2024-04-02
  • C语言预编译#define(预处理)
    目录一、预定义符号二、#define 定义标识符三、#define 定义宏四、#define 替换规则:五、#和## 两个符号(少见)六、宏和函数的对比七、#undef一、预定义符号...
    99+
    2024-04-02
  • C语言预处理详解
    目录一,预定义符号二,#define1,#define 定义标识符2,#define 定义宏3,#define 替换规则三,##的作用1,概念2,带副作用的宏参数3,宏和函数对比四,...
    99+
    2024-04-02
  • C语言进阶:指针的进阶(1)
    目录指针进阶字符指针字符指针的作用字符指针的特点指针数组指针数组的定义指针数组的使用总结指针进阶 我们在初阶时就已经接触过指针,了解了指针的相关内容,有: 指针定义:指针变量,用于...
    99+
    2024-04-02
  • C语言进阶:指针的进阶(2)
    目录数组指针数组指针的定义&数组名和数组名数组指针的使用反面用例正面用例Example类型辨别方法总结数组指针 由前面的例子,不难得出,数组指针是指向数组的指针,是指针而非...
    99+
    2024-04-02
  • C语言进阶:指针的进阶(3)
    目录数组传参和指针传参一维数组传参二维数组传参一级指针传参二级指针传参总结数组传参和指针传参 实践之中不免会碰到数组和指针作函数参数而如何设计形参的问题。 一维数组传参 一维数...
    99+
    2024-04-02
  • C语言进阶:指针的进阶(4)
    目录函数指针函数指针的定义函数指针的类型函数指针的使用Example总结函数指针 函数指针的定义 整型指针存放整型的地址;数组指针存放数组的地址;那么类比可得,函数指针存放函数的地...
    99+
    2024-04-02
  • C语言进阶:指针的进阶(5)
    目录函数指针数组函数指针数组的定义函数指针数组的使用转移表回调函数指向函数指针数组的指针总结函数指针数组 //整型数组 - 存放整型变量 int arr[10]; //字符数组 ...
    99+
    2024-04-02
  • C语言深入探究程序的编译之预处理
    目录1.程序的翻译环境和执行环境2.详解编译与链接2.1翻译环境2.2编译本身也分为几个阶段2.3运行环境3.预处理详解3.1预处理符号3.2#define3.2.1#define定...
    99+
    2024-04-02
  • C语言新手初阶教程之三子棋实现
    目录三子棋创建项目环境头文件内容编写main函数(test.c)实现每一个接口函数1.board2.printfboard3.play4.computerplay5.win总结三子棋...
    99+
    2024-04-02
  • C语言进阶之文件操作详解
    目录0. Intro1. 文件名2. 文件的打开和关闭2.1 文件指针2.2 打开和关闭文件3. 文件顺序读写3.1 利用以上函数实现拷贝文件操作3.2 二进制的读写3.3 格式化输...
    99+
    2024-04-02
  • react进阶教程之异常处理机制error Boundaries
    目录Error Boundaries介绍ComponentDidCatch 参数触发error boundaries后程序要走向哪里?对于不能捕获的错误的新处理方式在堆中跟踪comp...
    99+
    2022-11-13
    react事件处理机制 react error Boundaries react异常处理机制
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作