在C语言/C++中把资源编译进exe可执行文件,并运行时释放资源

简介: 在C语言/C++中把资源编译进exe可执行文件,并运行时释放资源

有时为了方便起见,我们会把资源文件(图片、声音等等)编译进exe程序,需要的时候释放出来。使用gcc可以实现这个东西。

1、安装TDM-GCC

TDM-GCC是推荐用的很好的编译器,基于mingw64改造,安装时会自动配置环境变量,简单方便。下载地址备用地址

2、准备资源文件并链接

通过objcopy命令链接资源文件为o文件:

# Windows系统下(编译64位程序时)objcopy -I binary -O pe-x86-64 -B i386:x86-64 资源文件名 输出的链接文件名
# Linux系统下(编译32位程序时)objcopy -I binary -O elf32-i386 -B i386 资源文件名 输出的链接文件名
# Linux系统下(编译64位程序时)objcopy -I binary -O elf64-x86-64 -B i386:x86-64 资源文件名 输出的链接文件名

注意输出的文件扩展名要是o。

比如我这里有一个jst.jar的文件,通过objcopy链接为jst.o

objcopy -I binary -O pe-x86-64 -B i386:x86-64 jst.jar jst.o

这样,当前目录下面出现了链接的文件:

网络异常,图片无法展示
|

再通过nm命令查询被链接的文件指针名:

nm 被链接的文件

例如我这里:

网络异常,图片无法展示
|

  • _binary_jst_jar_end 文件末指针
  • _binary_jst_jar_size 文件大小指针
  • _binary_jst_jar_start 文件头指针

3、编写C/C++源文件并合并编译

刚刚我们获取了文件各指针名,那么在C语言/C++中我们就可以使用它们了!具体先要用extern语句来声明它们为char数组类型,平时就用文件头指针和文件末指针即可。要获取文件大小,不建议直接用上面获得的大小指针,建议通过末指针减头指针来计算得到。我的代码如下:

#include <stdio.h>#include <stdlib.h>externchar_binary_jst_jar_start[]; //引用文件头指针externchar_binary_jst_jar_end[]; //引用文件末指针intmain() {
intsize=_binary_jst_jar_end-_binary_jst_jar_start; //始末指针相减得到文件大小printf("文件的起始指针为:%d\n", _binary_jst_jar_start);
printf("文件的结束指针为:%d\n", _binary_jst_jar_end);
printf("文件的大小:%d\n", size);
system("pause");
}

可以看到虽然引用的时候是char数组形式,但实质上它就是一个指针。数组名即为它的地址值。直接用其数组名获取地址。

C++的话操作方式相同,把printf换成cout语句即可。

然后再把资源文件和C源文件编译到一起:

C语言:

gcc C源文件 链接的资源文件 -o 编译输出文件

C++:

g++ C++源文件 链接的资源文件 -o 编译输出文件

例如我的:

// C:
gcc Main.c jst.o -o out.exe
// C++:
g++ Main.cpp jst.o -o out.exe

注意Windows上输出文件格式是exe的,linux可以不带格式(扩展名)。

在如果需要编译32位程序或者64位程序需要使用-m指定架构(不带m参数时跟随系统架构),例如下:

# C语言编译32位应用程序:gcc-m32 Main.c jst.o -o out.exe
# C语言编译64位应用程序:gcc-m64 Main.c jst.o -o out.exe
# C++编译32位应用程序:g++-m32 Main.cpp jst.o -o out.exe
# C++编译64位应用程序:g++-m64 Main.cpp jst.o -o out.exe

运行结果:

网络异常,图片无法展示
|

如果想释放这个文件怎么办呢?既然得到了文件始末指针,那么释放它也就很方便了。

在C语言中,先通过fopen建立一个新文件,释放文件时就写入到这个新文件里面(相当于这个新文件就是你释放的文件)。再用fwrite函数释放即可。例如我要把这个文件释放到E:\中转\outFile.jar,那么代码如下:

