C语言:预处理

C语言:预处理

    • 预定义符号
    • #define
      • 定义常量
      • 定义宏
      • 宏与函数对比
    • #操作符
    • ##操作符
    • 条件编译
    • 头文件包含
      • 库文件包含
      • 本地文件包含
      • 嵌套文件包含


预定义符号

C语⾔设置了⼀些预定义符号,可以直接使⽤,预定义符号也是在预处理期间处理的。

__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义

示例:

printf("file:%s line:%d\n", __FILE__, __LINE__);

输出结果:

file:test.c line:1

以上预定义符号,都是一些常量,可以自己一一尝试。


#define

在C语言中,#define是一个预处理器指令,用于定义宏。

宏是一个被预处理器替换的标识符或一个标识符和参数的组合。宏定义可以用来简化代码、提高代码的可读性和可维护性。

使用#define可以定义常量、函数宏等。

定义常量

  • 定义常量:
 #define PI 3.14

这样就可以在代码中使用PI来代表3.14

  • 定义关键字:
#define reg register

reg 定义为关键字 register,可以将reg这个简写代替register关键字使用。

  • 定义代码段
#define CASE break;case

正常的switch语句每一个case都要加上break,通过这个写法,我们可以在写CASE时自动补齐break


定义宏

#define 机制允许把参数替换到文本中,这种功能叫做 宏(macro) / 定义宏(define macro)

语法:

#define name(parament-list) stuff

其中的 parament-list 是⼀个由逗号隔开的符号表,它们可能出现在stuff中。

示例:

#define SQUARE(x) x * x

当我们在代码中输入以下代码:

int main()
{int a = 5;int b = SQUARE(a);return 0;
}

在编译后就会转化为:

int main()
{int a = 5;int b = a * a;return 0;
}

也就是直接发生了文本替换,这种宏的形式非常像函数,因此也可以称为宏函数。但是其也有很多需要注意的地方。

比如以下代码:

int a = 5;
int b = SQUARE(a + 1);

我们希望先执行a + 1,然后再传入SQUARE中,但是其不会这样做因为其会将上述代码直接替换为:

int a = 5;
int b = a + 1 * a + 1;

由于操作符优先级的问题,我们不会得到想要的结果。为了处理这个情况,我们需要把参数用小括号括起来:

#define SQUARE(x) (x) * (x)

代码就变成:

int a = 5;
int b = (a + 1) * (a + 1);

这样我们就可以行使预期的功能了。

那么我们再看到一串代码:

#define DOUBLE(x) (x) + (x)int a = 5;
int b = 10 * DOUBLE(a);

代码编译后为:

int a = 5;
int b = 10 * 5 + 5;  

又出现了一样的问题,我们的 10 * DOUBLE(a)并没有先执行DOUBLE(a),而展开后,又出现了操作符优先级问题,所以我们的宏还要再优化:

#define DOUBLE(x) ((x) + (x))

在宏的最外侧再加一层括号,就可以独立运行,不受外界操作符影响了。

通过以上推断,我们可以发现,宏虽然可以很好的替换代码,但是会受到外界操作符的影响,此时就要注意很多细节。


宏与函数对比

在这里插入图片描述
函数在调用时,是会开辟内存创建栈帧的,而宏则直接执行,所以速度更快。但是由于宏是在编译阶段就已经处理好了,所以宏不能通过调试观察现象,还要操作符优先级带来的种种问题。因此宏不适合处理复杂的函数,但是很适合短小简单的函数


#操作符

功能:

#可以将宏的参数转化为字符串

比如以下代码:

#define PRINT(n) printf("the value of "#n " is %d", n);

我们尝试调用:

int a = 5;
PRINT(a);

代码就会被转化为:

int a = 5;
printf("the value of ""a" " is %d", a);

可以看到,两个n的替换效果是不同的,对于n其会直接被替换为变量a;而对于#n,其不是简单的替换,而是把参数名转化为了字符串”a“


##操作符

##操纵符可以将两个符号合并为一个符号

