C-预处理

news2025/1/22 19:14:02

文章目录

  • 预处理
    • 宏定义
          • 数值宏常量
          • 字符串宏常量
          • 使用宏充当注释
            • 去注释和宏替换谁先进行
          • 用define宏定义表达式
            • 在if判断语句之后多条语句没有{}导致出错
          • 定义不能带空格
        • #undef
    • 条件编译
            • `#ifdef` `ifndef`:判定的是宏是否被定义
            • `#if-#endif`:宏定义是否为真
            • 宏定义进行多条件筛选
            • `#ifdef <==>#if defined() #ifndef <==>#if !defined()`
            • 条件编译支持嵌套
            • 多条件检测宏是否被定义
          • 为什么要有条件编译
    • 文件包含
            • 什么叫头文件展开?
            • 重复包含是错误吗?
    • #error
    • #line
    • #pragma
    • #运算符
    • ##运算符

预处理

宏定义

数值宏常量

define PI 3.14

字符串宏常量

define PATH "C:\\Users\\yuanwei\\Desktop"

结论:宏定义代表字符串的时候,一定要带上双引号,可以用\续行 ,双\\避免转义字符的存在.

使用宏充当注释
//程序翻译过程:
//预处理-E :头文件展开,去注释,宏替换->.i
//编译-S : 将干净的C语言,编译成为汇编语言->.s
//汇编-c :将汇编翻译成为目标二进制文件->.o
//链接 :将目标二进制文件与相关库链接,形成可执行程序  
去注释和宏替换谁先进行
  • 先进行去注释然后宏替换.

image-20230426125142405

用define宏定义表达式

image-20230426161457494

在if判断语句之后多条语句没有{}导致出错
  • 简单宏替换出现编译报错

image-20230426162522576

  • 宏替换多条语句封装进do{}while(0)句式之中(先执行再判断,执行一次就行)
#include<stdio.h>

#define INIT_VAL(a,b) do{a=0;b=1;}while(0)
int main()
{
  int x=10;
  int y=20;
  int flag=0;
  scanf("%d",&flag);
  if(flag)
    INIT_VAL(x,y);
  else
  {
    x=100;
    y=200;
  }
  printf("%d-%d\n",x,y);
  return 0;
}

定义不能带空格
#include <stdio.h>
#define INC(a) ((a)++) //定义不能带空格
# define  NUM 10 //define前面可以加空格(但是不建议) 
int main()
{
    int i = 0;
    INC (i); //使用可以带空格,但是严重不推荐(不要处处显得自己不一样哦)
    printf("%d\n", i);
}

#undef

宏可以在代码的任何地方定义.作用范围是从定义处往后都可.

  • 源文件的任何地方,宏都可以定义,与是否在函数内外没关系.

image-20230426171100884

  • 宏替换是在函数调用之前的.

image-20230426172805156

image-20230426171238205

after #undef,取消宏或者宏的作用范围限制结尾处.

image-20230426171536971

int main()
{ 
    #define X 3
    #define Y X*2
    #undef X
    #define X 2
    int z = Y;
    printf("%d\n", z);//4
    return 0;
}

在这里插入图片描述

条件编译

宏是否被定义 VS 宏是否为真or假

#define DEBUG
#define DEBUG 1
#define DEBUG 0
#ifdef ifndef:判定的是宏是否被定义

image-20230426174041045

#if-#endif:宏定义是否为真

image-20230427092406386

宏定义进行多条件筛选
	#include<stdio.h>
  2 int main()
  3 {
  4 #if VERSION==1
  5   	printf("hello VERSION==1\n");
  6 #elif VERSION==2
  7   	printf("hello VERSION==2\n");
  8 #elif VERSION==3
  9   	printf("hello VERSION==3\n");
 10 #else
 11   	printf("hello default version\n");
 12 #endif                                                       
 13   	return 0;
 14 }

image-20230427093009303

