完全自学C(干货) —— 预处理详解

目录

一,预定义符号

二,#define

#define定义的标识符

#define定义宏

#

##

带副作用的宏参数

宏和函数的对比

#undef

三,命令行定义

四,条件编译

五,文件包含

#include

六,其他预处理指令


一,预定义符号

  • C语言已预先定义好的内置符号;
  • 可用于日志信息,以便于调试等;
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如文件编译器遵循ANSI C,其值为1,否则未定义(gcc支持、vs不支持)
int main()
{
	printf("%s\n", __FILE__); //F:\VS\Project1\test.c
	printf("%d\n", __LINE__); //1990
	printf("%s\n", __DATE__); //Aug  1 2021
	printf("%s\n", __TIME__); //10:23 : 43
}

二,#define

#define定义的标识符

  • 预处理阶段时,替换;
  • 末尾建议不要加(;),否则多一个空语句或语法错误;
#define MAX 1000 //可替换数值
#define reg register //可替换关键字
#define do forever for(;;) //可替换一段语句
#define CASE break;case //可替换一段代码
//可替换多行代码(\续行符)
#define DEBUG_PRINT printf("file:%s\tline:%d\tdate:%s\ttime:%s\n",\
							__FILE__, __LINE__, \
							__DATE__, __TIME__)
	

#define定义宏

  • 允许把参数替换到文本中,称为宏(macro)或定义宏(define macro);

#define name( parament-list ) stuff

  • parament-list 参数列表,会在stuff中完成替换;
  • 括号()必须紧邻name;
#define SQUARE(x) x*x

int main()
{
	int ret = SQUARE(4); //先传参,在替换
	printf("%d", ret); 
}
//结果:16

注:

  • 宏是先替换,在计算的;
  • 对数值表达式进行求值的宏定义,应加上括号,避免在使用宏时由于参数中的操作符和邻近操作符之间产生歧义;
#define SQUARE(x) x*x

int main()
{
	int ret1 = SQUARE(3 + 1); //3+1*3+1
	int ret2 = 3 * SQUARE(3 + 1); //3*3+1*3+1
	printf("%d %d", ret1, ret2);
}
//结果:7 13
#define SQUARE(x) ((x)*(x))

int main()
{
	int ret1 = SQUARE(3 + 1); //(3+1)*(3+1)
	int ret2 = 3 * SQUARE(3 + 1); //3*((3+1)*(3+1))
	printf("%d %d", ret1, ret2);
}
//结果:16 48

#define替换规则

  • 在调用宏时,首先对参数进行检查,看是否包含任何由#define定义的符号;如果是,它们首先被替换;
  • 替换文本会被插入到程序中原来文本的位置;对于宏,参数名被他们的值替换;
  • 最后,再次对结果文件进行扫描,看是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
#define M 100
#define MAX(x,y) ((x)>(y)?(x):(y))

int main()
{
	int ret = MAX(10, M);
	return 0;
}

注:

  • 宏参数和#define定义中,可以出现其他#define定义的变量。但是对于宏,不能出现递归。
  • 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
#define M 100

int main()
{
	printf("M"); //此M不会被替换
	return 0;
}

#

  • #宏参数,可把一个宏参数变成对应的字符串;
  • 可实现在字符串中插入参数;
#define fun(x) #x //即转换为“x”

int main()
{
	char str[] = fun(abcd);
	printf("%s", str);
	return 0;
}
//结果:abcd
#define print(x, Format) printf("The value of "#x" "Format"!\n", x)

int main()
{
	int num1 = 10;
	float num2 = 20;
	print(num1, "%d"); //printf("The value of ""num1"" %d!\n", num1)
	print(num2, "%f"); //printf("The value of ""num2"" %f!\n", num2)
	return 0;
}
//The value of num1 10!
//The value of num2 20.000000!

##

  • 可把两边的符号合成一个符号;
  • 连接后需产生一个合法的标识符;
#define CAT(x, y) x##y

int main()
{
	char str[] = CAT("ab", "cd");
	int num1 = 10;
	int num2 = CAT(num, 1); 
	return 0;
}

带副作用的宏参数

  • 当宏参数在宏定义中出现超过一次时,带有副作用的参数,可能会出现未知风险;
#define MAX(x, y) ((x)>(y)?(x):(y))

int main()
{
	int a = 5;
	int b = 8;
	int ret = MAX(a++, b++); //((a++)>(b++)?(a++):(b++))
	printf("%d %d %d", ret, a, b); //9 6 10
}

宏和函数的对比

  • 宏,通常被用于执行简单的运算;

宏优势

  • 用于调用函数和函数返回的代码,可能会比实际执行小型计算的工作量所需时间更多;所以宏比函数在程序的规模和速度方面更优;
  • 函数的参数必须声明为特定的类型,所以函数只能在类型合适的表达式上使用;但宏与类型无关;

宏劣势

  • 每次使用宏时,会插入到程序中;可能会大幅度增加程序的长度;
  • 宏无法调试,调试是在可执行程序后;
  • 宏与类型无关,所以不够严谨;
  • 宏可能带来运算符优先级的问题,容易出错;
//宏,可传类型参数
#define MALLOC(num,type) (type*)malloc(num*sizeof(type))

int main()
{
    //(int*)malloc(10*sizeof(int))
	MALLOC(10, int);
    return 0;
}

注:

  • 命名约定,宏名全部大写,函数名不全部大写;

#undef

  • 取消宏定义
#define M 100

int main()
{
	#undef M
	printf("%d", M); //M为未定义的标识符
	return 0;
}

三,命令行定义

  • 许多C编译器,均允许命令行中定义符号,用于启动编译过程;
  • 如,在命令行中定义数值长度;

四,条件编译

#define M 100

int main()
{
#ifndef N
	printf("%d", M); //如未定义了N,即编译此语句
#endif

#ifdef M
	printf("%d", M); //如定义了M,即编译此语句
#endif
	return 0;
}

五,文件包含

#include

  • 可使另一个文件被编译;
  • 替换过程为预处理器先删除此指令,用包含文件的内容替换,被包含几次就替换几次;

头文件包含方式

  • 本地文件包含,#include “filename”;先在源文件所在目录下查找,如该头文件未找到,编译器在像查找库函数头文件一样,在标准位置查找头文件,如在未找到,提示编译错误;
  • 库文件包含,#include <filename>;直接去标准路径下去查找,如果找不到就提示编译错误;

注:

  • linux环境标准头文件的路径,/usr/include;
  • VS环境标准头文件的路径,C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt;

