C++基础之预处理

C/C++编程笔记:C语言预处理器(CPP)与预处理命令 C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。 我们将把 C 预处理器(C Preprocessor)简写为 CPP。 所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令: #define 定义宏 #include ... 阅读详情

C++基础之预处理

预处理器的主要作用就是: 把通过预处理的内建功能对一个资源进行等价替换,最常见的预处理有: 文件包含,条件编译、布局控制和宏替换4种。
常见的预处理命令:
#define 宏定义
#undef 取消宏
#include 文本包含
#ifdef 如果宏被定义就进行编译
#ifndef 如果宏未被定义就进行编译
#endif 结束编译块的控制
#if 表达式非零就对代码进行编译
#else 作为其他预处理的剩余选项进行编译
#elif 这是一种#else和#if的组合选项
#line 改变当前的行数和文件名称
#error 输出一个错误信息
#pragma 为编译程序提供非常规的控制流信息

文件包含

#include 是一种最为常见的预处理,主要是做为文件的引用组合源程序正文。
使用#include命令时,如果包含的是C++系统头文件,则用一对尖括号将文件名包含起来,例如 #include<"iostream>。如果包含的是用户自己定义的头文件,则用一对双引号将文件名包含起来。目的是先搜索当前目录,再搜索系统目录。例如,#include<"io.h>是系统的io流,#include“io.h”是用户自定义的io流。

补充:#include<iostream>与#include<iostream.h>的区别
1.iostream.h只支持窄字符集,iostream则支持窄/宽字符集。
2.标准对iostream作了很多的改动,接口和实现都有了变化。
3.iostream组件全部放入namespace std中,防止了名字污染。
因此我们建议使用#include<'iostream>

宏替换

#define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。
该命令有两种格式:一种是简单的宏定义,另外一种是带参数的宏定义。
(1)简单的宏定义。 #define 宏名 字符串
例如:#define pi 3.1415926
(2) 带参数的宏定义  #define <宏名>(<参数表>) <宏体>
  其中, <宏名>是一个标识符,<参数表>中的参数可以是一个,也可以是多个,视具体情况而定,当有多个参数的时候,每个参数之间用逗号分隔。<宏体>是被替换用的字符串,宏体中的字符串是由参数表中的各个参数组成的表达式。
  例如: #define SUB(a,b) a-b
  若程序中出现SUB(2,3) 则可以替换为2-3
若在程序中出现SUB(x-1,y+1)则可以替换为 x-1-y+1

补充:带参数的宏定义与带参数的函数的区别
  (1) 在带参的宏定义中,形式参数不是变量,只是一个符号,不分配内存单元,不必定义其形式参数的类型。发生宏"调用"时,只是将实参替换形参。而在函数中,形参和实参是完全独立的变量,它们均有自己的作用域。当调用发生时,实参传递给形参的过程(值传递或引用传递)完全不同于简单的宏替换。
  (2) 在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
宏替换发生的时机

在C++实际通过了预处理、编译、汇编和链接几个过程。其中预处理器产生编译器的输出,它实现如下的功能:
(1) 文件包含
能够把源程序中的#include 扩展为文件正文,即把包含的.h文件找到并展开到#include 所在处。
(2)条件编译
预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外,一般把排除在外的语句转换成空行。
(3)宏展开
预处理器将源程序文件中出现的对宏的引用展开成相应的宏定义,即本文所说的#define的功能,由预处理器来完成。
通过预处理器处理的源程序与以前的源程序有全部不一样, 在这个阶段所进行的工做只是纯粹的替换与展开,没有任何计算功能,因此在学习#define命令时只要能真正理解这一点,这样才不会对此命令引发误解并误用。
通过以上分析可以知道宏替换是发生在预处理阶段,并且替换只是简单的字符串替换

宏使用过程中的问题解析

1.在简单宏定义的使用中,当替换文本所表示的字符串为一个表达式时,容易引发误解和误用。以下例:

#define N 3+3
void main()
{
   int a=N*N;
   printf(“%d”,a);
}

(1)出现问题
在此程序中存在着宏定义命令,宏N表明的字符串是3+3,在程序中有对宏N的使用,通常同窗在读该程序时,容易产生的问 题是先求解N为3+3=6,而后在程序中计算a时使用乘法,即NN=66=36, 其实该题的结果为15,为何结果有这么大的误差?