#ifdef <==>#if defined() #ifndef <==>#if !defined()
#include <stdio.h>
#define DEBUG
int main()
{ 
#if defined(DEBUG)
	printf("hello debug\n");
#else
	printf("hello release\n");
#endif
	return 0;
} 
//演示#if模拟#ifndef
#include <stdio.h>
//#define DEBUG
int main()
{ 
#if !defined(DEBUG)
	printf("hello debug\n");
#else
	printf("hello release\n");
#endif
	return 0;
}
//code1
#include <stdio.h>
#define C
#define CPP
int main()
{ 
#if defined(C) && defined(CPP)
//#if (defined(C) && defined(CPP))
	printf("hello c&&cpp\n");
#else
	printf("hello other\n");
#endif
	return 0;
} 
//Code 2
#include <stdio.h>
#define C
#define CPP
int main()
{ 
#if defined(C) || defined(CPP)
//#if (defined(C) || defined(CPP))
//#if !(defined(C) && defined(CPP))
	printf("hello c&&cpp\n");
#else
	printf("hello other\n");
#endif
	return 0;
}
条件编译支持嵌套
int main()
{ 
#if defined(C)
	#if defined (CPP)
		printf("hello CPP\n");
	#endif
	printf("hello C\n");
#else
	printf("hello other\n");
#endif
	return 0;
}
多条件检测宏是否被定义
#include <stdio.h>
//#define C
//#define CPP
int main()
{ 
#if defined(C)
	printf("hello C\n");
#elif defined (CPP)
	printf("hello CPP\n");
#else
	printf("hello other\n");
#endif
	return 0;
}
为什么要有条件编译
  • 通过裁剪代码,快速实现某种目的(版本维护),功能裁剪,跨平台性.

本质认识:条件编译,其实就是编译器根据实际情况,对代码进行裁剪。而这里“实际情况”,取决于运行平台,代码本身的业务逻辑等。可以认为有两个好处:

  1. 可以只保留当前最需要的代码逻辑,其他去掉。可以减少生成的代码大小
  2. 可以写出跨平台的代码,让一个具体的业务,在不同平台编译的时候,可以有同样的表现

举一个例子吧
我们经常听说过,某某版代码是完全版/精简版,某某版代码是商用版/校园版,某某软件是基础版/扩展版等。其实这些软件在公司内部都是项目,而项目本质是有多个源文件构成的。所以,所谓的不同版本,本质其实就是功能的有无,在技术层面上,公司为了好维护,可以维护多种版本,当然,也可以使用条件编译,你想用哪个版本,就使用哪种条件进行裁剪就行。
著名的Linux内核,功能上,其实也是使用条件编译进行功能裁剪的,来满足不同平台的软件。

文件包含

#pragma once& 条件编译的方式

image-20230427175625436

第一次宏未定义,会立即定义.第二次之后因为#ifndef不再执行.

什么叫头文件展开?

将头文件的内容拷贝到目标文件可用过test.i中的内容对比发现.

如果源文件包含了很多次头文件,如果不做条件编译就会包含很多次冗余的代码.

重复包含是错误吗?

主要是编译的效率问题.那么,重复包含一定报错吗??不会!
重复包含,会引起多次拷贝,主要会影响编译效率!同时,也可能引起一些未定义错误,但是特别少。

#error

核心作用是可以进行自定义编译报错.编译时直接中止.

#include <stdio.h>
//#define __cplusplus
int main()
{ 
//#error 你好啊
#ifndef __cplusplus
#error 老铁,你用的不是C++的编译器哦
#endif
	system("pause");
	return 0;
}

image-20230427182153077

#line

本质其实是可以定制化你的文件名称和代码行号,很少使用.

image-20230427182401429

#pragma

以编译通过的方式让编译器打出信息.

//#pragma message()作用:可以用来进行对代码中特定的符号(比如其他宏定义)进行是否存在进行编译时消息提醒
#include <stdio.h>
#define M 10
int main()
{ 
#ifdef M
#pragma message("M宏已经被定义了")
#endif
	system("pause");
	return 0;
}

image-20230427182728590

#运算符

  • 将参数符号s对应的文本内容转化为字符串.
  • 相邻字符串(“”)具有自动连接特性.

image-20230427183803406

image-20230427183557718

//利用特性实现int->const char* 
#define TOSTRING(x) #x
int main()
{
	char* str[1024] = {0};
	strcpy(str,TOSTRING(123456));//是预处理阶段处理
    int a=123;
    strcpy(str,TOSTRING(a));//a
    //变量已经是运行之后才知道是啥数,所以只能打印出a
	printf("%s\n",str);
	return 0;
}

##运算符

将左侧符号和右侧符号链接起来构成一个新符号.

  • 无效符号

image-20230427184734842

  • 有效符号

image-20230427185103504

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

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

相关文章

听好程序员一句劝!零基础这样学Java才是正确的方式!

有一说一&#xff0c;Java还是现在很多新手入门学习的首选之一&#xff0c;新手学Java入门从哪里开始学&#xff1f;很多入门的小伙伴都会疑惑&#xff0c;今天好程序员给大家分享一下&#xff0c;超适合菜鸟入门的 Java学习路线 第一步&#xff0c;JavaSE&#xff0c;这是Java…

工具类——Java导出EXCEL2(设置样式、加载并填充图片、加载指定模板、大数据量设置窗口大小与刷新频率)

文章目录 一、POI设置样式二、POI导出图片1.解释XSSFClientAnchor 三、加载指定模板导出四、&#x1f449;Workbook、XSSFWorkbook与SXSSFWorkbook1.大数据量导出1&#xff09;根据数据量选择XSSFWorkbook&#xff0c;还是SXSSFWorkbook 书接上篇&#xff1a;工具类——Java 浏…

机器学习笔记之密度聚类——DBSCAN方法(Python代码实现)

机器学习笔记之密度聚类——DBSCAN方法[Python代码实现] 引言基本思想概念介绍算法过程完整算法描述 DBSCAN \text{DBSCAN} DBSCAN的优点和缺陷 2023/4/25 \text{2023/4/25} 2023/4/25补充&#xff1a;基于 Python \text{Python} Python的代码实现 引言 本节将介绍密度聚类——…

【MySQL】索引与事务

索引&#xff1a; 什么是索引&#xff1f;为什么要⽤索引 为了提高查询效率而使用的一种数据结构把数据组织起来&#xff1b; 例如生活中的字典&#xff1a;声母&#xff0c;韵母&#xff0c;声调。 创建索引&#xff1a;自动创建的主键约束&#xff0c;唯一约束&#xf…

qiankun-微前端

项目结构 主应用&#xff1a; vue3 vite 子应用1&#xff1a;vue3 vite 背景介绍 项目采用了vue3vite构建的&#xff0c;又因为qiankun不支持vite&#xff0c;所以需要引用 vite-plugin-qiankun 插件来解决 主应用–改造 安装 qiankun npm install qiankun重新定义一个…

浅浅入门SpringBoot之Thymeleaf模板

Thymeleaf是一个流行的模板引擎,该模板引擎采用Java语言开发模板引擎是一个技术名词,是跨领域跨平台的概念,在Java语言体系下有模板引擎,在C#、PHP语言体系下也有模板引擎,甚至在 Javascript中也会用到模板引擎技术,Java生态下的模板引擎有 Thymeleaf、 Freemaker、Ⅴ elocity、…

【EMQX】EMQX管理控制台即EMQX Dashboard简介

EMQX管理控制台 一、EMQX Dashboard简介二、主要功能2.1 监控和管理 EMQX 集群中的相关信息与数据2.2 访问控制&#xff08;认证与授权&#xff09;管理2.3 数据集成2.4 在线配置热更新2.5 管理系统扩展能力2.6 全面的诊断工具 三、启动3.1 首次登录3.2 忘记密码 四、配置 Dash…

我老板:你根本不懂 React!

前言 我已经使用 React 多年&#xff0c;我确信我非常了解它&#xff0c;但最近我的老板对我说&#xff0c;“你根本不知道 React&#xff0c;你对它一无所知。” 我很生他的气&#xff0c;但他指出了我程序中的三个漏洞。我现在把它记录下来&#xff0c;也分享给还不知道的小…

用好ChatGPT,毕业直接走上人生巅峰

毕业论文交上去了&#xff0c;学分也攒齐了。考研没上岸的准毕业生们接下来要面对的不是惨烈的秋招&#xff0c;就是蹲家准备二战。生活终于要对各位小可爱们动手啦&#xff01; 不想默默承受社会的毒打&#xff1f; 不愿在屡战屡败屡败屡战的压力下秃头&#xff1f; 想必各位…

API 都有这些功能,你真的都知道么?

API&#xff08;应用程序编程接口&#xff09;可以提供以下功能&#xff1a; 数据传输&#xff1a;API可以在应用程序之间传输数据&#xff0c;包括发送和获取数据、更新数据等。 访问功能: API 可以调用另一个系统或应用程序的某些功能&#xff0c;例如获取天气&#xff0c;查…

基于无线传感器网络(WSN) 查找两个节点之间的最短路径并发送数据(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 本代码基于无线传感器网络&#xff0c;在两个节点&#xff08;源节点和目标节点&#xff09;之间找到最短路径&#xff0c;并开…

Java学习过程(韩顺平661-665)

网络通信是指在计算机网络中&#xff0c;通过网络连接的设备之间进行数据交换的过程。网络通信可以分为两种类型&#xff1a;有线网络通信和无线网络通信。 有线网络通信主要通过物理介质&#xff08;如光纤、双绞线等&#xff09;来传输数据&#xff0c;其中最常用的协议是以…

数字孪生新能源智慧充电桩Web3D可视化运维系统

放眼全球&#xff0c;近十年来&#xff0c;新能源汽车赛道堪称“热得发烫”。伴随着进入成年期的新能源汽车行业对相关配套设备支撑水平的提升&#xff0c;作为其“新基建”的充电桩领域表现更为突出的价值势能。过去&#xff0c;在一系列补贴政策和资本刺激下&#xff0c;充电…

七种MOS管栅极驱动电路

01 直接驱动 首先说一下电源IC直接驱动&#xff0c;下图是我们最常用的直接驱动方式&#xff0c;在这类方式中&#xff0c;我们由于驱动电路未做过多处理&#xff0c;因此我们进行PCB LAYOUT时要尽量进行优化。如缩短IC至MOSFET的栅极走线长度&#xff0c;增加走线宽度&#x…

《商用密码应用与安全性评估》第三章商用密码标准与产品应用3.4商用密码标准与产品

一、智能IC卡标准与产品 智能IC卡的分类 存储器卡&#xff1a;内部一般不包含密码安全机制&#xff0c;不具备信息处理能力&#xff0c;外部可对片内信息任意存取&#xff0c;因此存储器卡一般用于存放不需要保密的信息逻辑加密卡&#xff1a;逻辑加密卡内除了具有非易失性存…

redis缓存生产实践(一)---大key压缩

文章目录 前言一、缓存到底是使用String还是hash我该如何选择二、什么是大key及其影响2.1 什么是 Redis 大 key&#xff1f;2.2 大key带来的影响 三、大key压缩3.1 注解标记可能需要压缩的数据3.2 获取注解信息判断内存占用大小3.2 判断对象占用内存3.2 gzip压缩json3.2 判断当…

Reid之损失函数理论学习讲解

基于深度学习的Reid主要流程为输入图像-->CNN(提取特征)-->Global average pooling-->特征向量&#xff0c;将用这些特征来衡量图像的相似情况。并用这些特征进行检索&#xff0c;返回分类情况。 在训练网络的时候需要涉及损失函数&#xff0c;因此就引出了表征学习和…

大数据专业好找工作么

现在&#xff0c;在数字化转型的推动下&#xff0c;越来越多的企业意识到大数据的魅力&#xff0c;并不断在这个领域投入资金&#xff0c;Python大数据开发相关人才也备受青睐&#xff01; 学Python之前&#xff1a;这玩意真有传说中那么好么&#xff1f; 学Python之后&#…

【browser】浏览器跨域处理

好久没有更新博客了&#xff0c;前段时间在疯狂面试&#xff0c;最近工作了才有时间来写博客。 准备来讲讲面试里常问到的跨域处理吧。 说到跨域&#xff0c;我们可能会下意思的说出jsonp&#xff0c;服务端配置cors&#xff0c;node配置代理等&#xff0c;再多了&#xff0c;我…