声明char变量导致TGA文件处理C程序崩溃
问题解答
一、程序崩溃原因及修复
你代码的崩溃根源不是char buffer[100]的声明,而是**main函数中的Image* image是未初始化的野指针**。
在main里,你仅声明了Image* image;但未给它分配内存(比如malloc(sizeof(Image))),也未指向实际的Image对象。调用image_load(image, "img1.tga")时,函数会通过这个野指针修改image->header、image->pixels等成员,属于非法内存访问。
之前未声明char buffer[100]时,栈内存布局刚好让野指针的非法读写未立刻触发崩溃;声明数组后栈布局改变,非法访问直接命中敏感区域,导致程序立刻崩溃。
修复方案
有两种可选方式:
- 声明栈上
Image对象,传递指针:
int main(int argc, char* argv[]){ Image image; // 栈上分配实际对象 Image letters[26]; image_load(&image, "img1.tga"); // 传递对象地址 load_letters(&letters, "font"); char buffer[100]; // 此时声明不会再崩溃 drawString(&image, &letters, "LOL", 5); image_create(&image, "image.tga"); free(image.pixels); // 注意使用.访问成员 image.pixels = NULL; for(int i=0; i<26; i++){ free(letters[i].pixels); letters[i].pixels = NULL; } return 0; }
- 动态分配
Image对象:
int main(int argc, char* argv[]){ Image* image = malloc(sizeof(Image)); // 分配堆内存 if(image == NULL){ perror("malloc failed"); return 1; } Image letters[26]; image_load(image, "img1.tga"); load_letters(&letters, "font"); char buffer[100]; drawString(image, &letters, "LOL", 5); image_create(image, "image.tga"); free(image->pixels); free(image); // 别忘了释放image本身 for(int i=0; i<26; i++){ free(letters[i].pixels); letters[i].pixels = NULL; } return 0; }
注:报错中memmove-vec-unaligned-erms.S找不到是调试器问题,它试图跳转至glibc源码但本地无对应文件,与程序崩溃的根本原因无关。
二、简化TGA_Header的读写方式
你当前逐个成员读写的方式过于繁琐,可直接读写整个结构体,但需注意结构体对齐和字节序两个核心问题:
1. 强制结构体无对齐
TGA头成员是紧密排列的(固定18字节),但编译器默认会给结构体加对齐字节,导致sizeof(TGA_Header)不等于实际头大小。可通过编译指令强制1字节对齐:
#pragma pack(push, 1) // 保存当前对齐规则,强制1字节对齐 typedef struct{ uint8_t idlength; uint8_t colourmaptype; uint8_t datatypecode; uint16_t colourmaporigin; uint16_t colourmaplength; uint8_t colourmapdepth; uint16_t x_origin; uint16_t y_origin; uint16_t width; uint16_t height; uint8_t bitsperpixel; uint8_t imagedescriptor; } TGA_Header; #pragma pack(pop) // 恢复原对齐规则
2. 直接读写整个结构体
修改readHeader和writeHeader为:
// 跨平台字节序转换工具函数 static uint16_t tga_le16toh(uint16_t val){ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return val; #else return ((val >> 8) & 0xFF) | ((val & 0xFF) << 8); #endif } static uint16_t tga_htole16(uint16_t val){ return tga_le16toh(val); } void readHeader(TGA_Header* header, FILE* input_F){ // 直接读取整个头结构体 fread(header, sizeof(TGA_Header), 1, input_F); // 将小端字节序的16位成员转为主机字节序 header->colourmaporigin = tga_le16toh(header->colourmaporigin); header->colourmaplength = tga_le16toh(header->colourmaplength); header->x_origin = tga_le16toh(header->x_origin); header->y_origin = tga_le16toh(header->y_origin); header->width = tga_le16toh(header->width); header->height = tga_le16toh(header->height); } void writeHeader(TGA_Header* header, FILE* output_F){ // 创建临时副本,将主机字节序转回小端 TGA_Header tmp = *header; tmp.colourmaporigin = tga_htole16(tmp.colourmaporigin); tmp.colourmaplength = tga_htole16(tmp.colourmaplength); tmp.x_origin = tga_htole16(tmp.x_origin); tmp.y_origin = tga_htole16(tmp.y_origin); tmp.width = tga_htole16(tmp.width); tmp.height = tga_htole16(tmp.height); // 写入整个临时结构体 fwrite(&tmp, sizeof(TGA_Header), 1, output_F); }
说明
- 字节序转换函数保证了代码的跨平台兼容性,避免不同端序系统下的解析错误。
- 此方式大幅简化了头读写的代码量,同时保留了原有的功能完整性。
内容的提问来源于stack exchange,提问作者PHOENIX33201
相关产品推荐
相关产品推荐