(2)问题解析
宏展开是在预处理阶段完成的,这个阶段把替换文本只是看做一个字符串,并不会有任何的计算发生,在展开时是在宏N出现的地方 只是简单地使用串3+3来代替N,并不会增添任何的符号,因此对该程序展开后的结果是a=3+3*3+3,计算后=16,这就是宏替换的实质。

(3)解决办法
/将宏定义写成以下形式/
#define N (2+2)
/这样就可替换成(2+2)(2+2)=16*/

  1. 带参数的宏定义出现的问题
    在带参数的宏定义的使用中,极易引发误解。例如咱们须要作个宏替换能求任何数的平方,这就须要使用参数,以便在程序中用实际参数来替换宏定义中的参数。通常容易写成以下形式:
#define area(x) x*x
/*这在使用中是很容易出现问题的,看以下的程序*/
void main()
{
   int y = area(2+2);
   printf(“%d”,y);
}
按理说给的参数是2+2,所得的结果应该为4*4=16,可是错了,由于该程序的实际结果为8,仍然是没能遵循纯粹的简单替换的规则,又是先计算再替换 了, 在这道程序里,2+2即为area宏中的参数,应该由它来替换宏定义中的x,即替换成2+2*2+2=8了。那若是遵循(1)中的解决办法,把2+2 括起来,即把宏体中的x括起来,是否能够呢?#define area(x) (x)*(x),对于area(2+2),替换为(2+2)*(2+2)=16,能够解决, 可是对于area(2+2)/area(2+2)又会怎么样呢,有的学生一看到这道题立刻给出结果,由于分子分母同样,又错了,仍是忘了遵循先替换再计算的规则了,这道题替换后会变为 (2+2)*(2+2)/(2+2)*(2+2)即4*4/4*4按照乘除运算规则,结果为16/4*4=4*4=16,那应该怎么呢?解决方法是在整个宏体上再加一个括号,即 #define   area(x) ((x)*(x)), 不要以为这不必,没有它,是不行的。
要想可以真正使用好宏定义,那么在读别人的程序时, 必定要记住先将程序中对宏的使用所有替换成它所表明的字符串,不要自做主张地添加任何其余符号,彻底展开后再进行相应的计算,就不会写错运行结果。
若是是本身编程使用宏替换,则在使用简单宏定义时,当字符串中不仅一个符号时,加上括号表现出优先级,若是是带参数的宏定义,则要给宏体中的每一个参数加上括号,并在整个宏体上再加一个括号。看到这里,不由要问,用宏定义这么麻烦,这么容易出错,可不能够摒弃它, 那让咱们来看一下在C语言中用宏定义的好处吧。

如:

#include <iostream.h>
#define product(x)    x*x
int main()
{
    int i=3;
    int j,k;
    j = product(i++);
    cout<<"j="<<j<<endl;
    cout<<"i="<<i<<endl;
    k = product(++i);
    cout<<"k="<<k<<endl;
    cout<<"i="<<i<<endl;
    return 0;
}
依次输出结果:
j=9;i=5;k=49;i=7

条件编译

通常,源文件中所有内容都要参加编译,但是在某些时候,可能希望源文件中某些部分在满足某些条件的情况下才进行编译,这就是所谓的“条件编译”。
条件编译有以下三种方式。
1:
#ifdef 标识符
程序片段1
#else
程序片段2
#endif
上述语句的意思就是如果标识符已被#define命令定义过,则对程序片段1进行编译;否则
对程序片段2进行编译。其中,程序片段2可以为一个空的程序段,那么#else就没有什么
实际的意义了,这时可以将其省略。下面看这段示例代码。

#include <iostream>
#define DEBUG
using namespace std;
void main()
{
     #ifdef DEBUG 
         cout << "Begin..." << endl;
     #else
         cout << "Cannot Begin!" << endl;
     #endif
     int array[10];
     for(int i = 0;i < 10;i++) {
       array[i] = i;
       #ifdef DEBUG 
          cerr << "i = " << i << endl;
          cerr << "array[i] = " << array[i] << endl;
       #endif
    }
}

2:
#ifndef 标识符
程序片段1
#else
程序片段2
#endif
形式2和形式1的区别在于#ifdef关键字换成了#ifndef关键字,其功能是如果标识符未被#define命令定义过,则对程序片段1进行编译;否则对程序片段2进行编译,这与形式1的功能正好相反。
3:
#if 表达式
程序片段1
#else
程序片段2
#endif
该形式完成的功能是如果表达式的值为真,则对程序片段1进行编译;否则对程序片段2进行编译。因此可以使程序在不同条件下完成不同的功能。

条件编译还常用于防止包含于头文件中的类或者函数被被重复定义多次。在一个程序中
多次包含某个头文件会导致类和函数的重定义问题。可以通过使用条件编译来防止该问
题的发生。下面看这段代码。

#ifndef FA_H
#define FA_H
// ... some declaration codes
#endif // #ifndef FA_H

布局控制

#progma,这也是我们应用预处理的一个重要方面,主要功能是为编译程序提供非常规的控制流信息。
#pragma指令的作用是:用于指定计算机或操作系统特定的编译器功能。C 和 C++ 的每个实现均支持某些对其主机或操作系统唯一的功能。 例如,某些程序必须对将数据放入的内存区域进行准确的控制或控制某些函数接收参数的方式。 在保留与 C 和 C++ 语言的总体兼容性的同时,#pragma 指令使每个编译器均能够提供特定于计算机和操作系统的功能。

根据定义,#pragma指令是计算机或操作系统特定的,并且通常对于每个编译器而言都有所不同。 #pragma指令可用于条件语句以提供新的预处理器功能,或为编译器提供实现所定义的信息。

语法是:

#pragma  token-string
__pragma( token-string )	   //__pragma 关键字是特定于 Microsoft 编译器的

token-string 是一系列字符,这些字符提供了特定的编译器指令和参数(如果有)。 数字符号"#" 必须是位于包含#pragma指令行上的第一个非空白字符;空白字符可以分隔数字符号和词“pragma”。 在 #pragma 之后,编译转换器可分析为预处理标记的所有文本。 #pragma 的参数受宏展开的约束,如果编译器发现它无法识别的杂注,则它会发出警告并继续编译。
#pragma的具体内容请详见《#pragma用法解析》。
###其他预编译指令
1.#line
#line的语法如下:

#line number filename
例如:#line 30 a.h 其中,文件名a.h可以省略不写。

这条指令可以改变当前的行号和文件名,例如上面的这条预处理指令就可以改变当前的行号为30,文件名是a.h。初看起来似乎没有什么用,不过,他还是有点用的,那就是用在编译器的编写中,我们知道编译器对C++源码编译过程中会产生一些中间文件,通过这条指令,可以保证文件名是固定的,不会被这些中间文件代替,有利于进行分析。
2.#error
#error语法如下:

#error info
例如:

#ifndef UNIX
#error This software requires the UNIX OS.
#endif

这条指令主要是给出错误信息,上面的这个例子就是,如果没有在UNIX环境下,就会输出This software requires the UNIX OS.然后诱发编译器终止。所以总的来说,这条指令的目的就是在程序崩溃之前能够给出一定的信息。

【C/C++基础】08_预处理命令  在前面各章中,已多次使用过以“#”号开头的预处理命令。如包含命令#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称为预处理部分。   所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分... 阅读详情

相关推荐

【016】C++ 预处理:内存分区、变量存储、头文件、宏,你都掌握了吗?

C++预处理器是一个非常重要的编译阶段,它在源代码被编译之前对源代码进行一些操作。#define:定义宏常量或宏函数,可以提高代码的可读性和可维护性。:条件编译指令,用于根据条件选择是否编译某段代码。#include:包含头文件,将其他源代码文件中的定义导入到当前文件中以供使用。#pragma:通用预处理命令,用于指示编译器执行特定的动作或更改默认行为。#error:产生错误消息并停止编译过程。#undef:取消已定义的宏。除了上述常见预处理指令外,C++还有一些其他的预处理功能,例如。

Lion_Long的博客 1410

C++ 预处理详解

C++ 预处理详解

qq_61553520的博客 1462

【C++基础】面试高频考点解析:预处理指令实战与真题深度剖析

预处理是C/C++编译过程中最容易被忽视却又至关重要的环节,也是大厂面试的必考内容。本文结合腾讯、阿里、Google等企业历年真题,深入解析预处理核心考点,助你轻松应对校招与社招!

byte轻骑兵的技术小窝 2663

C++ 预处理指令介绍

C++ 预处理指令,指示编译器在实际编译之前所需完成的预处理。 所有的预处理指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。 C++支持很多预处理指令,比如 #include、#define、#if、#else、#line 等。

cnds123的专栏 4177

一文搞懂STM32定时器翻转模式(产生多路频率、占空比的PWM)

(产生多路频率、占空比的PWM)

weixin_56688599的博客 7936

【Hadoop】开发插件安装

前言 Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是Eclipse 附带了一个标准的插件集,包括 Java 开发工具(Java Development Tools,JDT)。 Eclipse的插件机制是轻型软件组件化架构。在客户机平台上,Eclipse使用插件来提供所有的附加功能,例如支持Java...

weixin_44039347的博客 3209

C/C++预处理

C/C++预处理

qq_64109690的博客 1765

深度学习模型轻量化方法之【量化】

量化虽然有着硬件友好和功效提升的特点,但它也引入了模型性能的折衷。因此,设计有效的量化策略需要在模型大小、性能以及运算速度之间做出平衡。随着深度学习算法和硬件加速技术的快速发展,量化方法在使深度网络轻量化和提高部署效率方面持续发挥着重要作用。

m0_73916791的博客 2847

C++基础之预处理命令

目录 概述——对预处理命令的理解 预处理命令的处理时间 C++中主要的预处理命令 宏定义 “文件包含”处理 选择编译 概述 使用C++的预处理命令可以提高程序的可维护性,有利于程序员以更加方便和快捷的方式进行项目开发,除此之外,还使用预处理命令还可以提高程序的编译和执行效率 预处理命令的处理时间 在编译系统执行编译之前 ...

anyuanpang5060的博客 174

ROS2实战:手把手教你用CMake集成自定义DDS(以CycloneDDS为例)

本文详细介绍了如何在ROS2中通过CMake集成自定义DDS(以CycloneDDS为例),涵盖环境准备、源码编译、ROS2工作区集成、性能调优及常见问题排查。通过RMW中间件抽象层,开发者可以灵活切换DDS实现,提升系统实时性和可靠性。

weixin_29284201的博客 347

5分钟搞定!用Cursor和.NET 9.0快速搭建串口通讯上位机(附VS Code插件配置)

本文分享了如何利用Cursor AI编程助手与.NET 9.0,在5分钟内快速构建一个功能完整的串口通讯上位机程序。通过自然语言描述需求,AI自动生成WinForms界面和核心代码,极大提升了硬件调试工具的定制开发效率。文章详细介绍了环境配置、AI协作编码、功能实现与调试的全过程。

weixin_28366353的博客 277

c++ 基础——预处理、编译、链接

c++支持四种编程范式:面向过程、面向对象、模板、函数式编程 #include <iostream> using namespace std; int add1(int a,int b){ return a+b; } class ADD{ public: int operator()(int a,int b){ return a+b; } }add2; template<typename T,typename U> auto add3(

超级码力 408

时间序列学习(4):平稳性检验(单位根检验、ADF检验)

时间序列学习(4):平稳性检验(单位根检验、ADF检验)1、单位根检验2、ADF检验3、指数走势的检验4、对数收益率序列检验 相关图可以大致判断序列是否平稳。但是,这毕竟不是严格的。 这篇笔记来就谈一谈平稳性的检验。 到目前为止,我们有了以下的时间序列模型: 白噪声; 随机游走; AR模型; MA模型; ARMA模型。 我们知道白噪声、MA模型一定是平稳的(这里的平稳都是弱平稳);随机游走一定是不平稳的;ARMA模型取决于其AR部分。 所以唯一需要做平稳性检验的就是AR模型。 1、单位根检验 先来看一阶

lucialucia的博客 7万+

C++基础之预处理和语句

(1)C++语言源程序中可以使用一些预处理中的编译命令,这些命令在程序被正常编译之前执行,被称为预处理命令,这些命令所实现的功能被称为预处理功能(2)常用的预处理命令有文件包含命令、宏定义命令和条件编译命令(3)预处理命令都以#开头,每条命令独占一行,不用分号结束,命令太长可用续行符(’\’)写成多行。(4)文件包含命令格式如下#include<文件名>或者#include "文件名"...

weixin_33768481的博客 186

汽车电缆选型实战:基于ISO 19642-3~12的7类电缆电压/材料/场景对照表

本文详细解析了基于ISO 19642-3~12标准的7类汽车电缆选型实战,涵盖电压等级、导体材料和场景应用的对照表。通过全参数速查表和典型选型陷阱分析,帮助工程师快速掌握从低压信号线到高压动力电缆的选型逻辑,提升新能源汽车与智能驾驶技术的安全性和可靠性。

weixin_29043895的博客 366

RTCA DO-160G标准详解:航空电子设备的全面环境适应性测试指南

