Windows10 Qt环境C++调用libjpeg-turbo写jpeg时出现段错误求助
问题根因分析
1. 最可能原因:传入的图像数据容量/参数不匹配
你观察到的行尾乱码、固定15次循环后崩溃是典型的内存越界访问特征:
- 首先在函数入口必须做参数校验:传入的
std::vector<unsigned char>的实际大小必须 ≥w * h * 3,如果实际容量不足,循环到第16行时指针就会访问到vector分配范围之外的非法内存,直接触发段错误。你看到的行尾的encoded/autoder等乱码字符,就是指针飘到了程序其他内存区域(比如常量字符串存储区)读取到的脏数据。 - 如果你传入的RGB数据是从
QImage转换而来,不要自行计算row_stride = w *3:Qt的图像默认做4字节对齐,宽度不是4的倍数时,每行末尾会有填充字节,必须用QImage::bytesPerLine()获取每行实际的字节数作为步长,否则逐行偏移的误差会累积,到后面必然越界。
2. 次要可能原因:libjpeg-turbo库适配问题
Windows平台下如果库的编译环境和Qt的编译环境不匹配,也会触发莫名其妙的内存错误:
- 确认你用的libjpeg-turbo库和Qt的编译器套件一致:MSVC编译的库不能给MinGW的Qt项目链接,反之亦然
- 确认库编译时没有修改
JSAMPLE的默认大小(默认是1字节,如果编译时改为2字节,所有指针偏移都会出错)
调试技巧
- 先加硬校验:在
writeJpeg函数入口先打印image.size()和w*h*3的数值,两者不匹配直接返回报错,不要继续执行 - 隔离验证:自己构造一个测试用的标准vector,比如设置
w=10、h=10,手动填充10*10*3个255,调用这个函数如果能正常生成全白JPEG,就说明函数本身没有问题,问题出在你传入的image数据或者w、h参数错误 - 开启地址消毒器:Qt Creator的项目构建配置中,给编译flag加上
-fsanitize=address -g,运行后会直接精准定位到内存越界的具体代码位置,比单步打印效率高很多 - 对齐校验:如果数据源是QImage,打印
bytesPerLine()的返回值和你自行计算的w*3对比,数值不一致就说明存在对齐填充,必须用实际的行字节数作为步长
临时验证修复代码
#include <cassert> void writeJpeg(const char *filename, std::vector<unsigned char> &image, uint w, uint h, int rowPitch = 0) { // 入口强制校验 assert(image.size() >= w * h * 3); // 没有传入实际行步长就用默认计算值 if (rowPitch == 0) rowPitch = w * 3; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; FILE *outfile; JSAMPROW row_pointer[1]; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); if ((outfile = fopen(filename, "wb")) == NULL) { fprintf(stderr, "can't open %s\n", filename); jpeg_destroy_compress(&cinfo); return; } jpeg_stdio_dest(&cinfo, outfile); cinfo.image_width = w; cinfo.image_height = h; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, 100, TRUE); jpeg_start_compress(&cinfo, TRUE); unsigned char* arr = image.data(); while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = arr + cinfo.next_scanline * rowPitch; jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); fclose(outfile); jpeg_destroy_compress(&cinfo); }
如果是QImage转存的场景,调用时传入bytesPerLine()即可:
writeJpeg("test.jpg", imageVec, qimg.width(), qimg.height(), qimg.bytesPerLine());
内容的提问来源于stack exchange,提问作者Ethan Armstrong
相关产品推荐
相关产品推荐

