ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

【我的C语言学习进阶之旅】关于C/C++内存对齐读取文件产生的问题以及解决方法

2021-11-25 16:32:39  阅读:222  来源: 互联网

标签:__ 进阶 unsigned C++ C语言 char HEADER TGA GL


一、问题描述

今天在使用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函数返回的buffernullptr 所以报错了。

  • 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.ColorDepth87 'W',既不等于8,也不等于24,还不等于32,所以最终返回了nullptr

2.2 为啥呢?

原来产生这个问题的原因和C/C++内存对齐有关。这里,读者可以参考下面的文章了解。

我在esLoadTGA函数里面有两句代码,如下所示,在读取TGA文件之前,打印了TGA_HEADERsizeof

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 ) )

参考下面博客:

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 修改后的效果

改完之后,重新执行,正常运行。

断点调试,发现 ColorDepth8 ,所以正常加载了 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)

参考下面两篇博客:

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 }

在这里插入图片描述

四、参考链接

参考下面博客:

标签:__,进阶,unsigned,C++,C语言,char,HEADER,TGA,GL
来源: https://blog.csdn.net/qq446282412/article/details/121539330

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有