记录C++学习过程中遇到一些调试、编译问题及当时的解决方法,方便日后查阅。
1、0xC0000005: 读取位置0x00000038时发生访问冲突
问题描述:
0xC0000005: 读取位置0x00000038时发生访问冲突,如下图,在进行读文件时发生异常。
解决方法:
主要代码(修改前):
for (int i = 1; i <= nBandCount;++i)
{
// GDAL内波段存储顺序为RGB,需要转换为我们一般的BGR顺序,即低地址到高地址为:B,G,R
unsigned char *pTemp = m_pImgData + i - 1;
GDALRasterBand *pBand = poSrcDataset->GetRasterBand(nBandCount + i - 1);
pBand->RasterIO(GF_Read, 0, 0, m_imgWidth, m_imgHeight, pTemp, m_imgWidth, m_imgHeight, GDT_Byte, 3, 0);
}
分析:关于内存访问的异常,先看是否有野指针以及是否有非法访问(比如越界访问等),先在中断的地方加断点,单步调试进一步缩小出错的位置,然后再调试找解决方法。
我这里发现,中断发生在该for循环结束的地方,说明是for循环里发生的错误,于是我在for循环开始大括号那行加了断点,调试时nBandCount=3,发现第一次循环正常结束,第二次循环结束时便中断了。考虑到可能是访问越界的问题,重点看了下循环变量i相关的语句,便找到了问题所在:nBandCount+i-1,当i=2时该表达式值为4,而我的程序里当前只有三个波段,因此出错,正确的应该是nBandCount - i + 1。 果然还是越界访问了,只修改这一个地方就行,修改后问题解决。
2、GDAL写文件后不能及时关闭数据流
问题描述:
利用GDAL进行影像文件写操作,点击“另存为”,输入待保存的文件名之后点“确定”(写操作结束),此时在输出目录里也能看到新产生的图像文件,但该新产生的图像文件却无法打开(如下图所示),看其文件大小也跟预期的实际大小有差别。等程序完全退出后,该图像文件才正常显示出来。
分析:
既然程序退出后文件能正常保存下来,说明本身文件的写操作没问题,猜想极有可能是“另存为”后,文件的写入数据流未能及时的关闭,导致数据仍存在内存中,只等到最后程序关闭释放内存时才一下子把内存中数据流入本地文件中。
因为是GDAL写文件操作,函数结尾前也进行了GDALDateset的关闭工作,但写函数中也使用了GDALRasterBand,还怀疑是否因为没关闭GDALRasterBand导致,查了下GDAL帮助文档,发现这样一句话:
“Please keep in mind that GDALRasterBand objects are owned by their dataset, and they should never be destroyed with the C++ delete operator. GDALDataset's can be closed by calling GDALClose()...” (注意GDALRasterBand对象归它们的GDALDataset数据集所拥有,它们-即GDALRasterBand-在C++中不能用delete 运算符进行销毁,只需要将它们所归属的GDALDataset数据集进行销毁即可,而对于GDALDataset的销毁,也不建议使用delete运算符,而是用GDAL封装好的GDALClose()函数关闭该数据集对象就可以了。)
因此,此处不用管GDALRasterBand的关闭。那这个问题是什么原因呢?
我的写函数用到了CreateCopy()函数,再次看帮助文档(Note that the CreateCopy() method returns a writeable dataset, and that it must be closed properly to complete writing and flushing the dataset to disk.),发现其中有个CreateCopy()的例子,便试着仿照它的思路调整了下代码,发现可行,问题解决,具体见下面代码。
解决方法:
修改前代码:
poDstDriver->CreateCopy(lpszPathName, poDstDataset, FALSE, NULL, NULL, NULL);
GDALClose((GDALDatasetH)poDstDataset);
修改后代码:
GDALDataset *poTempDataset; // 临时数据集
poTempDataset = poDstDriver->CreateCopy(lpszPathName, poDstDataset, FALSE, NULL, NULL, NULL);
/* Once we're done, close properly the dataset */
if (poTempDataset != NULL)
{
GDALClose((GDALDatasetH)poTempDataset);
}
GDALClose((GDALDatasetH)poDstDataset);
通过比较上述代码,其实也只是新建了一个临时的数据集(poTempDataset)做为接收CreateCopy()的返回值,并及时将其关闭。
3、去掉MDI启动时的子窗口。
问题描述:
用MFC应用程序向导创建MDI多文档应用程序时,运行程序后会自动产生一个子窗口,如何让该默认子窗口不自动显示呢?
分析:
MFC的应用程序类里封装了该应用程序在启动时的初始化、运行和终止等任务,对应于InitInstance、Run和ExitInstance函数。修改其中相应代码即可。
解决方法:
修改应用程序类(CxxxApp.cpp)中的InitInstance()函数,定位到CCommandLineInfo cmdInfo行,删除其后面一行,再添加一行代码即可。修改后代码如下:
CCommandLineInfo cmdInfo;
//ParseCommandLine(cmdInfo); // 删除这一行
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing; // 添加这一行
4、error LNK2019: 无法解析的外部符号 “*******“ 该符号在函数中被引用
问题描述:
分析:
该情况较常见,这里可能原因是:函数(DownSample())只找到了声明,但未找到其实现。
去相应类中找了下,发现该方法已经作为一个未暴露接口的方法实现(即函数前未添加类名标识--类名::),又去头文件中看了下,才发现头文件中原来已经对其进行了声明,因此才会报”找不到其实现“,这种情况的解决方法也很简单,直接在.cpp文件中该类的实现前添加类名标识即可。
解决方法:
将
void DownSample(MImage srcData, MImage& dstData){...}
改为:
void Sift::DownSample(MImage srcData, MImage& dstData){...}
5、控制台停留(不要一闪而过)的解决方法:
#include <stdlib.h> main函数中加上 system("pause"); 即可。