示例:

#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
return (x>y?x:y); \
}

该宏用于创建不同类型的比大小函数,由于不同函数需要不同的函数名,于是利用##来连接函数名,也就是type##_max部分。type是一个宏参数,当传入floattype##_max整体就被连接为float_max,当传入int,整体就被连接为int_max。也就是##起到一个连接作用。


条件编译

条件编译是C语言中的一种编译指令,用于在编译过程中根据指定的条件选择性地包含或排除某些代码。它主要是为了满足不同平台、不同编译选项或不同场景下的需求。

条件编译使用预处理指令实现,预处理指令以#开头。下面是一些常用的条件编译指令及其用法:

  1. #if / #elif / #else / #endif
    #if用于基于预处理器常量的值进行条件判断。
    #elif用于在多个条件之间进行选择。
    #else用于在没有匹配的#if#elif时执行。
    #endif用于结束条件编译块。
    示例:
   #define NUM 5#if NUM > 10printf("NUM is greater than 10\n");#elif NUM > 0printf("NUM is greater than 0\n");#elseprintf("NUM is less than or equal to 0\n");#endif

这个代码和C语言的if代码很像,不过多讲解了。

  1. #ifdef / #ifndef
    #ifdef用于检查一个标识符是否已经定义,如果已定义则编译后面的代码,否则跳过。
    #ifndef#ifdef相反,用于检查一个标识符是否未定义。
    示例:
#ifdef DEBUGprintf("Debug mode enabled\n");
#endif

以上代码中,只要我们定义了DEBUG这个变量,就会输出"Debug mode enabled\n"语句。

  1. #define
    #define用于定义宏。宏是一种将一组指令作为一个整体进行替换的方式。
    示例:

    #define MAX(a, b) ((a) > (b) ? (a) : (b))int x = 10;
    int y = 20;
    int max = MAX(x, y);
    
  2. #include
    #include用于将指定的头文件包含到当前文件中。
    示例:

    #include <stdio.h>int main() {printf("Hello, World!\n");return 0;
    }
    
  3. #pragma
    #pragma用于向编译器发出特定的指令,如优化选项、警告控制等。它的语法和功能因编译器而异。
    示例:

    #pragma warning(disable: 4996)
    

这些是C语言中常用的条件编译指令和代码用法。通过合理使用条件编译,我们可以根据不同的需求自由地控制代码的编译过程。


头文件包含

头文件包含分两种形式:本地头文件库文件

库文件包含

语法:

#include <filename.h>

查找头⽂件会直接去标准路径下去查找,如果找不到就提⽰编译错误

我们平常使用的库文件都通过尖括号<>来包含,其会直接到存放库文件的路径中查找。

本地文件包含

#include "filename"

先在源⽂件所在⽬录下查找,如果该头⽂件未找到,编译器就像查找库函数头⽂件⼀样在标准位置查找头⽂件,如果找不到就提⽰编译错误

如果是用户自己编写的头文件,我们要用双引号""包含,如果通过这种方式包含头文件,那么会先在当前源文件的目录下查找,如果没有找到,再去库文件中查找

也就是说:库文件也可以通过双引号包含,但是会多出额外的查找步骤,所以库文件还是用尖括号包含更好,而自己编写的头文件必须双引号包含。

嵌套文件包含

假设我们现在有以下文件结构:
头文件test.h

void test();
struct Stu
{int id;char name[20];
};

源文件test.c

#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
int main()
{return 0;
}

我们在test.c中多次包含了头文件,这就会导致test.h反复被展开,产生大量重复代码。这就是嵌套文件包含的问题,那么我们要如何处理这个问题,让其只能被包含一次呢?

条件编译方法
在头文件test.h中加入以下代码:

#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容
#endif //__TEST_H__

第一次包含头文件:
先执行#ifndef __TEST_H__,我们此时没有定义__TEST_H__这个变量,if成立,此时头文件会被展开,同时执行#define __TEST_H__,此时__TEST_H__就已经被定义了


第二次包含头文件:
第二次站时,由于上一次展开已经定义了__TEST_H__这个变量,导致#ifndef __TEST_H__判断为假,此时整个头文件都不会再被编译,直接舍弃

后续再展开头文件,都会因为 __TEST_H__被定义而不会编译,解决了嵌套编译的问题。

一般而言,我们这个用于判断头文件有没有被展开过的变量,是头文件名通过一定规则转化来的:

  1. 在头文件前后加上两个下划线__头文件.h__
  2. 把头文件中的点.也改为下划线__头文件_h__

因此test.h的常量就是:__TEST_H__

pragma
通过条件编译其实是比较传统的写法,我们还有一种更加简洁方便的写法:

#pragma once

只要在任何头文件前面加上这句话,就只会被编译一次了。


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

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

相关文章

在你的 Vue + Electron 项目里,引入 ESLint

因为我的项目是基于 Electron 平台的 Web 应用&#xff0c;使用 Vue 3 实现&#xff0c;而且用了 TypeScript&#xff0c;所以&#xff0c;在引入 ESLint 的时候&#xff0c;要考虑好几种规范的问题。 文章目录 零、简介1. 规则2. 配置文件3. 共享配置4. 插件5. 解析器6. 自定义…

Vue开发实例(九)动态路由实现左侧菜单导航

之前在【Vue开发实例&#xff08;六&#xff09;实现左侧菜单导航】文中实现了菜单的导航&#xff0c;本篇是在那个基础上改造的。 动态路由实现左侧菜单导航 一、动态菜单创建二、根据菜单数据来创建路由三、添加路由已加载标记&#xff0c;省的每次点击菜单都要加载 一、动态…

【设计模式 01】单例模式

单例模式&#xff0c;是一种创建型设计模式&#xff0c;他的核心思想是保证一个类只有一个实例&#xff08;即&#xff0c;在整个应用程序中&#xff0c;只存在该类的一个实例对象&#xff0c;而不是创建多个相同类型的对象&#xff09;&#xff0c;并提供一个全局访问点来访问…

java012 - Java集合基础

1、集合基础 1.1 集合概述 引用数据类型包括&#xff1a;类、接口、数组[] 1.2 ArrayList构造和添加方法 代码&#xff1a; 空集合对象&#xff1a;[] add() add(int index,E element): 1.3 ArrayList集合常用方法

Stable Diffusion 模型分享:Henmix_Real(人像、真实、写真、亚洲面孔)

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八下载地址模型介绍 作者述:这个模型试图改

Springboot教程(五)——单元测试

idea中一般使用JUnit进行单元测试 基本使用 我们可以在idea的test文件夹下的XXXXApplicationTests内进行单元测试&#xff1a; 可以在Test标注的方法上写测试代码&#xff1a; SpringBootTest class C0101ApplicationTests {Testfun contextLoads() {println("Hello …

基础二分学习笔记

