【问题标题】:diffent image file size causes segmentation fault when memory is freed释放内存时,不同的图像文件大小会导致分段错误
【发布时间】:2023-03-03 10:54:01
【问题描述】:

我正在学习如何在 OpenGL 中添加纹理,出于教育目的,我决定不使用“stb_image.h”之类的库来加载图像,而是为 .bmp 图像编写自己的库。

我使用的图片尺寸为 1920x1080(全高清),文件大小为 6.2 MB(6,220,922 字节)。在加载图像并将其从蓝色、绿色、红色 (BGR) 转换为红色、绿色、蓝色 (RGB) 后,我决定释放 BGR 版本,但在这样做时发生了“分段错误(核心转储)”。

我尝试过的其中一个方法是使用不同的图像,这会导致错误消失。 新的图像分辨率为 1280x1920,文件大小为 7.4 MB(7,372,922 字节)。

我决定翻转早期图像的尺寸(从 1920x1080 到 1080x1920),“分段错误(核心转储)”又回来了。

这是 C 中的相关代码(删除了所有 OpenGL 和 GLFW 的东西)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

typedef struct 
{
   uint16_t type;
   uint32_t size;
   uint16_t reserved1;
   uint16_t reserved2;
   uint32_t offbit;
}BITMAPFILEHEADER;

typedef struct {
    uint32_t size;
    uint32_t width;
    uint32_t height;
    uint16_t planes;
    uint16_t bitcount;
    uint32_t comprestiontype;
    uint32_t sizeimage;
    uint32_t xres;
    uint32_t yres;
    uint32_t crlused;
    uint32_t crlimportant;
}BITMAPINFOHEADER;

typedef struct{
    uint8_t r;
    uint8_t g;
    uint8_t b;
}RGB;

void ReadFileHeader(FILE* file, BITMAPFILEHEADER* fileHeader){
    fread(&(fileHeader->type),2,1,file);
    fread(&(fileHeader->size),4,1,file);
    fread(&(fileHeader->reserved1),2,1,file);
    fread(&(fileHeader->reserved2),2,1,file);
    fread(&(fileHeader->offbit),4,1,file);
}

void ReadInfoHeader(FILE* file, BITMAPINFOHEADER* fileHeader){
    fread(&(fileHeader->size),4,1,file);
    fread(&(fileHeader->width),4,1,file);
    fread(&(fileHeader->height),4,1,file);
    fread(&(fileHeader->planes),2,1,file);
    fread(&(fileHeader->bitcount),2,1,file);
    fread(&(fileHeader->comprestiontype),4,1,file);
    fread(&(fileHeader->sizeimage),4,1,file);
    fread(&(fileHeader->xres),4,1,file);
    fread(&(fileHeader->yres),4,1,file);
    fread(&(fileHeader->crlused),4,1,file);
    fread(&(fileHeader->crlimportant),4,1,file);
}


unsigned char* ReadBMP(const char * p_path, int* width, int* hight){
    FILE* image = fopen(p_path,"rb");
    if(image == NULL){
        printf("file not found\n");
    }
    BITMAPFILEHEADER file_header;
    BITMAPINFOHEADER info_header;

    ReadFileHeader(image,&file_header);
    ReadInfoHeader(image,&info_header);

    if (width != NULL)
    {
        *width = info_header.width;
    }
    
    if (hight != NULL)
    {
        *hight = info_header.height;
    }

    unsigned char* pixelData = (unsigned char*)malloc(sizeof(RGB)*info_header.width*info_header.height);
    fread(pixelData,sizeof(RGB),info_header.width*info_header.height,image);
    fclose(image);
    return pixelData;
}

unsigned char* BmpToRgb(unsigned char* input, int width,int hight){
    
    RGB* data = (RGB*)malloc(width*hight*3);
    int y = 0;
    int x = 0;
    memcpy(data,input,width*hight*3);
    for (y = 0; y < hight; y++)
    {
        for (x = 0; x < width; x++)
        {

        // the problem is probably in these 3 lines, if i comment
        // them out no "Segmentation fault (core dumped)" happens.
        data[x+(hight-y)*width].r = input[x+y*width+2];
            
        data[x+(hight-y)*width].g = input[x+y*width];
            
        data[x+(hight-y)*width].b = input[x+y*width+1];

        }
    }
    
    return (unsigned char*)data; 
}

int main(){

    int width;
    int hight;

    unsigned char* imageBMP = ReadBMP("image.bmp",&width,&hight);
    printf("width: %d hight: %d\n",width,hight);
    unsigned char* image = BmpToRgb(imageBMP,width,hight);

    free(image);
    printf("freeing\n");
    free(imageBMP); // segmentation fault (core dumped) for 1920x1080 images
    printf("done\n"); // "done" not printed

    return 0;
}

我想知道为什么不同的图像分辨率(或者可能是文件大小)可能导致“分段错误(核心转储)”。

一些可能相关的额外信息:

Operating system : Ubuntu 20.04  
c compiler: gcc

【问题讨论】:

  • 首先,您需要使用调试器来捕获崩溃并定位代码中发生崩溃的时间和位置。我还推荐使用 Valgrind 等工具来帮助您找到此类问题。
  • 还有RGB结构might not be what you expect的大小,在这种情况下你没有分配足够的内存。
  • struct RGB的大小是3字节,没有struct padding,我查了。
  • data[x+(hight-y)*width].r = input[x+y*width+2];y == 0 写入超过分配的data 缓冲区的末尾。 input 索引看起来也不正确。
  • 附带说明,“width”和“hight”局部变量应定义为 uint32_t (unsigned int) 而不是 int。否则你可能会面临整数溢出。

标签: c


【解决方案1】:

问题在于这些行

data[x+(hight-y)*width].r = input[x+y*width+2];
            
data[x+(hight-y)*width].g = input[x+y*width];

data[x+(hight-y)*width].b = input[x+y*width+1];

y = 0data 的缓冲区溢出时。这会使代码进入未定义的行为状态(https://en.cppreference.com/w/c/language/behavior.

为了解决这个问题,将上面显示的三行替换为以下内容

data[x+(hight-y-1)*width].r = input[x+y*width+2];
            
data[x+(hight-y-1)*width].g = input[x+y*width];

data[x+(hight-y-1)*width].b = input[x+y*width+1];

【讨论】:

    猜你喜欢
    • 2020-03-14
    • 2021-08-23
    • 2021-05-12
    • 2012-06-15
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    • 2013-08-01
    • 1970-01-01
    相关资源
    最近更新 更多