简体   繁体   中英

Stack overflow in recursive function

I am writing a simple app that outputs all files in some directory to console. To achieve this I dynamically allocate memory in function PathCreator() and return a pointer to this memory. I don't know how to correctly free this memory segment in GetAllFiles() . When I use the code below I get a stack overflow exception. How can I fix this? Please don't offer me to use something that doesn't need dynamically allocated memory, I just want to fix my code.

#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");
}

So, you have "." and ".." in your directory search.

The first entry is ".", so:

p = PathCreator(dir, file.cFilename)

yields:

"C:\Users\."

Then the next line:

if (!is_directory(p))

Is ALWAYS false, so it just keeps recursing into:

GetAllFiles(p)

forever ... or until your stack blows up, whichever comes first ;-)

I would recommend explicitly checking for "." and ".." and skipping those entries (also MFC and Qt, etc. have nice directory handling classes, but I think you want to do it this way).

My modification:

    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));

Again you try to use C in place of C++ and you still using wcout ?! no problem you are a programmer and I'm sure you have a reason for this! but memory management in C is much much harder than C++ and you should have some skills to use it. Here is a fully working code but as you see it is really harder to manage, use and understand than its C++ version using standard containers and string, so if you are allowed to use C++(as you use wcout ) then use its C++ version for ease:

#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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM