矩阵加载断言失败求助:mat_load函数magic值匹配断言错误
问题现象
使用mat_save将矩阵保存到文件后,调用mat_load加载时触发断言失败,错误信息如下:
Assertion failed: magic == 0x74616d2e682e6e6e, file ../Headers/nn.h, line 159
相关代码片段
矩阵结构体定义
typedef struct { size_t rows; size_t cols; size_t stride; float *es; } Mat;
矩阵内存分配函数
Mat mat_alloc(size_t rows, size_t cols) { Mat m; m.rows = rows; m.cols = cols; m.stride = cols; m.es = NN_MALLOC(sizeof(*m.es)*rows*cols); NN_ASSERT(m.es != NULL); return m; } // 使用示例 Mat t = mat_alloc(img_width*img_height, 3);
注:NN_ASSERT对应标准assert,NN_MALLOC对应标准malloc
矩阵保存函数及调用
const char *out_file_path = "img.txt"; FILE *out = fopen(out_file_path, "w"); mat_save(out, t); void mat_save(FILE *out, Mat m) { const char *magic = "nn.h.mat"; fwrite(magic, strlen(magic), 1, out); fwrite(&m.rows, sizeof(m.rows), 1, out); fwrite(&m.cols, sizeof(m.cols), 1, out); for (size_t i = 0; i < m.rows; ++i) { size_t n = fwrite(&MAT_AT(m, i, 0), sizeof(*m.es), m.cols, out); while (n < m.cols && !ferror(out)) { size_t k = fwrite(m.es + n, sizeof(*m.es), m.cols - n, out); n += k; } } } #define MAT_AT(m, i, j) (m).es[(i)*(m).stride + (j)]
矩阵加载函数
Mat mat_load(FILE *in) { uint64_t magic; fread(&magic, sizeof(magic), 1, in); NN_ASSERT(magic == 0x74616d2e682e6e6e); size_t rows, cols; fread(&rows, sizeof(rows), 1, in); fread(&cols, sizeof(cols), 1, in); Mat m = mat_alloc(rows, cols); size_t n = fread(m.es, sizeof(*m.es), rows*cols, in); while (n < rows*cols && !ferror(in)) { size_t k = fread(m.es, sizeof(*m.es) + n, rows*cols - n, in); n += k; } return m; }
问题原因分析
文件打开模式错误:
保存时使用"w"文本模式,加载时若使用"r"文本模式,在Windows系统下会自动处理换行符(如\n转\r\n),即使魔数字符串无换行符,文本模式也可能修改字节流,导致读取的魔数与写入值不匹配。魔数读写方式不匹配:
保存时写入ASCII字符串"nn.h.mat",但加载时直接将8字节数据转为uint64_t整数。该方式依赖系统字节序,若系统为大端字节序或文件字节流被修改,就会触发断言失败。加载函数读取参数错误:
mat_load中补全读取的代码参数错误:fread(m.es, sizeof(*m.es) + n, ...)的第二个参数应为单个元素大小sizeof(*m.es),而非sizeof(*m.es)+n,会导致数据加载异常。保存函数行数据写入错误:
mat_save循环内补全写入时,错误地从数组全局第n个元素开始,而非当前行的第n个元素,会导致矩阵数据存储错乱。
解决方法
使用二进制模式打开文件:
保存和加载均使用二进制模式,避免文本模式的字节转换:// 保存时 FILE *out = fopen(out_file_path, "wb"); // 加载时 FILE *in = fopen(in_file_path, "rb");统一魔数读写方式:
直接写入uint64_t类型的魔数值,避免字节序依赖:// 修改mat_save的魔数写入部分 const uint64_t magic = 0x74616d2e682e6e6e; fwrite(&magic, sizeof(magic), 1, out);修复加载函数的读取参数:
将补全读取的代码修改为:size_t k = fread(m.es + n, sizeof(*m.es), rows*cols - n, in);修复保存函数的行数据写入逻辑:
补全写入时从当前行的未写入位置开始:size_t k = fwrite(&MAT_AT(m, i, n), sizeof(*m.es), m.cols - n, out);
内容的提问来源于stack exchange,提问作者Botodach

