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

文件包含的本质、预处理符号、# vs ##

何为头文件?

        在C语言中,文件包含是一种常见的编程技术,它允许程序员在一个源文件中使用另一个源文件中的函数或变量。

        文件包含通常使用`#include`预处理指令来实现。`#include`指令告诉预处理器将文件的内容插入到当前文件的指定位置中。

        例如,在一个C源文件中,如果想要使用另一个源文件中的函数,可以使用以下语句:

#include "otherfile.c"

        这个语句会告诉编译器将`otherfile.c`中的代码插入到当前文件的位置,然后再进行编译。当编译器遇到调用`otherfile.c`中的函数时,它能够找到函数的定义,并将它们编译到可执行文件中。

        需要注意的是,文件包含应该遵循一些最佳实践:

  • 为了避免重复包含,应该使用头文件而不是源文件进行文件包含。例如,使用`#include "otherfile.h"`而不是`#include "otherfile.c"`。
    #include "otherfile.h"  //使用
    #include "otherfile.c"  //不使用
  • 应该避免在头文件中放置函数或变量的定义。头文件应该只包含函数和变量的声明。
  • 应该避免在头文件中使用全局变量。全局变量会在包含文件的每个源文件中创建一个独立的实例,这样可能会导致命名冲突和意外行为。

为何所有头文件,都推荐写入下面代码?本质是为什么?

#ifndef XXX
#define XXX//TODO#endif

        这是为了避免头文件重复包含多次,导致编译错误或者不必要的浪费。当一个头文件被多次包含时,如果没有预处理器指令的保护,就会重复定义同一个符号,从而出现编译错误。

        为了避免这种问题,使用了 `#ifndef`、`#define`、`#endif` 三个预处理器指令,将头文件的内容包含在一个条件编译的块中。第一次包含头文件时,`XXX`未被定义,`#ifndef` 判断为真,进入条件编译块,`#define XXX` 定义符号 `XXX`,然后包含头文件的内容。

        当再次包含同一头文件时,`XXX`已被定义,`#ifndef` 判断为假,直接跳过条件编译块,从而避免了重复定义的问题。

#include究竟干了什么?

#include本质是把头文件中相关内容,直接拷贝至源文件中!

那么,在多文件包含中,有没有可能存在头文件被重复包含,乃至被重复拷贝的问题呢?

test.h
#ifndef _TEST_H_
#define _TEST_H_ //注意,这里没有包含<stdio.h>防止信息太多干扰我们
extern void show(); //任意一个函数声明
#endif
test.c
#include "test.h" //故意包含两次
#include "test.h"
int main()
{return 0;
}

经过预编译后的结果

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"
# 1 "test.h" 1 //test.h只被包含了1次extern void show();
# 2 "test.c" 2
int main()
{return 0;
}

但是当我们去掉条件编译呢?

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"
# 1 "test.h" 1extern void show(); //内容被拷贝第一次
# 2 "test.c" 2
# 1 "test.h" 1extern void show(); //内容被拷贝第二次
# 3 "test.c" 2
int main()
{return 0;
}

结论:

        所有头文件都必须带上条件编译,防止被重复包含! 那么,重复包含一定报错吗?不会! 重复包含,会引起多次拷贝,主要会影响编译效率!同时,也可能引起一些未定义错误,但是特别少。

#error 预处理

`#error`指令是C语言预处理器中的一个预编译指令,用于在预处理阶段产生编译错误。

#include <stdio.h>
#define __welcome
int main()
{
#ifdef __welcome
#error 老铁,非常感谢观看此篇文章哟!!!
#endifreturn 0;
}

        程序使用了条件编译指令`#ifdef`和`#endif`来判断`__welcome`宏是否已经定义。如果已经定义,则使用`#error`指令生成一个编译错误,输出一条提示信息。

#include <stdio.h>
//#define __welcome
int main()
{
#ifndef __welcome
#error 老铁,非常感谢观看此篇文章哟!!!
#endifreturn 0;
}

        程序定义了一个名为`__welcome`的宏,并使用了条件编译指令`#ifndef`和`#endif`来判断`__welcome`宏是否已经定义。如果未定义,则使用`#error`指令生成一个编译错误,输出一条提示信息。

结论:核心作用是可以进行自定义编译报错。

#line 预处理

 `#line`指令是C语言预处理器中的一个预编译指令,用于更改源代码中的行号和文件名,从而影响编译器错误和警告信息中的行号和文件名。

//本质其实是可以定制化你的文件名称和代码行号,很少使用
#include <stdio.h>
int main()
{printf("%s, %d\n", __FILE__, __LINE__); //C预定义符号,代表当前文件名和代码行号
#line 60 "welcome.h" //定制化完成printf("%s, %d\n", __FILE__, __LINE__);return 0;
}

        程序使用了`#line`指令将当前行号设置为60,并将当前文件名设置为`"welcome.h"`,从而定制化了文件名称和代码行号。在后续的`printf`函数中,预处理器会将`__FILE__`和`__LINE__`再次替换成定制化后的文件名和行号,从而输出新的信息。

#pragma 预处理

`#pragma`指令是一种不可依赖的、非标准的预处理指令,它通常被编译器用来提供一些与平台、编译器或者其他特殊需求相关的功能。

一些常见的`#pragma`指令包括:

  1.  `#pragma once`:告诉编译器只包含一次某个头文件,避免重复定义。
  2.  `#pragma GCC optimize`:指示GCC编译器优化代码。
  3.  `#pragma warning`:指示编译器输出警告信息。
  4.  `#pragma pack`:指示编译器对结构体进行字节对齐。
  5.  `#pragma message()`:可以用来进行对代码中特定的符号(比如其他宏定义)进行是否存在进行编译时消息提醒。
#include <stdio.h>
#define READ 
int main()
{
#ifdef READ
#pragma message("谢谢宝子阅读文章!!!")
#endifreturn 0;
}

 # 运算符

`#`运算符是C/C++语言中的一个预处理运算符,用于将宏定义参数转换成字符串常量。

#include<stdio.h>
int main()
{printf("hello world\n");printf("hello""world""\n");const char* msg = "hello""world""\n";//printf("%s\n",msg);printf(msg);return 0;
}

结论:相邻字符串自动连接特性 

#include<stdio.h>
#define STR(s) #s
int main()
{printf("PI = "STR(3.1415926)"\n");return 0;
}

 ## 预算符

`##`预算符是C/C++语言中的一个预处理运算符,用于将两个符号拼接成一个新的符号。

#include<stdio.h>
#define XNAME(n) student##n
int main()
{XNAME(1);XNAME(2);XNAME(3);XNAME(4);XNAME(5);XNAME(6);return 0;
}

 ##的实质:将##相连的两个符号,连接成为一个符号。

小练习实例:计算一个的科学计数法值

#include<stdio.h>#define CONT(x,n) (x##e##n)
int main()
{//计算浮点数科学计数法,相当于1.1 * (10^2)printf("%f\n", 1.1e2);printf("%f\n", CONT(1.1, 2)); return 0;
}

这段代码定义了一个宏`CONT`,用于将两个参数拼接成一个科学计数法格式的浮点数。在`main`函数中,首先使用`1.1e2`的科学计数法直接输出了`110.000000`,然后使用`CONT(1.1, 2)`宏对参数进行拼接,得到了相同的结果。由于`1.1`和`2`经过了拼接,因此最终展开的结果相当于`1.1e2`,即`110.000000`。这说明了`##`预算符可以用于将数字、字符串、变量名等不同类型的记号拼接在一起,从而得到想要的结果。

#include <stdio.h>#define CONCAT(x, y) x##yint main() {int xy = 10;printf("%d\n", CONCAT(x, y));//10return 0;
}

        使用`##`运算符和`CONCAT`宏定义输出变量`xy`的值。首先,在`main`函数中定义了一个名为`xy`的整型变量,并赋值为`10`。然后,通过`CONCAT(x, y)`宏调用,将参数`x`和`y`拼接在一起,得到了记号`xy`。最后,使用`printf`函数输出了`xy`变量的值,结果为`10`。由此可见,`##`运算符可以将字符串、变量名等记号拼接在一起,从而实现更加灵活的程序设计。

相关文章:

文件包含的本质、预处理符号、# vs ##

何为头文件&#xff1f; 在C语言中&#xff0c;文件包含是一种常见的编程技术&#xff0c;它允许程序员在一个源文件中使用另一个源文件中的函数或变量。 文件包含通常使用#include预处理指令来实现。#include指令告诉预处理器将文件的内容插入到当前文件的指定位置中。 例如&a…...

【JavaSE】Java基础语法(三十九):网络编程入门

文章目录 1. 网络编程概述2. 网络编程三要素3. IP地址4. InetAddress5. 端口和协议 1. 网络编程概述 计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络 操作系统&#xff0c;网络管理软件及网络通信协…...

中间件SOME/IP简述

SOME/IP SOME/IP 不是广义上的中间件&#xff0c;严格的来讲它是一种通信协议&#xff0c;但中间件这个概念太模糊了&#xff0c;所以我们也一般称 SOME/IP 为通信中间件。 SOME/IP 全称是 Scalable service-Oriented MiddlewarE over IP。也就是基于 IP 协议的面向服务的可扩…...

[自学记录03|百人计划]移动端GPU的TB(D)R架构基础

一、专有名词解释 1.System on Chip&#xff08;Soc&#xff09; Soc是把CPU、GPU、内存、通信基带、GPS模块等等整合在一起的芯片的称呼。常见有A系Soc&#xff08;苹果&#xff09;&#xff0c;骁龙Soc&#xff08;高通&#xff09;&#xff0c;麒麟Soc&#xff08;华为&am…...

详解Java枚举

一、知识点 二、概念 enum 的全称为 enumeration&#xff0c; 是 JDK 1.5 中引入的新特性。 在Java中&#xff0c;被 enum 关键字修饰的类型就是枚举类型。形式如下&#xff1a; enum Color { RED, GREEN, BLUE }如果枚举不添加任何方法&#xff0c;枚举值默认为从0开始的有…...

ES6-ES13学习笔记(4.0)