C语言:

#include <stdio.h>#include <stdlib.h>externchar_binary_jst_jar_start[]; //引用文件头指针externchar_binary_jst_jar_end[]; //引用文件末指针intmain() {
intsize=_binary_jst_jar_end-_binary_jst_jar_start; //始末指针相减得到文件大小FILE*fp=fopen("E:\\中转\\outFile.jar", "wb");  //创建文件指针,指定释放的位置并指定为二进制只写模式fwrite(_binary_jst_jar_start, size, 1, fp); //fwrite各参数含义:文件头指针位置,单次写入区块大小,写入区块数量,文件指针fclose(fp); //一定要fclose!否则文件可能损坏printf("文件释放完毕\n");
system("pause");
}

文件操作方面,C++和C语言略有不同。C++需要使用ofstream创建对象并打开文件,然后使用其write()函数写入。

C++:

#include <iostream>#include <fstream>#include <stdlib.h>usingnamespacestd;
externchar_binary_jst_jar_start[];
externchar_binary_jst_jar_end[];
intmain() {
intsize=_binary_jst_jar_end-_binary_jst_jar_start;
ofstreamfp;
fp.open("E:\\中转\\outFile.jar", ios::binary|ios::out);    //打开(指定)输出文件fp.write(_binary_jst_jar_start, size);    //write函数各参数意义:待写入数据头指针,写入的数据大小fp.close();
cout<<"文件释放完毕"<<endl;
system("pause");
}

结果:

网络异常,图片无法展示
|

它出现在了指定位置。

最后需要说明一下得是昨天通过测试,发现在Windows系统上只有64位的gcc/g++编译器才能实现这个。32位编译器或者编译32位程序这样做就无法编译通过。 Linux系统上都可以实现。

相关文章
|
4月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
2月前
|
机器学习/深度学习 人工智能 API
如何在c++侧编译运行一个aclnn(AOL)算子?
CANN的AOL库提供了一系列高性能算子API,优化了昇腾AI处理器的调用流程。通过两段式接口设计,开发者可以高效地调用算子库API,实现模型创新与应用,提升开发效率和模型性能。示例中展示了如何使用`aclnnAdd`算子,包括环境初始化、算子调用及结果处理等步骤。
|
25天前
|
自然语言处理 编译器 C语言
为什么C/C++编译腰要先完成汇编
C/C++ 编译过程中先生成汇编语言是历史、技术和实践的共同选择。历史上,汇编语言作为成熟的中间表示方式,简化了工具链;技术上,分阶段编译更高效,汇编便于调试和移植;实践中,保留汇编阶段降低了复杂度,增强了可移植性和优化能力。即使在现代编译器中,汇编仍作为重要桥梁,帮助开发者更好地理解和优化代码。
51 25
为什么C/C++编译腰要先完成汇编
|
1月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
3月前
|
自然语言处理 编译器 Linux
|
2月前
|
存储 自然语言处理 Unix
【C语言】C语言 4 个编译过程详解
编译是将源代码转换为目标代码的过程。它是在编译器的帮助下完成的。编译器检查源代码是否存在语法或结构错误,如果源代码没有错误,则生成目标代码。
90 1
|
3月前
|
自然语言处理 编译器 Linux
告别头文件,编译效率提升 42%!C++ Modules 实战解析 | 干货推荐
本文中,阿里云智能集团开发工程师李泽政以 Alinux 为操作环境,讲解模块相比传统头文件有哪些优势,并通过若干个例子,学习如何组织一个 C++ 模块工程并使用模块封装第三方库或是改造现有的项目。
|
4月前
|
存储 自然语言处理 编译器
|
4月前
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
270 22
|
4月前
|
编译器 Linux C语言
【C语言篇】编译和链接以及预处理介绍(下篇)
【C语言篇】编译和链接以及预处理介绍(下篇)
45 1
【C语言篇】编译和链接以及预处理介绍(下篇)