C/C++获取文件名的方法(__FILE__,__builtin_FILE(),__BASE_FILE__)
目录标题
- C/C++获取文件名的方法
- __FILE__宏
- 避免__FILE__宏的错误
- 慎用`$(subst $(dir $<),,$<)\"'")`来重定义
- __BASE_FILE__宏
- __builtin_FILE()函数
- Windows API函数GetModuleFileName()
- getenv()
- 使用cmake中的变量重定义__FILE__宏的CMake示例
C/C++获取文件名的方法
- 使用
__FILE__
宏可以获取当前源码文件的文件名
- 使用
__builtin_FILE()
函数__builtin_FILE()是一个内建函数,不同于__FILE__是一个预定义宏,因此__builtin_FILE()的效率可能更高。
- 使用
__BASE_FILE__
宏与__FILE__宏和__builtin_FILE()函数功能类似,它只包含当前编译单元的文件名,不包含任何路径信息(理论上是这样,实际往往跟__FILE__内容一致)。
- 使用
__PRETTY_FUNCTION__
宏__func__宏可以获取当前函数名,而__PRETTY_FUNCTION__宏可以获取当前函数的带有参数和返回类型的完整签名,其中包含了源码文件路径。
- 使用标准库中的
getenv
函数获取pwd
路径pwd 大家都懂。
- Windows API函数GetModuleFileName()
使用Windows API函数GetModuleFileName()可以获取当前程序的完整路径
- 编译时,譬如在cmake中获取文件名,并声明宏
编译时必然要获取所有源文件,这时候用这些文件名定义一个宏即可。
__FILE__宏
近年来,C/C++标准中的__FILE__宏定义引起了广泛关注。该宏定义可用于获取当前程序的文件路径,但是它也有一些限制和风险。
首先,让我们来看看__FILE__宏定义的定义和用法。__FILE__宏定义的作用是返回当前源文件的名称,这个名称可以在命令行或环境变量中设置。例如,假设你在C语言中编写了一个文件读取函数readFile,并且在代码中定义了myfile.c作为目标文件名,那么你可以使用以下代码获取当前文件的名称:#include <stdio.h> int main() { printf("Current file name: %s\n", __FILE__); return 0; }
然而,由于该宏定义只能返回当前源文件的名称,因此在某些情况下可能会导致问题。例如,如果你正在使用共享库或第三方库,而这些库或库中使用了与原始文件不同的文件名,那么__FILE__宏定义就无法正确地获取到库或库中使用的文件名。此外,即使在单线程环境下,__FILE__宏定义也不是一个安全的选项,因为它允许攻击者获取到当前程序的完整文件名。
因此,虽然__FILE__宏定义非常有用,但我们需要考虑它的安全性和限制。在实际应用中,我们可以通过使用更加安全的文件操作函数来替换__FILE__宏定义。例如,在Windows系统中,你可以使用资源管理器来读取和写入文件,而不需要使用预处理器指令。在Linux系统中,你可以使用进程空间的文件访问权限来达到类似的效果。
接下来,让我们分析一下__FILE__宏定义存在的风险和限制。首先,__FILE__宏定义只能返回当前源文件的名称,因此在某些情况下可能会导致问题。例如,如果你正在使用共享库或第三方库,而这些库或库中使用了与原始文件不同的文件名,那么__FILE__宏定义就无法正确地获取到库或库中使用的文件名。此外,即使在单线程环境下,__FILE__宏定义也不是一个安全的选项,因为它允许攻击者获取到当前程序的完整文件名。
其次,__FILE__宏定义还有一些局限性,它并不能正确地返回当前程序的真正文件路径。事实上,计算机文件系统的逻辑结构是非常复杂的,它由多个层次组成。例如,根目录下的子目录结构、父目录结构等都会影响到文件的真正路径。因此,__FILE__宏定义并不能准确地反映出文件的路径。
FILE 宏返回的是绝对路径的名称,但是在很多时候我们只需要获取文件名即可,而不需要那一长段繁琐的目录前缀,这时候我们需要对
__FILE__
宏重定义,当然这只是在不得不用__FILE__宏的情况,如果不是这样,你完全可以用其他方式。。
避免__FILE__宏被重定向到错误的文件,我们可以按照以下建议进行操作:
如果使用的是CMake 3.14及以上版本,可以使用CMAKE_CURRENT_LIST_FILE
变量来获取当前正在处理的CMake列表文件的完整路径,而不是使用__FILE__宏。
- 如果您的项目中使用了多个CMake文件,可以在每个文件中使用不同的宏定义来避免重复定义__FILE__宏。,需要仔细检查您的代码和CMake文件,并确保它们是正确的,并使用适当的宏定义和变量来获取文件路径。
这个语法是GNU make中的函数subst的使用,用于将字符串中的某个子串替换为另一个字符串。
在 Makefile 中,$<
表示当前规则中的第一个依赖文件,$(dir $<)
表示$<
的目录部分,$(subst OLD,NEW,TEXT)
表示将 TEXT 中的 OLD 字符串替换为 NEW 字符串。
因此,$(subst $(dir <),,<),,<),,<) 表示将 $< 中的目录部分替换为空,即只保留文件名部分。
例如,如果当前规则中的第一个依赖文件是 src/foo/bar.c,那么$(dir $<)
的值为src/foo/,$(subst $(dir $<),,$<)
的值就是 bar.c。然后,“'”) 表示在文件名后添加一个双引号和单引号,以便将文件名传递给编译器。
在cmake工程中
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -U__FILE__ -D__FILE__='\"$(subst $(dir $<),,$<)\"'")
这个命令告诉编译器不要定义__FILE__宏,而是在后面使用-D__FILE__='“$(subst $(dir <),,<),,<),,<)”'选项重新定义__FILE__宏,将其重定向到输入文件的相对路径。
依赖文件名并不一定意味着源文件名,有时候会被指定为编译器依赖项文件名。
这时候我们的__FILE__就会得不到正确的文件名,而且重定向到错误的中间文件名上,比如compiler_depend.ts
文件,有关此文件的介绍可以查看:Cmake 中 compiler_depend.ts文件
__BASE_FILE__宏
如果您使用的是GCC编译器,可以使用
__BASE_FILE__
宏来获取当前源文件的路径,而不是使用__FILE__宏。该宏在GCC 4.3及以上版本中可用。
__BASE_FILE__宏包含文件名,但不包含文件路径。但是,如果你在源代码中包含了文件路径,那么__BASE_FILE__宏的值将包含路径。这是由于__BASE_FILE__宏的值是在预处理阶段计算的,此时源代码中包含的文件路径已经被展开了。
例如,假设你的源代码文件位于/path/to/source目录下,且包含以下行:
#include "file.h"
在预处理阶段,这行代码将被展开为:
#include "/path/to/source/file.h"
因此,在这种情况下,__BASE_FILE__宏的值将是file.h,但包含路径/path/to/source`。
__builtin_FILE()函数
__builtin_FILE()函数确实是GCC内置函数之一,但是其具体实现并不是源代码形式的,而是由GCC编译器自带的。因此,我们无法提供其具体源代码的定义。__builtin_FILE()函数的实现方式可能随着GCC版本的更新而发生变化,因此无法确切地给出其源代码的定义。
__builtin_FILE()函数的作用是获取当前文件的文件名,它是由编译器自动实现的。在使用该函数时,只需要在代码中调用__builtin_FILE()函数即可,无需手动编写该函数的源代码。由于是内建函数,可以被编译器优化,理论上比__FILE__宏效率更高。
重定义时要指定参数set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin")
Windows API函数GetModuleFileName()
GetModuleFileName()是Windows API函数之一,用于获取指定模块的文件名,包括路径和文件名。该函数提供了一种方法,可以在运行时获取当前可执行文件的路径和文件名。
DWORD GetModuleFileName(HMODULE hModule,LPSTR lpFilename,DWORD nSize );
其中,hModule参数指定要获取路径和文件名的模块的句柄。如果该参数为NULL,则GetModuleFileName()函数将返回调用它的可执行文件的路径和文件名。
lpFilename参数是指向一个缓冲区的指针,用于接收路径和文件名。该缓冲区必须足够大,以至于可以容纳完整的路径和文件名。如果函数成功执行,lpFilename将包含路径和文件名。
nSize参数指定缓冲区的大小,以字节为单位。如果lpFilename缓冲区的大小小于路径和文件名的长度,函数将无法成功执行,并返回0。
GetModuleFileName()函数返回值表示复制到缓冲区的字符数,不包括结尾的空字符。如果函数执行失败,返回值为0。
使用GetModuleFileName()函数,我们可以在运行时获取当前可执行文件的路径和文件名,并将其存储到缓冲区中。例如:#include <windows.h>#include <stdio.h>int main() {char path[MAX_PATH];GetModuleFileName(NULL, path, MAX_PATH);printf("The current file is: %s\n", path);return 0;}
getenv()
使用getenv函数即可,没必要使用system之类执行shell命令的函数。
getenv() 函数的返回值是一个指向环境变量值的字符串指针,可以将其用于后续的操作,例如打印、比较等等。而 system() 函数的返回值是执行命令的状态码,用于判断命令是否执行成功。
getenv() 函数在获取环境变量时不会对系统进行任何修改,只是返回变量的值。而 system() 函数会执行指定的系统命令,可能会对系统进行修改,例如创建、删除文件等等。#include <iostream> #include <cstdlib>int main() {const char* filename = std::getenv("PWD"); //获取当前工作目录std::cout << filename << std::endl;return 0; }
使用cmake中的变量重定义__FILE__宏的CMake示例
- 根目录遍历子目录情况一:如果源文件在多个目录下,比如src和lib
# 遍历 src 目录下的源文件 file(GLOB_RECURSE SRC_FILES_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cxx" "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc" "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c")# 遍历 lib 目录下的源文件 file(GLOB_RECURSE SRC_FILES_LIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/lib/*.cxx" "${CMAKE_CURRENT_SOURCE_DIR}/lib/*.cc" "${CMAKE_CURRENT_SOURCE_DIR}/lib/*.c")# 合并源文件列表 set(SRC_FILES ${SRC_FILES_SRC} ${SRC_FILES_LIB})# 添加源文件到项目中 foreach(SRC_FILE ${SRC_FILES})# 获取相对路径和文件名file(RELATIVE_PATH FILE_REL_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${SRC_FILE})string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR}/ "" MY_FILE_NAME ${FILE_REL_PATH})# 设置源文件的宏定义set_source_files_properties(${SRC_FILE} PROPERTIES COMPILE_DEFINITIONS "__FILE__=\\\"${MY_FILE_NAME}\\\"")# 添加源文件到项目中add_executable(my_project ${SRC_FILE}) endforeach()
- 根目录遍历子目录情况二:如果每个目录都有可执行文件要生成
file(GLOB_RECURSE SRC_FILES_SRC1"${CMAKE_CURRENT_SOURCE_DIR}/libs/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/libs/*.cxx" "${CMAKE_CURRENT_SOURCE_DIR}/libs/*.cc" "${CMAKE_CURRENT_SOURCE_DIR}/libs/*.c" "libs/subdir1/*.cpp" "libs/subdir1/*.cxx" "libs/subdir1/*.cc" "libs/subdir1/*.c" "libs/subdir2/*.cpp" "libs/subdir2/*.cxx" "libs/subdir2/*.cc" "libs/subdir2/*.c") foreach(SRC_FILE ${SRC_FILES_SRC1})# 获取相对路径和文件名file(RELATIVE_PATH FILE_REL_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src1 ${SRC_FILE})string(REPLACE "src1/" "" MY_FILE_NAME ${FILE_REL_PATH})# 设置源文件的宏定义set_source_files_properties(${SRC_FILE} PROPERTIES COMPILE_DEFINITIONS "__FILE__=\\\"${MY_FILE_NAME}\\\"") endforeach()add_executable(executable1) target_sources(executable1 PRIVATE ${SRC_FILES_SRC1})
- 最简单的方式:遍历源文件获取名称并重定义__FILE__宏
#遍历源码文件获取文件名逐以重定义宏 foreach(source ${USR_SOURCES}) get_filename_component(file_name ${source} NAME) set_source_files_properties(${source} PROPERTIES COMPILE_DEFINITIONS"FILE_NAME=\"${file_name}\";__FILE__=FILE_NAME") endforeach()
相关文章:
C/C++获取文件名的方法(__FILE__,__builtin_FILE(),__BASE_FILE__)
目录标题C/C获取文件名的方法__FILE__宏避免__FILE__宏的错误慎用$(subst $(dir $<),,$<)\"")来重定义__BASE_FILE__宏__builtin_FILE()函数Windows API函数GetModuleFileName()getenv()使用cmake中的变量重定义__FILE__宏的CMake示例C/C获取文件名的方法 使用…...

线程池的讲解和实现
🚀🚀🚀🚀🚀🚀🚀大家好,今天为大家带来线程池相关知识的讲解,并且实现一个线程池 🌸🌸🌸🌸🌸🌸🌸🌸…...
linux编程──gcc和clang
实验链接 编译原理实验-GCC/Clang工具链在ARM架构上的使用 实验报告 第1关:理解程序的不同表示形式 ##问题1-1: 如果在命令行下执行 gcc -DNEG -E sample.c -o sample.i生成的sample.i 与之前的有何区别? 根据定义NEG,而选择了M定义为-4…...

字节跳动测试岗面试记:二面被按地上血虐,所幸Offer已到手...
在互联网做了几年之后,去大厂“镀镀金”是大部分人的首选。大厂不仅待遇高、福利好,更重要的是,它是对你专业能力的背书,大厂工作背景多少会给你的简历增加几分竞争力。 但说实话,想进大厂还真没那么容易。最近面试字…...

5.多线程学习
作者:爱塔居 专栏:JavaEE 作者简介:大三学生,喜欢总结与分享~ 文章目录 目录 文章目录 章节回顾 一、wait 和notify 二、设计模式 2.1 单例模式 章节回顾 线程安全 1.一个线程不安全的案例(两个线程各自自增5w次&…...

数据结构中的堆
一、树的重要知识点 节点的度:一个节点含有的子树的个数称为该节点的度(有几个孩子)叶节点或终端节点:度为0的节点称为叶节点;如上图:B、C、H、I...等节点为叶节点(0个孩子)非终端节点或分支节点…...

Linux内核设备信息集合
本文结合设备信息集合的详细讲解来认识一下设备和驱动是如何绑定的。所谓设备信息集合,就是根据不同的外设寻找各自的外设信息,我们知道一个完整的开发板有 CPU 和各种控制器(如 I2C 控制器、SPI 控制器、DMA 控制器等)࿰…...

若依框架---权限管理设计
前言 若依权限管理包含两个部分:菜单权限 和 数据权限。菜单权限控制着我们可以执行哪些操作。数据权限控制着我们可以看到哪些数据。 菜单是一个概括性名称,可以细分为目录、菜单和按钮,以若依自身为例: 目录,就是页…...
Java设计模式(二)——工厂模式
当用户需要一个类的子类实例,且不希望与该类的子类形成耦合或者不知道该类有哪些子类可用时,可采用工厂模式;当用户需要系统提供多个对象,且希望和创建对象的类解耦时,可采用抽象工厂模式。 工厂模式一般分为简单工厂、…...

【Maven】
MavenMaven简介仓库坐标Maven项目构建依赖管理生命周期及插件插件模块拆分与开发聚合继承属性版本管理资源配置多环境开发配置跳过测试私服Maven简介 Maven的本质时一个项目管理工具,将项目开发和管理过程抽象成一个项目对象模型(POM) POM(Project Object Model)&a…...

[JAVA]继承
目录 1.继承的概念 2.继承的语法 3.父类成员访问 3.1子类中访问父类成员变量 3.2子类中访问父类成员方法 4.super关键字 5.子类构造方法 6.继承方式 7.final关键字和类的关系 面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复…...

Vue3 pinia持久化存储(组合式Api案例演示)
pinia-plugin-persist( pinia持久化插件) 本文采用的是 组合式Api的方式来做Pinia的持久化存储演示 如果对pinia的持久化还是不是很了解的👨🎓|👩🎓,可以看一下笔者的上一篇文章…...
8个你一看就觉得很棒的Vue开发技巧
1.路由参数解耦 通常在组件中使用路由参数,大多数人会做以下事情。 export default {methods: {getParamsId() {return this.$route.params.id}} }在组件中使用 $route 会导致与其相应路由的高度耦合,通过将其限制为某些 URL 来限制组件的灵活性。 正…...

vue3+ts 开发效率提升
1、vite pnpm项目初始化 pnpm: 比npm或yarn快10倍 pnpm与其他包管理器(如npm和Yarn)的不同之处在于它使用一种称为“硬链接”的独特安装方法。当你使用PNPM安装一个包时,它并不会将包的文件复制到每个项目的node_modules目录中&a…...

【数据结构与算法】队列和栈的相互实现以及循环队列
目录🌔一.用队列实现栈🌙1.题目描述🌙2.思路分析🌙3.代码实现⛈二.用栈实现队列☔1.题目描述☔2.思路分析☔3.代码实现🌈三.实现循环队列🌔一.用队列实现栈 🌙1.题目描述 我们先看一下题目链接…...
mysql连接不上问题解决
公司新搭内网测试环境,mysql远程登录问题解决 远程登录: 1 修改host, mysql> select user,host,plugin from user; ---------------------------------------------------- | user | host | plugin | ------------------------…...

利用nginx实现动静分离的负载均衡集群实战
前言 大家好,我是沐风晓月,今天我们利用nginx来作为负载,实现两台apache服务器的动静分离集群实战; 本文收录于沐风晓月的专栏《linux基本功-系统服务实战》,更多内容可以关注我的博客: https://blog.csd…...

与chatGPT神聊,引领你深入浅出系统调用
在操作系统的教学中,系统调用的作用不言而喻,但是,对系统调用常常是雾里看花,似乎明白,又难以真正的触及,即使在代码中调用了系统调用,比如调用fork()创建进程࿰…...

自学大数据第十天~Hbase
随着数据量的增多,数据的类型也不像原来那样都是结构化数据,还有非结构化数据; Hbase时google 的bigtable的开源实现, BigtableHbase文件存储系统GFSHDFS海量数据处理MRMR协同管理服务chubbyzookeeper虽然有了HDFS和MR,但是对于数据的实时处理是比较困难的,没有办法应对数据的…...
vue更高效的工具-vite
目录 1.webpack 2.vite是什么 3.使用vite创建项目 4.最后总结 🐼webpack 简单来说,Webpack是一个打包工具。 站在2018年的角度,成为一个优秀的前端工程师,除了要会写页面样式和动态效果之外,还需要会用主流的单页…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...