嵌套文件包含

  • 会造成多次重复引用头文件;
  • 解决方法,条件编译(在每个头文件开头添加#ifndef...#endif、或#pragma once);

六,其他预处理指令

  • #error
  • #pragma
  • #line
  • #pragma pack()
  • ...

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

搞的谁还不会爬福利美女跳舞视频一样,用我这个方法非常简单。

大家好啊&#xff01;经常听别人说爬虫玩的好&#xff0c;*****&#xff01;其实没有这么恐怖&#xff0c;爬虫你一般都是采集公开的信息&#xff0c;所以不会像网络传言那样&#xff0c;大家只要遵守协议&#xff0c;不会出问题的。 话说学编程语言的应该都是男孩子哈&#xf…

不解释

&#xff08;1&#xff09;业务线下 VS 线上大陆 VS 全球整合-国际规则合规企业 VS 产业极速联动-社会化资源调度&#xff08;2&#xff09;手段P&#xff1a;人工预测 VS 时序预测D&#xff1a;管理者人工指派任务 VS 运筹学最优求解C&#xff1a;人工检查监督审批工作流 VS …

第十五章 镜像架构和规划 - 示例镜像架构和网络配置

文章目录第十五章 镜像架构和规划 - 示例镜像架构和网络配置示例镜像架构和网络配置在单个数据中心、机房或校园内镜像配置简单故障转移对具有 DR 的故障转移配对和报告 Ayncs 同构连接第十五章 镜像架构和规划 - 示例镜像架构和网络配置 示例镜像架构和网络配置 本节描述并说…

Python用re模块使用正则表达式

Python正则表达式是一种强大的工具&#xff0c;用于在字符串中查找和匹配特定模式的文本。在Python中&#xff0c;可以使用re模块来使用正则表达式。正则表达式是一种模式匹配语言&#xff0c;可以在文本中寻找特定模式的字符串。正则表达式可以用于验证输入&#xff0c;搜索和…

MYSQL——美团面试题

MYSQL——美团面试题 2023/3/27 美团二面 题目描述 Create table If Not Exists courses (student varchar(255), class varchar(255));insert into courses (student, class) values (A, Math); insert into courses (student, class) values (B, English); insert into co…

《C++那些事》之开启你的BenchMark项目

《C那些事》之开启你的BenchMark测试0.导语本节目标&#xff1a;完成一个BenchMark小项目!在平时开发中&#xff0c;如何测试自己的接口性能呢&#xff1f;C里面如何快速搭建一个BenchMark测试框架呢&#xff1f;本节将Step By Step开启BenchMark入门的第一课。1.项目结构我们以…

灵动微基于 MM32SPIN040C 为主控的无感方波水泵应用方案

水泵是一种运输液体或增压液体的机器。将原动机的机械能或其他外部动能量传递给液体&#xff0c;提高液体动能&#xff0c;主要用于运输含水、油、酸碱液、乳化液、悬乳液和液体金属的液体。 介绍一款适用于水泵的32位单片机——MM32SPIN040C。 RAMSUN推荐一款应用于水泵的MC…

从零开始实现一个C++高性能服务器框架----协程调度模块

此项目是根据sylar框架实现&#xff0c;是从零开始重写sylar&#xff0c;也是对sylar丰富与完善 项目地址&#xff1a;https://gitee.com/lzhiqiang1999/server-framework 简介 项目介绍&#xff1a;实现了一个基于协程的服务器框架&#xff0c;支持多线程、多协程协同调度&am…

倒计时组件:可视化如何自定义目标时间 / 数字倒数

倒计时组件支持通过自定义目标时间或倒数数字&#xff0c;在报表和大屏中展示时间倒数和数字倒数。 下面以Sugar BI为例&#xff0c;为大家展示 倒计时展示模式 倒计时组件提供「时间倒数」和「数字倒数」两种展示模式&#xff0c;效果如下&#xff1a; 默认为「时间倒数」模…

将本地项目上传到远程仓库的步骤

文章目录将本地项目上传到远程仓库的步骤1.进入想上传的项目文件夹2.初始化本地仓库3.添加该项目下的所有文件4.将文件添加到本地仓库中5.添加远程仓库6.将文件更新到远程仓库上7.将本地文件推送回到指定的远程仓库中将本地项目上传到远程仓库的步骤 1.进入想上传的项目文件夹…

简单介绍TensorFlow中关于tf.app.flags命令行参数解析模块

这篇文章主要介绍了TensorFlow中关于tf.app.flags命令行参数解析模块&#xff0c;具有很好的参考价值&#xff0c;希望对大家有所帮助。如有错误或未考虑完全的地方&#xff0c;望不吝赐教 tf.app.flags命令行参数解析模块 说道命令行参数解析&#xff0c;就不得不提到 python…

Spring的IOC和DI入门

1、相关概念 1.1、Spring来源 官网&#xff1a;Spring | Home Spring是一个分层的Java SE/EE应用一站式的轻量级开源框架。Spring核心是IOC和AOP。 Spring主要优点包括&#xff1a; 方便解耦&#xff0c;简化开发&#xff0c;通过Spring提供的IoC容器&#xff0c;我们可以将…

分布式事务问题

分布式事务问题 1、什么是分布式事务 一次课程发布操作需要向数据库、redis、elasticsearch、MinIO写四份数据&#xff0c;这里存在分布式事务问题。 什么是分布式事务&#xff1f; 首先理解什么是本地事务&#xff1f; 平常我们在程序中通过spring去控制事务是利用数据库…

【C++】一维数组练习案例 - 五只小猪称体重

目录 1、缘起 2、案例描述 3、代码 4、总结 1、缘起 最近在黑马程序员 UP 主那里学习 C 编程语言&#xff0c;学习到了【第44节】一维数组练习案例 - 五只小猪称体重 知识点。找出五只小猪中最重的小猪&#xff0c;这不就是基本算法中的 "求最大值算法" 嘛。 为…

ChatGPT相关核心算法

ChatGPT 的卓越表现得益于其背后多项核心算法的支持和配合。本文将分别介绍作为其实现基础的 Transformer 模型、激发出其所蕴含知识的Prompt/Instruction Tuning 算法、其涌现出的思维链能力、以及确保其与人类意图对齐的基于人类反馈的强化学习算法。 1.基于Transformer的预…

STM32F4_时钟系统精讲

目录 1. 什么是系统时钟 2. 时钟树 2.1 LSI 2.2 LSE 2.3 HSI 2.4 HSE 2.5 PLLCLK 2.6 SYSCLK 2.7 HCLK 2.8 PCLK1 2.9 PCLK2 2.10 RTC/AWU 3 SysTick定时器 3.1 为什么会有Systick定时器&#xff1f; 3.2 SysTick定时器的作用 3.3 SysTick定时器的寄存器 4.…

DAMA-CDGA/CDGP数据治理认证考试地点一般有哪些?

据目前数据统计&#xff0c;进行过DAMA-CDGA/CDGP数据治理认证考试的地区主要是一线城市及直辖市&#xff0c;主要有&#xff1a;北京、上海、广州、深圳、西安、杭州、成都、重庆、武汉、厦门、太原、甘肃、长沙等。&#xff08;*当所在城市报名人数达到25人以上方可增加考场开…

C++基本语法

C 程序可以定义为对象的集合&#xff0c;这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象&#xff0c;方法、即时变量。 对象 - 对象具有状态和行为。例如&#xff1a;一只狗的状态 - 颜色、名称、品种&#xff0c;行为 - 摇动、叫唤、吃。对象是类…

SpringBoot2基础入门 --- 了解自动配置原理

一、SpringBoot特点 1.1、依赖管理 父项目做依赖管理 依赖管理 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version> </parent>…

固定资产AFAB进行折旧选择页面的各个选项分别是什么意思?

固定资产AFAB进行折旧选择页面的“记账运行原因”的4个选项&#xff0c;及下面的各个选项分别是什么意思&#xff1f; 一、“记账运行原因”的4个选项&#xff1a; 1&#xff0c;计划内记账运行&#xff08;Planned posting run&#xff09;&#xff1a;上期折旧成功运行&…