【高级语言程序设计(一)】第 9 章:编译预处理命令

news2024/11/18 0:43:04

目录

前言

一、宏定义命令

(1)无参宏定义

(2)有参宏定义

① 带参数的宏定义

②  带参宏定义与函数的区别

二、文件包含命

(1)文件包含命令的定义

(2)文件包含命令的格式

(3)“文件包含” 处理

三、条件编译命令

(1)条件编译命令的定义

(2)条件编译命令的格式

① 格式一:#ifdef

② 格式二:#ifndef

③ 格式三:#if 



前言

编译预处理命令:

编译预处理 是指对源程序进行编译之前,先对源程序中的各种预处理命令进行处理;然后再将处理结果和源程序一起进行编译,以获得目标代码。


一、宏定义命令

C 语言包括两种宏定义命令,即 无参宏定义命令 有参宏定义命令
通常,宏定义用于定义程序中的符号常量、类型别名、运算式代换和语句代换等。

(1)无参宏定义

【格式】 #define 宏名 字符序列
【说明】
  1. “宏名” 是标识符的一种,命名规则与标识符相同,通常采用大写字母,以便与变量区分
  2. “字符序列” 可以是常量、表达式各种符号等。
  3. “宏名” 和 “字符序列” 之间使用空格符分隔。
  4. 宏定义不是 C 语言的语句,所以不能在行尾加分号。否则,宏展开时会将分号作为字符串的一个字符,用于替换宏名。
  5. 宏展开时,预处理程序仅按宏定义简单地替换宏名,而不做任何检查。
  6. 对于双引号括起来的字符串内的字符,即使与宏名重名,也不进行宏展开。
  7. 宏定义命令 #define 出现在函数的外部,宏名的作用域是从宏定义开始到本文件结束。通常,宏定义命令放在文件的开头。
  8. 宏定义时,可以引用已定义的宏名,宏展开是逐层替换的。

【优点】

  1. 提高源程序的可读性
  2. 提高源程序的可修改性,修改宏定义中的“字符序列”可以起到一改全改的作用
  3. 避免源程序中重复书写字符串

(2)有参宏定义

【格式】 #define  宏名(形参表)  带参数的字符序列
【有参宏定义的调用和宏展开格式 宏名(实参表)
【宏展开的过程
  1. 若程序中有带实参的宏,则按照 #define 指定的字符序列从左至右进行替换
  2. 若宏定义中包含形参,则用实参直接替换宏定义命令行中相应的形参,其中实参可以是常量、变量或表达式
  3. 若宏定义的字符序列中的字符是非形参字符则替换时保留不变

【说明

  1. 定义有参宏时,宏名与左圆括号之间不能留有空格。否则,C 编译程序将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。
  2. 有参的宏展开只是将实参作为字符串,简单地替换形参字符串,而不进行任何语法检查。

① 带参数的宏定义

【格式】 #define  宏名(参数表)  宏体
【注意】“宏体” 不能加空格
【功能】进行字符串替换,并进行参数替换
【示例

 【说明

  1. 宏展开:形参用实参换,其它字符保留
  2. 宏体及各形参外一般应加括号 ()

/* 示例:使用带参数的宏 */
#include <stdio.h>
#define PI 3.1415926
#define S(r) PI*r*r
void main()
{ 
  float a,area;
  a=3.6; area=S(a);
  printf("r=%f\narea=%f\n", a, area);
}

/* 运行结果 */
r=3.600000
area=40.715038

②  带参宏定义与函数的区别

【区别】

  • 函数调用时,先求实参表达式的值,再带入形参。 宏只进行简单字符替换,不求值。
  • 函数调用在程序运行时处理和分配临时内存单元。 宏展开在编译时进行,不分配内存单元,无值传递和返值。
  • 函数要定义形实参且类型一致,宏无类型,其参数无类型。
  • 函数只有一个返回值,宏可以设法得到几个结果。
  • 宏展开使源程序变长,函数调用源程序不变长。
  • 宏替换不占运行时间,只占编译时间。 函数调用占运行时间。

【示例】

/* 示例:使用宏带回几个结果 */
#include <stdio.h>
#define PI 3.1415926
#define CIRCLE(R,L,S,V)  L=2*PI*R;S=PI*R*R;
                         V=4.0/3.0*PI*R*R*R
void main()
{ 
  float r, l, s, v;
  scanf("%f", &r);
  CIRCLE(r, l, s, v);
  printf("r=%6.2f, l=%6.2f, s=%6.2f, v=%6.2f\n", r, l, s, v);
} 

/* 宏展开后 */
void main()
{ 
  float r, l, s, v;
  scanf("%f", &r);
  l=2*3.1415926*r; s=3.1415926*r*r; v=4.0/3.0*3.1415926*r*r*r;
  printf("r=%6.2f, l=%6.2f, s=%6.2f, v=%6.2f\n", r, l, s, v);}


/* 运行结果 */
3.5
r=3.50, l=21.99, s=38.48, v=179.59

/* 示例 1 :用宏定义和函数实现同样的功能 */
#define MAX(x,y) (x)>(y)?(x):(y) 
...
main()
{ 
  int a,b,c,d,t; 
  ...
  t=MAX(a+b,c+d); 
  ...
}

/* 宏展开后 */
t=(a+b)>(c+d)?(a+b):(c+d);


/* 示例 2 :用宏定义和函数实现同样的功能 */
int max(int x, int y)
{ 
  return(x>y?x:y);
}

main()
{ 
  int a, b, c, d, t; 
  ...
  t=max(a+b,c+d); 
  ...
}
/* 示例 1 :用宏代表输出格式 */
#include <stdio.h>
#define PR printf
#define NL "\n"
#define D "%d"
#define D1 D NL
#define D2 D D NL
#define D3 D D D NL
#define D4 D D D D NL
#define S "%s"

/* 示例 2 :用宏代表输出格式 */
void main()
{ 
  int a, b, c, d;
  char string[]="CHINA";
  a=1; b=2; c=3; d=4;
  PR(D1, a);
  PR(D2, a, b);
  PR(D3, a, b, c);
  PR(D4, a, b, c, d);
  PR(S, string);
}


/* 运行结果 */
1
12
123
1234
CHINA


二、文件包含命

(1)文件包含命令的定义

  1. 文件包含是指一个源文件可以将另一个源文件的全部内容包含进来。
  2. 文件包含命令可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中。

(2)文件包含命令的格式

【格式】
  1. #include “文件名”
  2. #include <文件名>
【区别】 上述 2 种格式的区别仅在于:
  • 格式 1,系统首先到当前源文件所在的目录查找被包含文件,如果没有找到,再到系统指定的 “包含文件目录” 去查找,必要时在文件名前加上所在的路径。
  • 格式 2 ,直接到系统指定的 “包含文件目录” 去查找。
【说明】
  1. 编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到 #include 命令出现
  2. 的位置上。
  3. 文件头部的被包含文件称为 “标题文件” 或 “头部文件” ,常以 “h”(head)作为扩展名,简称头文件。
  4. 一条文件包含命令,只能指定一个被包含文件。
  5. 由于库函数以及某些宏定义都存放在系统文件中,一般都存放在系统指定的路径下,所以将存放库函数的头文件包含在源程序时一般建议采用格式二,即 #include <文件名>。
  6. 文件包含命令允许嵌套,即一个被包含的文件中可以再使用文件包含命令包含另一个文件,而在该文件中还可以再包含其他文件。
  7. 若被包含文件的内容发生变化,则应该对包含此文件的所有源文件重新进行编译。

(3)文件包含” 处理

【一般形式】
  1. #include “文件名”
  2. #include <文件名>

【区别】上述 2 种格式的区别仅在于:

  • < >  直接按标准目录搜索
  • " " 先在当前目录搜索,再搜索标准目录可指定路径
【功能】一个源文件可将另一个源文件的内容全部包含进来
【处理过程】
  • 预编译时用被包含文件的内容取代该预处理命令,再将 “包含” 后的文件作为一个源文件单位进行编译,得目标文件 .obj



三、条件编译命令

(1)条件编译命令的定义

C 语言提供了条件编译命令,使得用户可以选择对源程序的一部分内容进行编译,即不同的编译条件产生不同的目标程序。

(2)条件编译命令的格式

① 格式一:#ifdef

【格式】
#ifdef 标识符
  程序段1
[#else
  程序段2]
#endif

【功能】

  • 当 “标识符” 已经被 #define 命令定义,则编译程序段1;否则编译程序段2。
  • 其中 [] 中的内容,即 #else 部分可以省略,即:
    #ifdef 标识符
      程序段1
    #endif

② 格式二:#ifndef

【格式】
#ifndef 标识符
  程序段1
[#else
  程序段2]
#endif

【功能】

  • 当 “标识符” 没有被 #define 命令定义,则编译程序段1;否则编译程序段2。
  • 其中 [] 中的内容,即 #else 部分可以省略,即:
    #ifndef 标识符
      程序段1
    #endif

③ 格式三:#if 

【格式】
#if 标识符
  程序段1
[#else
  程序段2]
#endif

【功能】

  • 当表达式为 “真”(非 0 )时,则编译程序段1;否则编译程序段2。
  • 其中 [] 中的内容,即 #else 部分可以省略,即:
    #if 标识符
      程序段1
    #endif

【注意】

  • 其中的 “表达式” 是在编译阶段计算值的,所以此处的 “表达式” 不能是变量,必须是常量或用宏定义命令 #define 定义的标识符。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/570268.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Leetcode60天带刷】day02—— 977.有序数组的平方、209.长度最小的子数组、 59.螺旋矩阵II

题目&#xff1a;997.有序数组的平方 Leetcode原题链接&#xff1a;997.有序数组的平方——力扣 思考历程与知识点&#xff1a; 题目的意思很简单&#xff0c;就是把每个数的平方&#xff0c;按从小到大的顺序排个序&#xff0c;再输出出来。 第一想法是先每个数平方一遍&a…

Stream API的使用

使用Stream API对集合中的数据进行操作&#xff0c;就类似使用SQL语句对数据库执行查询 Stream不会存储数据Stream不会改变源对象&#xff0c;而是返回一个持有结果的新StreamStream是延迟执行的&#xff0c;只有在需要结果的时候才执行&#xff0c;即只有执行终止操作&#xf…

离散数学_十章-图 ( 2 ):图的术语和几种特殊的图

&#x1f4f7;10.2 图的术语和几种特殊的图 1. 基本术语1.1 邻接&#xff08;相邻&#xff09;1.2 邻居1.3 顶点的度1.4 孤立点1.5 悬挂点例题 2. 握手定理3. 握手定理的推论4. 带有有向边的图的术语4.1 邻接4.2 度——出度 和 入度4.3 例题&#xff1a; 5. 定理&#xff1a;入…

PHP 反序列化漏洞

PHP反序列化漏洞在实际测试中出现的频率并不高&#xff0c;主要常出现在CTF中。 PHP序列化概述 PHP序列化函数&#xff1a; serialize&#xff1a;将PHP的数据&#xff0c;数组&#xff0c;对象等序列化为字符串unserialize&#xff1a;将序列化后的字符串反序列化为数据&…

chatgpt赋能python:Python单词库的重要性

Python单词库的重要性 Python是一种高级编程语言&#xff0c;被广泛用于应用程序开发、网络编程、数据科学和人工智能开发等领域。而在Python编程中&#xff0c;单词库(或词典)的重要性不言而喻。单词库就是存放Python程序中经常使用的关键字、方法名、函数名等词汇的地方。本…

SpringBoot --- 实用篇

一、热部署 1.1、概念 什么是热部署&#xff1f;简单说就是你程序改了&#xff0c;现在要重新启动服务器&#xff0c;嫌麻烦&#xff1f;不用重启&#xff0c;服务器会自己悄悄的把更新后的程序给重新加载一遍&#xff0c;这就是热部署。 ​ 热部署的功能是如何实现的呢&…

谷歌浏览器被2345劫持

方法1&#xff1a; 打开控制面板的卸载程序&#xff0c;搜索2345&#xff0c;把那个恶心的“安全组件-2345”卸载掉&#xff01;&#xff01; 这个方法比修改 host 以及注册表要好使地多&#xff01; 参考网址&#xff1a; 【小技巧】修复chrome被2345劫持 方法2&#xff1a; …

Alma Linux 9.2、Rocky Linux 9.2现在是RHEL 9.2的替代品

随着Red Hat Enterprise Linux (RHEL) 9.2的发布&#xff0c;Alma Linux 9.2和Rocky Linux 9.2成为了RHEL 9.2的备选替代品。这两个Linux发行版旨在提供与RHEL兼容的功能和稳定性&#xff0c;以满足那些需要企业级操作系统的用户需求。本文将详细介绍Alma Linux 9.2和Rocky Lin…

nginx反向代理缓存

背景 nginx 一般用来做反向代理和负载均衡&#xff0c;将客户端请求发送到后端的 jetty&#xff0c;并将 jetty 的响应发送给客户端。后端的 jetty 通常不止一个&#xff0c;nginx 根据配置来选择其中一个 jetty&#xff0c;比较常见的选择策略是轮询。示意图如下 启动缓存支…

oracle19c介绍和安装

目录 一、版本 &#xff08;1&#xff09;历史 &#xff08;2&#xff09;11g和12c管理方式区别 11g 12C &#xff08;3&#xff09;各个版本对操作系统要求 二、分类 &#xff08;1&#xff09;分为桌面类和服务器类 &#xff08;2&#xff09;分为企业版和标准版 三…

基于遗传算法的BP神经网络优化算法(matlab实现)

1 理论基础 1.1 BP神经网络概述 BP网络是一类多层的前馈神经网络。它的名字源于在网络训练的过程中&#xff0c;调整网络的权值的算法是误差的反向传播的学习算法&#xff0c;即为BP学习算法。BP算法是Rumelhart等人在1986年提出来的。由于它的结构简单&#xff0c;可调整的…

个人网站实现微信扫码登录

⭐个人网站实现微信扫码登录 &#x1f948;效果图 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kzSrNgiv-1685034480658)(https://img.ggball.top/picGo/动画.gif)] &#x1f4d7;开发背景 为什么想用微信扫码登录呢&#xff1f; 起因是自己开发…

【CH32】| 02——常用外设 | GPIO

系列文章目录 【CH32】| 00——开发环境搭建 【CH32】| 01——新建工程 | 下载 | 运行 |调试 【CH32】| 02——常用外设 | GPIO 失败了也挺可爱&#xff0c;成功了就超帅。 文章目录 前言1. GPIO简介2. IO口的内部结构框图保护二极管上下拉电阻施密特触发器两个MOS管输出寄存器…

chatgpt赋能python:Python加速循环的执行方法详解

Python 加速循环的执行方法详解 Python是一门非常流行的编程语言&#xff0c;它可以在很多领域应用&#xff0c;比如Web开发、数据分析、机器学习等等。然而&#xff0c;Python执行速度较慢&#xff0c;特别是在循环语句中&#xff0c;代码执行效率会大打折扣。在本文中&#…

【基于ROS Melodic环境安装rosserial arduino】

【基于ROS Melodic环境安装rosserial arduino】 1. 简介2. 安装2.1 Ubuntu下的Arduino IDE安装2.2 Ubuntu下rosserial arduino软件安装2.3 安装ros_lib到Arduino IDE开发环境 3. 将ros_lib配置到 Arduino 环境库中4. 使用helloword5. 实验验证6.总结 1. 简介 这个教程展示如何…

Linux系统初始化命令的备忘单,Linux运维工程师收藏!

在管理和维护Linux系统时&#xff0c;有一些常用的命令可以帮助您进行系统初始化和配置。这些命令涵盖了各种任务&#xff0c;包括系统设置、用户管理、软件安装和网络配置等。 本文将为您提供一个Linux系统初始化命令的备忘单&#xff0c;以便在需要时方便查阅和使用。 系统设…

chatgpt赋能python:Python动画制作指南:从入门到精通

Python动画制作指南&#xff1a;从入门到精通 Python作为一种易学易用的编程语言&#xff0c;在数据分析、机器学习等领域已经得到广泛应用。但是你知道吗&#xff1f;Python还可以用来制作动画&#xff01;本文将为你介绍如何用Python制作动画&#xff0c;从入门到精通&#…

chatgpt赋能python:Python写计算器:从入门到精通

Python写计算器&#xff1a;从入门到精通 简介 计算器无疑是计算机编程中最基本且实用的工具之一。Python 作为一门易于学习且功能强大的编程语言&#xff0c;能够轻松实现计算器的功能。在本文中&#xff0c;我们将介绍如何使用 Python 编写一个简单的计算器。 如何实现&am…

chatgpt赋能python:Python的封装:提高代码的可维护性和可复用性

Python的封装&#xff1a;提高代码的可维护性和可复用性 在软件开发领域中&#xff0c;封装是一种重要的概念。它被用于隐藏程序的实现细节&#xff0c;使得程序的功能变得更加易于使用和维护。在Python编程中&#xff0c;封装是一种被广泛使用的技术&#xff0c;可以帮助你提…

Linux 信号知识点总结

对于 Linux来说&#xff0c;实际信号是软中断&#xff0c;许多重要的程序都需要处理信号。信号&#xff0c;为 Linux提供了一种处理异步事件的方法。比如&#xff0c;终端用户输入了 ctrlc 来中断程序&#xff0c;会通过信号机制停止一个程序。信号概述 1.信号的名字和编号: 每…