You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

声明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]时,栈内存布局刚好让野指针的非法读写未立刻触发崩溃;声明数组后栈布局改变,非法访问直接命中敏感区域,导致程序立刻崩溃。

修复方案

有两种可选方式:

  1. 声明栈上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;
}
  1. 动态分配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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 23:45:54