模板 : 个人倾向第一种 ; 整数二分 : 最大化查找 : 可行区域在左侧 : 查找最后一个<q的数的下标 : int find(int q){// 查找最后一个 < q 的下标 int l 0 , r n 1 ;while(l 1 < r){int mid l r >> 1 ;if(a[mid]<q) l mid ;else r mid ;}return…

土壤类型数据

国家地球系统科学数据中心

AGM CPLD (AGRV2K )的时钟(外部时钟和片上内部振荡器)

AGM CPLD &#xff08;AGRV2K &#xff09;的时钟(外部时钟和片上内部振荡器) 外部晶振 与 内部振荡器&#xff1a; mcu 和 cpld 联合编程时&#xff0c; 整颗芯片需要一颗外部晶振。 &#xff08;芯片有内部振荡器&#xff0c; 但误差较大&#xff0c; 校准后 5%以内误差&…

Electron通过预加载脚本从渲染器访问Node.js

问题&#xff1a;如何实现输出Electron的版本号和它的依赖项到你的web页面上&#xff1f; 答案&#xff1a;在主进程通过Node的全局 process 对象访问这个信息是微不足道的。 然而&#xff0c;你不能直接在主进程中编辑DOM&#xff0c;因为它无法访问渲染器 文档 上下文。 它们…

【软考】数据库的三级模式

目录 一、概念1.1 说明1.2 数据库系统体系结构图 二、外模式三、概念模式四、内模式 一、概念 1.1 说明 1.数据的存储结构各不相同&#xff0c;但体系结构基本上具有相同的特征&#xff0c;采用三级模式和两级镜像 2.数据库系统设计员可以在视图层、逻辑层和物理层对数据进行抽…

matplotlib散点图

matplotlib散点图 假设通过爬虫你获取到了北京2016年3, 10月份每天白天的最高气温(分别位于列表a, b), 那么此时如何寻找出气温和随时间(天)变化的某种规律? from matplotlib import pyplot as pltx_3 range(1, 32) x_10 range(51, 82)y_3 [11,17,16,11,12,11,12,6,6,7,8…

试手一下CameraX(APP)

书接上回。 首先还是看谷歌的官方文档&#xff1a; https://developer.android.com/media/camera/camerax?hlzh-cn https://developer.android.com/codelabs/camerax-getting-started?hlzh-cn#1 注&#xff1a;这里大部分内容也来自谷歌文档。 官方文档用的是Kotlin&…

Day14:信息打点-主机架构蜜罐识别WAF识别端口扫描协议识别服务安全

目录 Web服务器&应用服务器差异性 WAF防火墙&安全防护&识别技术 蜜罐平台&安全防护&识别技术 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/数据库/系统/WAF等 系统&#xff1a;操作系统/端口服务/网络环境/防火墙等 应用&#xff1a;APP对象/…

小程序图形:echarts-weixin 入门使用

去官网下载整个项目&#xff1a; https://github.com/ecomfe/echarts-for-weixin 拷贝ec-canvs文件夹到小程序里面 index.js里面的写法 import * as echarts from "../../components/ec-canvas/echarts" const app getApp(); function initChart(canvas, width, h…

Vscode 使用SSH远程连接树莓派的教程(解决卡在Downloading with wget)

配置Vscode Remote SSH 安装OpenSSH 打开Windows开始页面&#xff0c;直接进行搜索PowerShell&#xff0c;打开第一个Windows PowerShell&#xff0c;点击以管理员身份运行 输入指令 Get-WindowsCapability -Online | ? Name -like OpenSSH* 我是已经安装好了&#xff0c;…

学会玩游戏,智能究竟从何而来?

最近在读梅拉妮米歇尔《AI 3.0》第三部分第九章&#xff0c;谈到学会玩游戏&#xff0c;智能究竟从何而来&#xff1f; 作者: [美] 梅拉妮米歇尔 出版社: 四川科学技术出版社湛庐 原作名: Artificial Intelligence: A Guide for Thinking Humans 译者: 王飞跃 / 李玉珂 / 王晓…

基于springboot实现计算机类考研交流平台系统项目【项目源码+论文说明】

基于springboot实现计算机类考研交流平台系统演示 摘要 高校的大学生考研是继高校的高等教育更上一层的表现形式&#xff0c;教育的发展是我们社会的根本&#xff0c;那么信息技术的发展又是改变我们生活的重要因素&#xff0c;生活当中各种各样的场景都存在着信息技术的发展。…

Debezium发布历史163

原文地址&#xff1a; https://debezium.io/blog/2023/09/23/flink-spark-online-learning/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. Online machine learning with the data streams from the database …

SpringBlade CVE-2022-27360 export-user SQL 注入漏洞分析

漏洞描述 SpringBlade是一个基于Spring Cloud和Spring Boot的开发框架&#xff0c;旨在简化和加速微服务架构的开发过程。它提供了一系列开箱即用的功能和组件&#xff0c;帮助开发人员快速构建高效可靠的微服务应用。该产品/api/blade-user/export-user接口存在SQL注入。 漏…