includes函数 判断字符串是否存在指定字符 <!--* Author: RealRoad1083425287qq.com* Date: 2023-06-01 08:40:33* LastEditors: Mei* LastEditTime: 2023-06-01 08:58:54* FilePath: \vscode\ECMA\05\01.html* Description: * * Copyright (c) 2023 by ${git_name_ema…...

线段树C++详细讲解和个人见解

问题引入 1275. 最大数 给定一个正整数数列 a1,a2,…,an&#xff0c;每一个数都在 0∼p−1 之间。 可以对这列数进行两种操作&#xff1a; 添加操作&#xff1a;向序列后添加一个数&#xff0c;序列长度变成 n1&#xff1b;询问操作&#xff1a;询问这个序列中最后 L 个数中…...

构建sysbench的镜像

方式1&#xff1a;先docker run一个镜像&#xff0c;手动安装好commit docker run -it --name mycentos arm64v8/centos:7 /bin/bash docker commit -a "PX Bai" mycentos mycentos1 docker run -it -d --namemycentos1 mycentos1 /bin/bash docker exec -it mycent…...

leetcode解题思路分析(一百四十)1201 - 1208 题

丑数3 给你四个整数&#xff1a;n 、a 、b 、c &#xff0c;请你设计一个算法来找出第 n 个丑数。丑数是可以被 a 或 b 或 c 整除的 正整数 。 容斥原理二分法 class Solution { public:int nthUglyNumber(int n, int a, int b, int c) {long long ab lcm((long long)a, (lo…...

FPGA设计的指导性原则 (一)

这一部分主要介绍FPGA/CPLD设计的指导性原则,如FPGA设计的基本原则、基本设 计思想、基本操作技巧、常用模块等。FPGA/CPLD设计的基本原则、思想、技巧和常用模 块是一个非常大的问题,在此不可能面面俱到,只能我们公司项目中常用的一些设计原则与 方法提纲携领地加以介绍,希…...

【架构】常见技术点--服务治理

导读&#xff1a;收集常见架构技术点&#xff0c;作为项目经理了解这些知识点以及解决具体场景是很有必要的。技术要服务业务&#xff0c;技术跟业务具体结合才能发挥技术的价值。 目录 1. 微服务 2. 服务发现 3. 流量削峰 4. 版本兼容 5. 过载保护 6. 服务熔断 7. 服务…...

手撕数据结构—单链表

✅作者&#xff1a;简单^不简单 &#x1f525;系列专栏&#xff1a;C语言数据结构 &#x1f496;如果文章有错误&#xff0c;时刻欢迎大家的指正。当然觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4ac;格言&#xff1a;希望我…...

Benewake(北醒) 快速实现 TF02-i-RS485 与电脑通信操作说明

目录 一、前言二、工具准备1. USB-RS485 转接器2. TF02-i-RS4853. 兆信直流电源4.连接线、绝缘胶带、螺丝刀5. PC&#xff1a;Windows 系统6. 串口助手软件 三、连接方式1. USB-RS485 转接板接口说明2. TF02-i-RS485 引脚定义3. 连接图 四、TF02-i-RS485 与电脑通信操作说明1. …...

【分享】科大讯飞星火认知大模型(初体验)

前言&#xff1a; 哈喽&#xff0c;大家好&#xff0c;我是木易巷~ 随着人工智能技术的迅猛发展&#xff0c;自然语言处理&#xff08;NLP&#xff09;成为了热门话题。在众多NLP模型中&#xff0c;科大讯飞星火认知大模型成为了一个备受瞩目的新秀&#xff0c;今天我们来了解…...

logstash 采集应用日志切割问题

1.logstash [oswatch@rce1 conf]$ cat test.conf input { file { path=>["/tmp/test/test.log*"] } } output { stdout { codec=>rubydebug{} } } 2.python脚本: [oswatch@rce1 conf]$ cat t1.py #!/usr/bin/python # -*- coding: UTF-…...

计算机网络实验:认识Packet Tracer软件

目录 前言实验目的实验内容及要求相关知识点实验指导实验过程总结 前言 计算机网络是当今信息技术的重要组成部分&#xff0c;它涉及到多种硬件和软件的协同工作&#xff0c;以实现数据的传输和交换。为了更好地理解和掌握计算机网络的基本原理和技术&#xff0c;我们需要进行…...

【MySQL新手到通关】第六章 时间日期函数

文章目录 1.获取日期时间函数1.1 获取当前日期时间1.2 获取当前日期1.3 获取当前时间 2.日期格式化★★★2.1 日期转指定格式字符串2.2 字符串转日期 3.日期间隔3.1 增加日期间隔 ★★★3.2 减去一个时间间隔★★★3.3 日期相差天数&#xff08;天&#xff09;3.4 相差时间&…...

深蓝学院C++基础笔记 第 1 章 C++初探

第 1 章 C初探 1&#xff0e;从Hello World 谈起 Hello World: #include <iostream> int mian() { std::cout << "Hello World!" << std::endl; }函数: 一段能被反复调用的代码&#xff0c;可以接收输入&#xff0c;进行处理并(或)产生输出-返回…...

【配电网重构】基于混合整数二阶锥配电网重构研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Kubernetes mysql 实战以及外部存储处理 [一]

在 Kubernetes 中部署 MySQL 数据库需要考虑以下几个方面: 部署方式:可以选择使用 StatefulSet 或者 Deployment 进行部署,如果需要有状态的服务,使用 StatefulSet 更加合适。存储:MySQL 需要一个持久化存储来保存数据。可以使用 Kubernetes 提供的 PersistentVolumeClaim…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...