第二章的主要目的是提供标准中使用的术语和定义。了解这些术语对于准确理解标准的要求至关重要,特别是在技术实施过程中。RTCA DO-160G 涉及多个学科的知识,包括航空、电子、电磁学和机械工程,因此需要确保使用的术语在所有学科中具有一致的定义。为了确保测试人员和工程师在执行测试时对所有条件和要求的理解一致,RTCA DO-160G 标准中定义了大量术语。环境应力:指的是设备在操作过程中可能遇到的外部物理或化学条件,包括温度、湿度、振动等。电磁干扰(EMI)

qq_41854911的博客 5277

C/C++ 基础笔记(十)预处理与类的基础

本文系统讲解了C++预处理指令与面向对象编程核心概念。预处理部分包括宏定义(无参/带参)、文件包含和条件编译的使用方法与注意事项。面向对象部分重点阐述类与对象定义、访问权限控制(public/protected/private)、成员函数实现(内联/外部)、this指针作用、const成员特性,以及头文件与源文件分离管理的最佳实践。通过对比宏与函数、结构体与类的异同,帮助开发者掌握封装特性,构建更安全高效的C++程序。全文配以代码示例,清晰展示了各语法要点的实际应用场景。

2301_80252792的博客 448

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

c++基础学习 - 预处理

C++的预处理(Preprocess),是指在C++程序源代码被编译之前,由预处理器(Preprocessor)对C++程序源代码进行的处理。这个过程并不对程序的源代码进行解析,但它把源代分割或处理成为特定的符号用来支持宏调调用。 1. #include 在程序中包含头文件 头文件通常以.h结尾,其内容可使用#include预处理器指令包含到程序中,头文件中一般包含: 函数原型与全

小何才露尖尖角 916

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

C++基础之常用预处理指令

  预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。预处理命令以符号“#”开头。   常用的预处理指令包括: 宏定义:#define 文件包含:#include 条件编译:#if、#elif、#ifndef、#ifdef、#endif、#undef 错误信息指令:#error #line指令 布局控制:#pragma 宏定义   宏定义又称为宏代换、宏...

CG-SpatialAI 368

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

C++基础理解 - 预处理指令

在编译之前,由编译器执行,预处理结果作为编译器的输入。 主要功能:把其他文件包含到当前文件、定义符号常量和宏、程序代码的条件编译以及预处理指令的条件执行。 预处理指令包括:#include, #define, #error, #if, #else, #elif, #endif, #ifdef, #ifndef, #undef, #line, #pragma 1、#include指令 作用:指示编译器将该指令所指出的另一个源文件嵌入该指令所在的程序中。 文件名使用尖括号:在指定目录中寻找被包括的文件 文件名使

u010125417的博客 598

C++编程基础(九):预处理指令

C++预处理是在编译前进行的文本替换处理,主要包括文件包含、宏定义和条件编译三大功能。文件包含通过#include指令将头文件内容插入源文件,常用#ifndef或#pragma once防止重复包含。宏定义使用#define进行文本替换,分为无参宏和带参宏,需注意加括号避免运算错误。条件编译通过#if/#ifdef等指令实现代码选择性编译,常用于跨平台和调试场景。预处理器还提供__FILE__、__LINE__等内置宏用于调试。现代C++推荐用constexpr和inline替代部分宏功能以提高安全性。

2301_80115847的博客 612

C++——基础知识(预处理命令、C++与C语言的区别、类和对象、函数重载、引用)

本文主要介绍C++——基础知识(预处理命令、C++与C语言的区别、类和对象、函数重载、引用)。以及一些例子帮助更好地理解。

x20020402的博客 376

c++高频知识点总结 第 1 章:语言基础与预处理

🧠 原理基础]链接错误:当链接器遍历符号表(Symbol Table)时,发现一个符号(Symbol)被引用(UND 标记),但在所有的目标文件和库中都找不到其定义(T/D 标记),就会报错。静态链接:在链接期,将静态库(.a.lib,本质是.o的归档)中的代码段复制到最终的可执行文件中。动态链接:在链接期,仅记录动态库(.so.dll)的符号表信息和重定位表,不复制具体代码。程序运行时由动态链接器(loader)加载库并修正地址。[🗣️ 面试回答模板]错误信息:通常报区别静态链接:代码复制。

m0_37755875的博客 1134

C++基础了解-32-C++ 预处理器

C++ 预处理器

Xiaowu2048的博客 172
上一篇: 你不知道的常规流
下一篇: C++基础之cin
♚陌路,相逢☜
博客等级 码龄8年 21粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值