当前位置: 首页 > news >正文

c语言预处理命令

预处理指令是#号开头的代码行,# 号必须是该行除了任何空白字符外的第一个字符。# 后是指令关键字,在关键字和 # 号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换

部分预处理指令:

指令说明
#空指令,无任何效果
#include包含一个源代码文件
#define定义宏
#undef取消已定义的宏
#if如果给定条件为真,则编译下面代码
#ifdef如果宏已经定义,则编译下面代码
#ifndef如果宏没有定义,则编译下面代码
#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个#if……#else条件编译块

预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的,程序员在程序中用预处理命令来调用这些功能。

使用预处理功能便于程序的修改、阅读、移植和调试,也便于实现模块化程序设计。

宏定义(#define)

宏定义可以带有参数,宏调用时是以实参代换形参,而不是“值传送”。
为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两边也应加括号

#define非常方便,所以我们当然都喜欢用它。define常用来定义常量和字符串常量。例如,#define PI 3.1415926

在后续的代码中,你都可以用PI来代替这一串数字了。而且就算我要求PI的精度再提高一些那也没有关系,我们只要在define处修改一下就可以了。若是没有使用宏定义,你就要在代码中处处寻找3.1415926然后去修改,是不是感觉痛苦不堪呢?

虽然它可以给我们带来许多便利,但是在使用它的时候一定要小心再小心,它也经常会在你不注意的时候让你犯下错误。例如下面这段代码。

#define PCHAR char*

PCHAR p1,p2;

这段代码有什么问题呢?编译并不会出错,而且p1确实是char*类型。但是!

大家可以看到文中代码,我对p1和p2分别取地址(不要在意是否是野指针,只是为了演示类型)

照理来说指针再取地址就要用char**类型的变量来接收,所以char**a = &p1这句代码并无问题。

但是char**b = &p2提示了错误:char*类型的值不能用char**来接受。也就是说我们的p2它竟然是一个char类型,而并不是char*类型变量?

其实这个地方替换完之后是char *p1,p2,*是跟着p1的,所以p2是char类型。

所以像这种没有办法一下就发现的错误才是最恐怖的错误。像这种情况我还是建议大家一个个的定义变量。实在不行可以使用typedef,不会出现这种问题。

我们还可以用define定义字符串,一般用于路径:

#define FILE_PATH E:\folder1\1.txt

撤销宏定义(#undef)

这个预处理命令也就是undefine的意思,即撤销宏定义。也就是说宏定义的生命周期从#define开始到#undef结束。

包含(#include)

文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件

这是我们最常见的词语了。在编译一个程序的时候,首先第一句话就是#include <stdio.h>啦。它同样也非常重要,是将多个源文件连接成一个源文件进行编译,结果就生成一个目标文件(obj)。常见有两种形式:

1、include <xxx.h>

用尖括号括起来的头文件一般都是系统自带的,表示系统将在指定的路径进行寻找

2、include "xxx.h"

双引号一般则用于我们自己编写的头文件,系统也会优先在当前目录中查找。如果找不到指定文件名的文件就会和形式1一样在指定的路径进行寻找。

条件编译

条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。

平常写代码过程中,我们为了实现分支结构会经常使用if else结构,在预处理同样也有类似的功能,即条件编译。我们可以按照不同的条件去编译不同的部分,这对程序的移植和调试有着巨大的帮助。条件编译主要有以下两种形式。

#ifdef 标识符1  &&  #ifndef 标识符2
//code1
#else
//code2
#endif

这一段就是经典的条件编译。如果(ifdef)定义了标识符1 并且 如果没有定义(ifndef)标识符2,执行代码段code1;否则执行code2.

要注意的是#ifdef或#ifndef需要和#endif对应

#if 常量表达式
//code1
#else
//code2
#endif

这段则无限接近我们日常使用的if else了。同样要注意endif。

除此之外还有一个#elif,即是elseif,形成if else_if 阶梯状语句,可以进行多种编译选择。

#error预处理

它的作用人如其名,是用来提示错误的,编译程序时如果遇到#error就会生成一个编译错误提示信息并停止编译。关于提示的错误信息都是系统定义好的。

#pragma注释,编译指示

#pragma可能是所有预处理指令中最复杂的那个了,因为它可以跟很多参数,而这些参数实现的功能也都大相径庭。当然了,越复杂功能也就越强大,这里挑几个常见的参数进行讲解。

1.#pragma once

头文件的最开始处加上这句话,就可以避免头文件的重复引用(include)。它的作用就是保证每个头文件只编译一次,再加入同名的头文件也没有关系(反正也不编译,且不会报错)。

2.#pragma message

在我们编写程序时,有时一旦定义了许多宏则有可能忘记了某个关键的宏是否正确的设置了。这时只需要使用这条指令就可以一目了然。

具体用法:

编译时的控制台将显示引号中的内容。

  3.#pragma hdrstop(header stop

当出现这条语句代表预编译头文件到此为止,后面的将不再参与预编译。通常这样做用来指定编译优先级,同时也可以加快链接的速度(一次编译太多头文件可能会占很多磁盘空间)。

4.#pragma warning

此指令用于和warning有关的操作(即非致命编程错误的警告)。

具体示例:

#pragma warning (disable:4707)     //屏蔽4707警告

#pragma warning (once:4706)         //只显示一次4706警告

#pragma warning (error:164)           //将164号警告当作一个错误。

也可以三合一写成:#pragma warning (disable:4707;once:4706;error:164)

5.#pragma comment(备注,注释)

该指令用于导入库。

例如:#pragma comment (lib,"user32.lib")

将user32.lib 库文件导入本工程中。

6.#pragma pack

本知识涉及到内存对齐

相关文章:

  • C++头文件和std命名空间
  • C++输入输出(cin和cout)
  • C++运算符重载的概念和语法
  • GetLastError,ErrLookUp,$err,hr,$eax
  • 字符集和字符编码(Charset Encoding)
  • 进程
  • Windows中的对象
  • Windows对象、句柄与MFC对象
  • C++结构体
  • C++ 指针运算符( 和 *)
  • C++中 引用与取地址的区别
  • C/C++语言中函数参数传递的三种方式(x,*x,x)
  • VC 和 VS 区别
  • 什么是Windows API
  • c++类与对象用多文件编写以及vs的操作步骤
  • .pyc 想到的一些问题
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • Akka系列(七):Actor持久化之Akka persistence
  • Angularjs之国际化
  • Java的Interrupt与线程中断
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • 编写高质量JavaScript代码之并发
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 日剧·日综资源集合(建议收藏)
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 世界上最简单的无等待算法(getAndIncrement)
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 用element的upload组件实现多图片上传和压缩
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (SpringBoot)第七章:SpringBoot日志文件
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (笔试题)合法字符串
  • (多级缓存)缓存同步
  • (剑指Offer)面试题34:丑数
  • (算法设计与分析)第一章算法概述-习题
  • (五)IO流之ByteArrayInput/OutputStream
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)我也是一只IT小小鸟
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .net core 依赖注入的基本用发
  • .NET 的程序集加载上下文
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?
  • .Net6 Api Swagger配置
  • @WebServiceClient注解,wsdlLocation 可配置
  • [20150629]简单的加密连接.txt
  • [2019.3.20]BZOJ4573 [Zjoi2016]大森林
  • [C#]C#学习笔记-CIL和动态程序集
  • [C]整形提升(转载)
  • [elastic 8.x]java客户端连接elasticsearch与操作索引与文档
  • [gdc19]《战神4》中的全局光照技术
  • [JavaScript]_[初级]_[关于forof或者for...of循环语句的用法]