【我的C语言学习进阶之旅】关于C/C++内存对齐读取文件产生的问题以及解决方法
2021/11/26 7:12:02
本文主要是介绍【我的C语言学习进阶之旅】关于C/C++内存对齐读取文件产生的问题以及解决方法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
一、问题描述
今天在使用OpenGL ES 加载一个 TGA 图片文件的时候,出现了加载失败的问题。
关于什么是TGA文件以及如何打开TGA文件?
可以参考我的博客:【我的OpenGL学习进阶之旅】什么是TGA文件以及如何打开TGA文件?
如下图所示,没有texture加载进来,黑黢黢的页面。
查看日志打印,发现加载tga图片失败,如下所示:
2021-11-25 15:42:31.690 6385-6548/com.oyp.openglesdemo I/NDK_JNI_LOG_TAG: [GLUtils.cpp][loadTgaTexture][240]: Error loading (texture/heightmap.tga) image.
关于日志打印的内容带有文件文件名、方法名、行号 等信息的实现方法
可以参考我的博客 【我的Android进阶之旅】NDK开发之在C++代码中使用Android Log打印日志,打印内容带有文件文件名、方法名、行号 等信息,方便定位日志输出的地方
二、分析问题
2.1 断点调试
- loadTgaTexture 函数
loadTgaTexture 函数代码如下:
// // Load texture from disk // GLuint GLUtils::loadTgaTexture (const char *fileName ) { int width, height; char *buffer = esLoadTGA (fileName, &width, &height ); GLuint texId; if ( buffer == nullptr ) { LOGI ( "Error loading (%s) image.\n", fileName ) return 0; } glGenTextures ( 1, &texId ); glBindTexture ( GL_TEXTURE_2D, texId ); glTexImage2D ( GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); free ( buffer ); return texId; }
可以发现,因为loadTgaTexture
函数调用esLoadTGA
函数返回的buffer
为nullptr
所以报错了。
- esLoadTGA 函数
esLoadTGA 函数代码如下:
// // esLoadTGA() // // Loads a 8-bit, 24-bit or 32-bit TGA image from a file // char * esLoadTGA (const char *fileName, int *width, int *height ) { char *buffer; esFile *fp; TGA_HEADER Header; int bytesRead; // Open the file for reading fp = esFileOpen (fileName ); if ( fp == nullptr ) { // Log error as 'error in opening the input file from apk' LOGE ( "esLoadTGA FAILED to load : { %s }\n", fileName ) return nullptr; } LOGD ( "sizeof ( TGA_HEADER ) : { %d }\n", sizeof ( TGA_HEADER ) ) bytesRead = esFileRead ( fp, sizeof ( TGA_HEADER ), &Header ); *width = Header.Width; *height = Header.Height; if ( Header.ColorDepth == 8 || Header.ColorDepth == 24 || Header.ColorDepth == 32 ) { int bytesToRead = sizeof ( char ) * ( *width ) * ( *height ) * Header.ColorDepth / 8; // Allocate the image data buffer buffer = ( char * ) malloc ( bytesToRead ); if ( buffer ) { bytesRead = esFileRead ( fp, bytesToRead, buffer ); esFileClose ( fp ); return ( buffer ); } } return ( nullptr ); }
我们在这个esLoadTGA
函数打断点,调试一下看看:
定位到问题,因为Header.ColorDepth
为87 'W'
,既不等于8,也不等于24,还不等于32,所以最终返回了nullptr
。
2.2 为啥呢?
原来产生这个问题的原因和C/C++内存对齐
有关。这里,读者可以参考下面的文章了解。
- C/C++内存对齐详解 https://zhuanlan.zhihu.com/p/30007037
我在esLoadTGA
函数里面有两句代码,如下所示,在读取TGA
文件之前,打印了TGA_HEADER
的sizeof
LOGD ( "sizeof ( TGA_HEADER ) : { %d }\n", sizeof ( TGA_HEADER ) ) bytesRead = esFileRead ( fp, sizeof ( TGA_HEADER ), &Header );
打印出来的sizeof 大小为20,所以导致了无法加载TGA。
2021-11-25 15:42:31.688 6385-6548/com.oyp.openglesdemo D/NDK_JNI_LOG_TAG: [GLUtils.cpp][esLoadTGA][203]: sizeof ( TGA_HEADER ) : { 20 } 2021-11-25 15:42:31.690 6385-6548/com.oyp.openglesdemo I/NDK_JNI_LOG_TAG: [GLUtils.cpp][loadTgaTexture][240]: Error loading (texture/heightmap.tga) image.
- 结构体TGA_HEADER
结构体 TGA_HEADER 的定义如下,因为C/C++内存对齐,导致TGA_HEADER 的size为20,所以不对劲,如下所示:
typedef struct { unsigned char IdSize, MapType, ImageType; unsigned short PaletteStart, PaletteSize; unsigned char PaletteEntryDepth; unsigned short X, Y, Width, Height; unsigned char ColorDepth, Descriptor; } TGA_HEADER;
三、解决问题
3.1 使用 __attribute__ ( ( packed ) )
参考下面博客:
- C语言__attribute__的使用
3.1.1 使用 __attribute__ ( ( packed ) )
修改结构体
如下所示:
typedef struct // C语言__attribute__的使用 https://blog.csdn.net/qlexcel/article/details/92656797 // 使用该属性对struct 或者union 类型进行定义,设定其类型的每一个变量的内存约束。 // 就是告诉编译器取消结构在编译过程中的优化对齐(使用1字节对齐),按照实际占用字节数进行对齐,是GCC特有的语法。 // 这个功能是跟操作系统没关系,跟编译器有关,gcc编译器不是紧凑模式的 __attribute__ ( ( packed ) ) { unsigned char IdSize, MapType, ImageType; unsigned short PaletteStart, PaletteSize; unsigned char PaletteEntryDepth; unsigned short X, Y, Width, Height; unsigned char ColorDepth, Descriptor; } TGA_HEADER;
3.1.2 修改后的效果
改完之后,重新执行,正常运行。
断点调试,发现 ColorDepth
为 8
,所以正常加载了 TGA 图片
实现的效果如下所示:
3.1.3 分析结构体
打印出来的 sizeof ( TGA_HEADER ) 为 18,所以加载TGA文件成功。
2021-11-25 16:03:30.881 7080-7188/com.oyp.openglesdemo D/NDK_JNI_LOG_TAG: [GLUtils.cpp][esLoadTGA][203]: sizeof ( TGA_HEADER ) : { 18 }
3.2 使用 #pragma pack(1)
参考下面两篇博客:
- C/C++内存对齐详解
- #pragma的常用方法讲解
3.2.1 使用 #pragma pack(1)
修改代码
// 注意点:保证内存是连续的,不然读取错误 使用 #pragma pack(1) 或者 __attribute__ ( ( packed ) ) 都可以 // C/C++内存对齐详解 https://zhuanlan.zhihu.com/p/30007037 // #pragma的常用方法讲解 https://blog.csdn.net/weixin_39640298/article/details/84503428 #pragma pack(push,x1) // Byte alignment (8-bit) #pragma pack(1) // 如果前面加上#pragma pack(1),那么此时有效对齐值为1字节 typedef struct { unsigned char IdSize, MapType, ImageType; unsigned short PaletteStart, PaletteSize; unsigned char PaletteEntryDepth; unsigned short X, Y, Width, Height; unsigned char ColorDepth, Descriptor; } TGA_HEADER; #pragma pack(pop,x1)
3.2.2 修改后的效果
效果和 使用 __attribute__ ( ( packed ) )
修改结构体 的一样,这里不再重复描述
3.2.3 分析结构体
打印出来的 sizeof ( TGA_HEADER ) 为 18,所以加载TGA文件成功。
2021-11-25 16:11:12.936 7408-7542/com.oyp.openglesdemo D/NDK_JNI_LOG_TAG: [GLUtils.cpp][esLoadTGA][198]: sizeof ( TGA_HEADER ) : { 18 }
四、参考链接
参考下面博客:
- C/C++内存对齐详解
- #pragma的常用方法讲解
- C语言__attribute__的使用
这篇关于【我的C语言学习进阶之旅】关于C/C++内存对齐读取文件产生的问题以及解决方法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-17zero-shot-learning-definition-examples-comparison
- 2024-06-06Package Easy(基于 NSIS 的打包exe安装包工具)使用方法-icode9专业技术文章分享
- 2024-06-06基于 casdoor 的 ELK 开源登录认证解决方案: elk-auth-casdoor-icode9专业技术文章分享
- 2024-05-29Elasticsearch慢查询日志配置
- 2024-05-29揭秘华为如此多成功项目的产品关键——Charter模板
- 2024-05-29海外IDC业务拓展的7大挑战
- 2024-05-29InLine Chat功能优化对标Github Copilot,CodeGeeX带来更高效、更直观的编程体验!
- 2024-05-29CodeGeeX 智能编程助手 6 项功能升级,在Visual Studio插件市场霸榜2周!
- 2024-05-29AutoMQ 生态集成 Apache Doris
- 2024-05-292024年IDC行业的深度挖掘:机遇、挑战与未来展望