程序在运行时发生崩溃,如何查找错误出现在哪里

⌚Time: 2025-06-04 19:42:00

👨‍💻Author: Jack Ge

有时候程序错误出现了直接崩溃,不知道错误在哪里,编写代码的时候错误处理不好,没有直接输出错误位置。这就造成了麻烦。

如果是MSVC的话,调试模式在崩溃时直接定位到错误位置,MINGW开发的,用GDB调试工具也可以定位错误。

我还有2个通过修改代码查找问题所在的方法:

方法1

选定一个可能的函数的大概范围,不确定什么函数就向上级找。最多找到主函数main。随便插入几个输出语句

    void traverse_files_and_dirs_unicode(std::wstring sDir){
        if(m_isStopTraverse){
            return;
        }
        std::cout<<"111111111111111111\n";
        HANDLE file;    
        WIN32_FIND_DATAW fileData;
        file = FindFirstFileW((sDir+L"*").c_str(), &fileData);
        if (file != INVALID_HANDLE_VALUE)
        {
            std::cout<<"222222222222\n";
            if(wcscmp(fileData.cFileName,L".") == 0||wcscmp(fileData.cFileName,L"..") == 0){
                ;   
            }else if(fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
                std::string str324 = UnicodeToUTF8(sDir+fileData.cFileName+L"\\");
                m_dirProcesser(str324);
                traverse_files_and_dirs_unicode(sDir+fileData.cFileName+L"\\");
            }else{
                std::string str325 = UnicodeToUTF8(sDir+fileData.cFileName);
                m_fileProcesser(str325);
            }
            bool bState = false;
            bState = FindNextFileW(file, &fileData);    
            while(bState&&!m_isStopTraverse){
                std::cout<<"3333333333333\n";
                if(wcscmp(fileData.cFileName,L".") == 0||wcscmp(fileData.cFileName,L"..") == 0){
                    ;   
                }else if(fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
                    std::string str326 = UnicodeToUTF8(sDir+fileData.cFileName+L"\\");
                    m_dirProcesser(str326);
                    traverse_files_and_dirs_unicode(sDir+fileData.cFileName+L"\\");
                }else{
                    std::string str327 = UnicodeToUTF8(sDir+fileData.cFileName);
                    m_fileProcesser(str327);
                }
                bState = FindNextFileW(file, &fileData);    
                std::cout<<"44444444444444\n";
            }
        }else{
            ;
        }
        
        std::cout<<"55555555555555555\n";
        FindClose(file);
    }

得到下面的输出,没有完整的输完插入的语句就报错,说明找对了。


55555555555555555
44444444444444
3333333333333
44444444444444
3333333333333
44444444444444
3333333333333
terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_M_construct null not valid

--------------------------------
Process exited after 10.18 seconds with return value 3

Press ANY key to exit...

在[3]后面卡住,之后在原先[3]和[4]之间插入更多语句

            while(bState&&!m_isStopTraverse){
                std::cout<<"1111111111\n";
                if(wcscmp(fileData.cFileName,L".") == 0||wcscmp(fileData.cFileName,L"..") == 0){
                    
                    std::cout<<"222222222222\n";  
                }else if(fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
                    std::cout<<"3333333333333\n";
                    std::string str326 = UnicodeToUTF8(sDir+fileData.cFileName+L"\\");
                    m_dirProcesser(str326);
                    traverse_files_and_dirs_unicode(sDir+fileData.cFileName+L"\\");
                    std::cout<<"4444444444444\n";
                }else{
                    std::cout<<"55555555555\n";
                    std::string str327 = UnicodeToUTF8(sDir+fileData.cFileName);
                    m_fileProcesser(str327);
                    std::cout<<"66666666666666\n";
                }
                bState = FindNextFileW(file, &fileData);    
                std::cout<<"777777777777777\n";
            }

输出表示在[3]后面崩溃,没有运行到[4],中间只有2个执行的语句,递归自己的不算。

66666666666666
777777777777777
1111111111
3333333333333
terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_M_construct null not valid

--------------------------------
Process exited after 10.55 seconds with return value 3

Press ANY key to exit...

最后是发现m_dirProcesser(str326);这个函数的问题,之后再进入函数,再用这个办法找。

最后在这个函数里就是一个小问题,使用了g_locale_to_utf8转换字符编码,因为之前自己调整了一部分函数编码,造成这个函数接收的字符串本来就是utf8编码,再次转换会返回空指针,std::string赋值就造成了崩溃,直接去掉这个代码就正常了。

gchar *utf8Char = g_locale_to_utf8(dirPath.c_str(),-1,0,0,0);
std::string dirPathUTF8 = utf8Char;
g_free(utf8Char);

这种查找过程非常简单。

方法2

屏蔽模块:屏蔽就是直接去掉对该模块的使用,或者是编写一个绝对不会出错的简单的欺骗模块,没有实际功能的模块,对一些模块进行替换。

比如一个函数A,进行了一些计算返回一个值。

int functionA(){
    ...
}

直接用一个返回固定值的欺骗模块B进行替换。

int functionB(){
    return 13;
}

在调用的时候,就等于对模块A屏蔽了

//int a = functionA();
int a = functionB();

1.直接屏蔽一个或者多个模块的执行,如果正常了,就说明那些模块有问题。如果不正常,屏蔽更多的模块。

2.屏蔽到运行正常之后,缩小屏蔽的模块范围,最后定位到1个模块,这个模块屏蔽了就正常,不屏蔽就崩溃,之后进入那个模块。

3.进入那个模块。继续按照上面的办法屏蔽里面的内容,最后问题范围会缩小到一个很小的范围,几条代码,几个变量,肯定就知道问题原因了。

这个办法也是很有用的,我叫他问题屏蔽法。

总结

要做好错误处理,检查空指针,用try...catch块,在处理错误的时候要输出错误位置。

如果我当初这样写就不会崩溃了,直接打印错误结果和位置。

    gchar *utf8Char;
    std::string dirPathUTF8;
    utf8Char = g_locale_to_utf8(dirPath.c_str(),-1,0,0,0);
    if(NULL!=utf8Char){
        dirPathUTF8 = utf8Char;
        g_free(utf8Char);
    }else{
        std::cout<<"Error:"<<__FILE__<<" "<<__LINE__<<std::endl;
    }
Error:D:/ProjectSpace/reddevcpp/FileMemory/memorycreater.cpp 43

通用问题查找

根据我的经验,c/cpp程序运行崩溃。通常就是这些情况: