广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言程序环境中的预处理详解
  • 965
分享到

C语言程序环境中的预处理详解

2024-04-02 19:04:59 965人浏览 泡泡鱼
摘要

目录一、翻译环境二、执行环境三、预处理1. 预处理符号2. #define定义标识符3. #define定义宏4. #和##5. 宏和函数的对比6. 条件编译7. 文件包含总结一、翻

一、翻译环境

整个翻译环境大致就可以画成这样一张图。

下列有几点需要说明:

1. 组成一个程序的每一个源文件通过编译过程分别转换成目标文件(在linux中目标文件的后缀为.o;而在windows中目标文件后缀为.obj)

2. 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序

3. 链接器同时也会引入标准C函数库(链接库)中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中

接下来介绍每一步在Linux系统下整个翻译环境的实现方法,以及每一个步骤的作用。

编译可分为三个部分:

(1)预处理:输入指令GCc -E test.c -o,就会将test.c文件变为test.i文件。这一步的作用是是对头文件(#include)的包含、删除注释、#define定义符号的替换等文本操作(下文会对预处理这一个步骤展开详细的介绍)

(2)编译:输入指令gcc -S test.i,就会将test.i文件变为test.s文件,这一步主要作用是把C语言代码转换成汇编代码,其中包含4步:1. 语法分析;2. 词法分析;3. 语义分析;4. 符号汇总

(3)汇编:输入指令gcc -c test.s,就会将test.s文件变为test.o文件,这一步是把汇编代码转换成二进制的指令,这一步是会形成符号表,此时的符号表为接下来的链接操作做出了准备

多个.c文件通过编译过程后形成.o目标文件,在要执行链接的时候,输入指令gcc test.o add.o -o test,就会将.o文件变成可执行文件,这其中的操作包括合并段表和符号表的合并和重定位,这一步主要就是将多个目标文件进行连接的时候通过符号表查看来自外部的符号是否真实存在,这样就完成了整个翻译环境的操作。

二、执行环境

对于程序的执行过程可分为以下几个步骤:

1. 程序必须载入内存中。在有操作系统的环境中:一般由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成

2. 程序的执行开始。之后就会调用main函数

3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址;程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值

4. 终止程序。正在终止main函数,也有可能是意外终止的情况

三、预处理

1. 预处理符号

在C语言中,有些预处理符号是语言内置的,就比如:

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

2. #define定义标识符

#define定义的标识符可以是常量、简化关键字、一些符号等,例如:

#define M 10   //定义常量
#define reg reGISter   //将关键字简化
#define do_forever for(;;)   //用形象的符号来替换一种实现
#define CASE break;case   //在写case语句的时候会自动地把break写上

对于#define定义标识符来说,如果定义的东西过长,还可以分几行来写,除最后一行外,其他每行都加上'\',例如:

#define DEBUG_PRINT printf("file:%s\tline:%d\t \
							date:%s\ttime:%s\n",\
							__FILE__,__LINE__, \
							__DATE__,__TIME__)

3. #define定义宏

在#define定义标识符外,#define还有一个规定,就是允许把参数替换到文本中,进而就形成了#define定义宏。声明的方式如下:

#define name(parament-list) stuff

这里的parament-list是由一个逗号隔开的符号表,在实际的代码中他们也会存在于stuff中。

其中值得注意的是:

1. 参数列表的左括号必须与name相邻

2. 如果parament-list与stuff两者之间有任何空白存在,参数列表就会被注释为stuff的一部分

了解了#define定义宏是如何写后,接下来就是#define定义宏的替换规则:

1. 在调用宏的时候,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换

2. 替换文本随后被插入到程序中原来的文本位置,参数名被它们的值所替换

3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程

所以,总结以上规则后得出的结论就是:如果是#define定义宏用于对数值表达式进行求值的宏定义都应该加上括号,避免在使用宏时由于参数中的操作符或者邻近操作符之间不可预料的相互作用。

当然,对于#define的使用还有几个注意的点:

1. 宏参数和#define定义中可以出现其他#define定义的符号,但是对于宏,不能出现递归

2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

4. #和##

对于一些想要把参数插入到字符串中的情况,我们会使用#来把一个宏参数变成对应的字符串,下面举个例子:

如果是直接打印出来的话,因为字符串是可以拼接的,所以就如这样:

#include <stdio.h>
int main()
{
	int a = 10;
	printf("the value of ""a"" is %d\n", a);
	return 0;
}

那么,对于定义宏参数来说,就应该这样:

#include <stdio.h>
#define PRINT(n) printf("the value of "#n" is %d\n", n)
int main()
{
	int a = 10;
	PRINT(a);
	return 0;
}

这样字符串中的n才会根据跟着宏参数的值变化而变化。

而##的作用是可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本段创建标识符。但是这样连接必须产生一个合法的标识符,否则会报错说未定义标识符。

5. 宏和函数的对比

宏的优势:1. 在执行一些小型计算工作的时候,定义宏比调用函数和从函数返回的代码执行所需要的时间会更短;2. 函数的参数必须声明为特定的类型,二宏参数不用

宏的劣势:1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度;2. 宏是无法进行调试的,而函数可以;3. 宏由于没有进行类型定义,所以有时候就会不够严谨;4. 宏可能会带来运算符的优先级的问题,导致程序容易出错

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

6. 条件编译

下面列举一些编译指令:

1. #undef 该指令用于移除一个宏定义

2. 该指令是判断应该执行哪一个语句块

#if 常量表达式
    执行语块
#elif 常量表达式
    执行语块
#else
    执行语块
#endif

3. 该指令是判断是否被定义

#if define(symbol)
    如果有定义,执行此语句块
or
#ifdef symbol
    如果有定义,执行此语句块
or
#if !define(symbol)
    如果没有定义,执行此语句块
or
#ifndef symbol
    如果没有定义,执行此语句块

4. 对于条件编译指令来说,其实还可以对其进行嵌套,称为嵌套指令

7. 文件包含

我们在一些较大工程进行编译的时候、在多人合作同一块项目工程的时候,可能会出现头文件重复包含的情况,如果真是这样,则会导致整个代码运行时的效率大大降低,所以对头文件避免重复包含就显得十分重要了。那么,如何避免呢?下面就有一段代码可以用来避免这种情况:

#ifndef __TEST_H__
#define __TEST_H__
    写头文件内容
#endif

这段代码就可以很好地解决了头文件重复包含的问题,但是实际上,如果是在VS的环境下进行编译,会自动在最开始的地方写上:#pragma once,这句代码一样也是可以解决重复包含的问题。

那么,解决完头文件重复包含的问题后,就来介绍两种头文件包含的方式:

1. 用引号包含的头文件,例如:#include "test.h"。这种包含方式头文件的查找策略是先在源文件所在的目录下查找,如果该头文件未被找到,编译器就像查找库函数头文件一样在标准位置查找头文件,如果还找不到,则会直接报错。

2. 用尖括号包含头文件,例如:#include 。这种包含方式则是未有第一步,直接进行第二步。

但是不能说为了保证万无一失,直接把全部头文件的包含都用引号进行包含,这样的话有些时候其实是用尖括号的情况而错用引号导致程序的执行速度下降、效率下降等。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!     

--结束END--

本文标题: C语言程序环境中的预处理详解

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

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

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言程序环境中的预处理详解
    目录一、翻译环境二、执行环境三、预处理1. 预处理符号2. #define定义标识符3. #define定义宏4. #和##5. 宏和函数的对比6. 条件编译7. 文件包含总结一、翻...
    99+
    2022-11-13
  • c语言的程序环境与预处理详解
    目录1.翻译环境2.运行环境3.预处理详解3.1#define定义的符号3.2#define定义的宏3.3#define的替换规则3.4#与##4.宏与函数对比5.#undef6.条...
    99+
    2022-11-13
  • C语言程序环境和预处理详解分析
    目录一、程序的翻译环境和运行环境程序的翻译环境链接阶段执行环境(运行环境)二、预处理详解预定义符号#define定义标识符#define定义宏#define 替换规则#和##两个预处...
    99+
    2022-11-13
  • C语言中的程序环境与预处理详情
    目录1.程序的翻译环境和执行环境2.详解编译和链接2.1程序翻译环境下的编译和链接2.2深入编译和链接过程2.3运行环境3.预处理详解3.1预定义符号3.2#define3.2.1#...
    99+
    2022-11-13
  • C语言中程序环境和预处理的详细图文讲解
    目录1. 程序的翻译环境和执行环境 2. 详解编译+链接 2.1 翻译环境2.2 编译本身也分为几个阶段2.3 运行环境  3. 预处理详解3.1 预定...
    99+
    2023-02-16
    c语言程序环境和预处理的区别 c语言的编译预处理 c语言程序环境
  • C语言程序环境中的预处理实例分析
    本篇内容介绍了“C语言程序环境中的预处理实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、翻译环境整个翻译环境大致就可以画成这样一张...
    99+
    2023-06-29
  • C语言的程序环境与预处理你真的了解吗
    目录1.翻译环境2.运行环境3.预处理详解3.1#define定义的符号3.2#define定义的宏3.3#define的替换规则3.4#与##4.宏与函数对比5.#undef6.条...
    99+
    2022-11-13
  • C语言中程序环境与预处理的示例分析
    这篇文章主要介绍了C语言中程序环境与预处理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。c语言代码的实现包含两种环境1.翻译环境,将源代码转化成可执行的机器指令2....
    99+
    2023-06-29
  • C语言中的程序环境与预处理实例分析
    本篇内容主要讲解“C语言中的程序环境与预处理实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言中的程序环境与预处理实例分析”吧!1.程序的翻译环境和执行环境在ANSI C的任何一种实现...
    99+
    2023-07-02
  • 一起来学习C语言的程序环境与预处理
    目录1.程序的翻译环境和执行环境2.gcc C语言编译器来演示编译过程2.1编译2.2编译:2.3运行环境3详解预处理3.1预定义符号3.2#define3.2.1#define定义...
    99+
    2022-11-13
  • C语言程序的编译与预处理详解
    目录一、程序的编译1、 编译阶段2、链接二、预处理详解1、预定义符号2、#define定义的标识符3、#define定义的宏4、#unef总结一、程序的编译 我们写的源文件(*.c)...
    99+
    2022-11-12
  • C语言预处理详解
    目录一,预定义符号二,#define1,#define 定义标识符2,#define 定义宏3,#define 替换规则三,##的作用1,概念2,带副作用的宏参数3,宏和函数对比四,...
    99+
    2022-11-12
  • 详解C语言中的预处理命令
    目录一、预处理命令简介二、宏定义1、宏定义的定义2、宏定义的使用3、宏定义的终止——#undef三、文件包含初学C语言的时候,我们会在开头写下一句话,#inc...
    99+
    2022-12-08
    C语言预处理命令 C语言预处理
  • 详解C语言之预处理(上)
    目录程序的翻译环境编译预编译:编译:汇编:链接合并段表:#define的用法1.#define定义标识符,例如2.#define定义宏  3.#define实现将...
    99+
    2022-11-12
  • 详解C语言之预处理(下)
    目录#define定义宏带副作用的宏参数#define定义宏的优点#define定义宏劣势预处理预定义符号预处理指令条件编译1.调试性代码2.防止重复的头文件多次编译总结#defin...
    99+
    2022-11-12
  • 详解C语言的预处理效果
    目录前言一、预定义符号二、#define1.宏2.宏与函数3.带副作用的宏参数4. 宏和函数的不同5.#undef三、条件编译四、文件包含1.函数库文件包含2.本地文件包含总结前言 ...
    99+
    2022-11-12
  • 详解C语言#define预处理宏定义
    目录#define介绍: #define宏定义无参的一般形式为:#define  标识符 常量 #define宏定义有参的一般形式为:#define  标识符(参...
    99+
    2022-11-12
  • C语言程序环境编译+链接理论
    目录一、程序的翻译环境(编译和链接)二、程序的运行环境一、程序的翻译环境(编译和链接) 在ANSI C 的任何一种实现中,存在两个不同的环境: 第一种是翻译环境,在这个环境中源代码被...
    99+
    2022-11-13
  • C语言程序环境编译的方法
    这篇文章主要介绍“C语言程序环境编译的方法”,在日常操作中,相信很多人在C语言程序环境编译的方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言程序环境编译的方法”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-06-30
  • C语言预处理预编译命令及宏定义详解
    目录程序翻译环境和执行环境翻译环境:详解编译+链接1. 编译 — 预处理/预编译 test.c ---- test.i2. 编译 — 编译 test.i ---- test.s3. ...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作