C++读取MNIST时array、cv::Mat、vector的sizeof差异及函数返回异常问题
我正在尝试读取MNIST文件,并将元素存入存储matrix的vector中,可参考公开的MNIST读取代码实现。
我的代码片段如下:
for(int r = 0; r < n_rows; ++r) { for(int c = 0; c < n_cols; ++c) { unsigned char temp = 0; file.read((char*)&temp, sizeof(temp)); data[r][c] = temp; //std::cout << "r: " << r << " c: " << c << " temp: "<< sizeof(temp) << "\n"; } } // 此处打印数组输出正常 for(int k = 0; k < 28; k++) { for(int z = 0; z < 28; z++) { std::cout << data[k][z] << " "; if(z == 27) std::cout << "\n"; } } cv::Mat img(28,28,CV_8U,&data); mnist.push_back(img); std::cout<<"data: "<< sizeof(data) << " img: "<< sizeof(img) << " mnist: " << sizeof(mnist) <<"\n";
上述代码最后一行的输出如下:
data(array): 784 img(cv::Mat): 96 mnist(vector of matrix): 24
它们的大小难道不应该至少一致吗?我认为这就是打印matrix时无法得到和array相同的正确输出的原因。我猜测vector引用了matrix,而matrix又引用了array,后续内存某处发生了修改,导致输出不符合预期?
补充说明
上述代码写在一个返回matrix vector的函数中。
如果我把这段代码直接写在main函数里,输出就是正常的!
有没有人可以解释这个问题?我更希望把这段逻辑拆到独立函数中,而不是让main函数过于臃肿,不过目前我还是先使用可以正常运行的写法。
1. sizeof输出差异的原因
你观察到的sizeof输出差异是完全正常的,sizeof是编译期运算符,返回的是变量对应类型本身占用的栈内存大小,不会统计其内部指针指向的堆内存数据体积:
data是28*28的unsigned char栈数组,类型本身固定占784字节,所以sizeof返回784cv::Mat是封装了图像数据指针、尺寸、通道数等信息的类,64位系统下该类的实例本身固定占96字节,实际的像素数据存储在堆上,不会被计入sizeof结果std::vector本身仅保存三个管理内存的指针,64位系统下固定占24字节,内部存储的所有元素都存在堆上,也不会被sizeof统计
2. 函数运行异常、main运行正常的原因
你使用的cv::Mat img(28,28,CV_8U,&data);构造函数采用零拷贝引用逻辑:Mat不会复制data数组中的像素值,仅会保存指向data数组内存的指针。
如果这段代码写在独立函数中,data是函数的局部栈变量,函数执行结束返回后,栈内存会被系统自动回收,此时vector中存储的Mat持有的就是野指针,访问时会读到乱码,甚至触发崩溃。
而代码写在main函数中时,data的生命周期和整个程序运行周期一致,Mat引用的内存始终有效,所以运行正常。
3. 拆分到独立函数的解决方案
创建Mat时强制做一次数据拷贝即可,修改构造语句为:
cv::Mat img(28,28,CV_8U, &data).clone();
clone()方法会把data里的像素值拷贝到Mat自行管理的堆内存中,和原来的data数组完全解绑,就算函数执行完data被释放,Mat里的数据也不会受影响。
内容的提问来源于stack exchange,提问作者Pat cm pro

