堆栈在递归函数中溢出

时间:2012-10-16 19:43:53

标签: c++ winapi

我正在编写一个简单的应用程序,将某些目录中的所有文件输出到控制台。为此,我在函数PathCreator()中动态分配内存并返回指向此内存的指针。我不知道如何正确释放GetAllFiles()中的内存段。当我使用下面的代码时,我得到一个堆栈溢出异常。我怎样才能解决这个问题?请不要让我使用不需要动态分配内存的东西,我只是想修复我的代码。

#include "stdafx.h"
#include <windows.h>
#include <iostream>
wchar_t *PathCreator(wchar_t *dir, wchar_t *fileName);
int is_directory(wchar_t *p)
{
    wchar_t *t = PathCreator(p,L"\\");
    WIN32_FIND_DATA file;
    HANDLE search_hendle = FindFirstFile(t, &file);
    long error = GetLastError();
    if(error == 267)
    {
        return 0;
    }
    else
    {
        return 1;
    }
}

wchar_t *PathCreator(wchar_t *dir, wchar_t *fileName)
{
    wchar_t* path = 0;
    int size = 0;
    wchar_t *d = dir;
    wchar_t *f = fileName;
    while(*d != '\0')
    {
        d++;
        size++;
    }
    while(*f != '\0')
    {
        f++;
        size++;
    }
    path = new wchar_t[(size+=3) * sizeof(wchar_t)];
    int j = 0;
    while(j < size)
    {
        path[j] = '\0';
        j++;
    }
    int i;
    i = 0;
    while(*dir != '\0')
    {
        path[i] = *dir;
        i++;
        dir++;
    }
    path[i++] = '\\';
    wchar_t *t = fileName;  
    while(*t != '\0')
    {
        path[i] = *t;
        i++;
        t++;
    }
    path[i] = '\0';
    return path;
} 

void GetAllFiles(wchar_t* dir)
{
    wchar_t *p = 0;

    int i = 0;
    WIN32_FIND_DATA file;
    wchar_t *t = PathCreator(dir, L"*");
    HANDLE search_hendle = FindFirstFile(t, &file);
    if(search_hendle)
    {

        do
        {
            p = PathCreator(dir,file.cFileName);
            if(!is_directory(p))
            {
                std::wcout << p << std::endl;
            }
            else
            {
                GetAllFiles(p);
            }
            delete [] p;
        }
        while(FindNextFile(search_hendle, &file));

    }
    delete [] t;
    FindClose(search_hendle);
}


int _tmain(int argc, _TCHAR* argv[])
{
    GetAllFiles(L"C:\\Users");
}

2 个答案:

答案 0 :(得分:8)

所以,你有“。”和目录搜索中的“..”。

第一个条目是“。”,所以:

p = PathCreator(dir, file.cFilename)

的产率:

"C:\Users\."

然后是下一行:

if (!is_directory(p))

总是假的,所以它只是不断递归:

GetAllFiles(p)

永远...或直到你的筹码爆炸,以先发生者为准; - )

我建议明确检查“。”和“..”并跳过这些条目(也是MFC和Qt等有很好的目录处理类,但我想你想这样做)。

我的修改:

    do
    {

        // I added this - guess I can't embolden code text
        if (wcscmp(file.cFileName,L".") == 0 || wcscmp(file.cFileName,L"..")==0)
            continue;

        p = PathCreator(dir,file.cFileName);
        if(!is_directory(p))
        {
            std::wcout << p << std::endl;
        }
        else
        {
            GetAllFiles(p);
        }
        delete [] p;
    }
    while(FindNextFile(search_hendle, &file));

答案 1 :(得分:2)

您再次尝试使用C代替C++而您仍在使用wcout?!没问题,你是程序员,我相信你有理由这样做!但是C中的内存管理比C++要难得多,你应该有一些使用它的技能。这是一个完全正常工作的代码,但正如您所看到的那样,使用标准容器和字符串比使用C ++版本更难管理,使用和理解,因此如果您被允许使用C ++(当您使用wcout时),那么请使用它的C ++版本易于使用:

#include <Windows.h>
/*! \brief Merge \a folder and \a filename into a newly allocate memory and
 * return it to the caller. Use free to free returned memory!
 */
wchar_t* PathCreator( wchar_t const* folder, wchar_t const* filename )
{
    wchar_t* res;
    size_t i, len, folderLen = wcslen( folder ), filenameLen = wcslen( filename );
    len = folderLen + filenameLen;
    if( folder[folderLen - 1] != '\\' ) ++len;
    ++len;  // for \0

    res = (wchar_t*) malloc( sizeof(wchar_t) * len );
    if( !res ) return NULL;

    wcscpy_s( res, len, folder );
    /* Remove possible wide card at end of folder */
    for( i = folderLen; i--; ) {
        if( res[i] == '*' || res[i] == '?' ) {
            res[i] = 0;
            --folderLen;
        } else {
            break;
        }
    }
    if( res[folderLen - 1] != '\\' ) wcscat_s( res, len, L"\\" );
    wcscat_s( res, len, filename );

    return res;
}
/*! \brief Free memory that returned by \ref GetAllFiles
 */
void FreeAllFilesMemory( wchar_t** p )
{
    wchar_t** tmp = p;
    if( !p ) return ;
    while( *tmp ) free( *tmp++ );
    free( p );
}
wchar_t** AddToArray( wchar_t** p, size_t* pAllocated, size_t* pUsed, wchar_t* s )
{
    if( *pUsed >= *pAllocated ) {
        size_t newAlloc = *pAllocated * 3 / 2;  // Grow by 1.5
        if( newAlloc < 16 ) newAlloc = 16;
        p = (wchar_t**) realloc( p, newAlloc * sizeof(wchar_t*) );
        if( !p ) return NULL;
        *pAllocated = newAlloc;
    }

    p[*pUsed] = s;
    ++*pUsed;
    return p;
}
wchar_t** GetAllFilesImpl( wchar_t const* folder, wchar_t** res, size_t* pAllocated, size_t* pUsed )
{
    HANDLE hSearch;
    WIN32_FIND_DATAW fileinfo;
    size_t allocatedMemory = 0;

    hSearch = FindFirstFileW( folder, &fileinfo );
    if( hSearch != INVALID_HANDLE_VALUE ) {
        do {
            wchar_t* sFileName, ** tmp, sTmp[ 1024 ];
            /* ignore ., .. */
            if( !wcscmp(fileinfo.cFileName, L".") ||
                !wcscmp(fileinfo.cFileName, L"..") )
                continue;
            sFileName = PathCreator( folder, fileinfo.cFileName );
            wprintf( L"%s\n", sFileName );  /* Print result */
            tmp = AddToArray( res, pAllocated, pUsed, sFileName );
            if( !tmp ) return FreeAllFilesMemory(res), NULL;
            res = tmp;
            if( fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
                wcscpy_s( sTmp, sFileName );
                wcscat_s( sTmp, L"\\*" );
                tmp = GetAllFilesImpl( sTmp, res, pAllocated, pUsed );
                if( !tmp ) return NULL;
                res = tmp;
            }
        } while( FindNextFileW(hSearch, &fileinfo) );
        FindClose( hSearch );
    }
    return res;
}
/*! \brief List all files that match a pattern and return it as an array of
 * wide strings, free result using \ref FreeAllFilesMemory
 */
wchar_t** GetAllFiles( wchar_t const* folder )
{
    size_t nAllocated = 0, nUsed = 0;
    wchar_t** res = GetAllFilesImpl( folder, NULL, &nAllocated, &nUsed );
    if( res ) {
        /* to indicate end of result add a NULL string */
        wchar_t** tmp = AddToArray( res, &nAllocated, &nUsed, NULL );
        if( !tmp ) return FreeAllFilesMemory(res), NULL;
        res = tmp;
    }
    return res;
}