如何从C的“getcwd”函数返回一个std :: string

时间:2010-05-19 21:35:19

标签: c++ memory-leaks cross-platform getcwd

很抱歉继续抨击这个,但我正在努力学习:)。这有什么好处吗?是的,我关心内存泄漏。我找不到预先分配char *的好方法,因为它似乎没有跨平台的方式。

const string getcwd()
{
    char* a_cwd = getcwd(NULL,0);
    string s_cwd(a_cwd);
    free(a_cwd);
    return s_cwd;
}

UPDATE2:没有Boost或Qt,最常见的东西可以啰嗦(见接受的答案)

7 个答案:

答案 0 :(得分:7)

如果你想保持标准,如果你传递一个NULL,getcwd不需要做任何事情;你应该在堆栈上为大多数场合(比如255个字符)分配一个“足够大”的缓冲区,但要准备好getcwd可能失败的errno==ERANGE;在这种情况下,你应该在dinamically分配一个更大的缓冲区,并在必要时增加它的大小。

这样的东西可以起作用(注意:没有经过测试,只是从头开始编写,可以肯定会有所改进):

string getcwd()
{
    const size_t chunkSize=255;
    const int maxChunks=10240; // 2550 KiBs of current path are more than enough

    char stackBuffer[chunkSize]; // Stack buffer for the "normal" case
    if(getcwd(stackBuffer,sizeof(stackBuffer))!=NULL)
        return stackBuffer;
    if(errno!=ERANGE)
    {
        // It's not ERANGE, so we don't know how to handle it
        throw std::runtime_error("Cannot determine the current path.");
        // Of course you may choose a different error reporting method
    }
    // Ok, the stack buffer isn't long enough; fallback to heap allocation
    for(int chunks=2; chunks<maxChunks ; chunks++)
    {
        // With boost use scoped_ptr; in C++0x, use unique_ptr
        // If you want to be less C++ but more efficient you may want to use realloc
        std::auto_ptr<char> cwd(new char[chunkSize*chunks]); 
        if(getcwd(cwd.get(),chunkSize*chunks)!=NULL)
            return cwd.get();
        if(errno!=ERANGE)
        {
            // It's not ERANGE, so we don't know how to handle it
            throw std::runtime_error("Cannot determine the current path.");
            // Of course you may choose a different error reporting method
        }   
    }
    throw std::runtime_error("Cannot determine the current path; the path is apparently unreasonably long");
}

顺便说一下,在你的代码中有一个非常错误的东西:你试图取消定位a_cwd(可能是在非标准扩展中,使用malloc或其他一些内存分配函数分配的,因为getcwd被认为是C delete:你绝对不应该这样做,请记住每个分配方法都有它的释放对应物,并且它们不能不匹配。

答案 1 :(得分:3)

这适用于Windows和Linux,因为当buf的{​​{1}}参数为NULL时,它们都支持自动分配行为。但是,请注意,此行为不是标准,因此您可能会在更深奥的平台上遇到问题。

可以这样做而不依赖于这种行为,但是:

getcwd

上面的代码以1024的缓冲区大小开始,然后,如果const string getcwd() { size_t buf_size = 1024; char* buf = NULL; char* r_buf; do { buf = static_cast<char*>(realloc(buf, buf_size)); r_buf = getcwd(buf, buf_size); if (!r_buf) { if (errno == ERANGE) { buf_size *= 2; } else { free(buf); throw std::runtime_error(); // Or some other error handling code } } } while (!r_buf); string str(buf); free(buf); return str; } 抱怨缓冲区太小,它会将大小翻倍并再次尝试,并重复直到它有足够大的缓冲区并成功

请注意,将getcwd的第一个参数调用为NULL与realloc相同。

答案 2 :(得分:2)

您不能将空指针传递给std::string的构造函数,因此必须检查缓冲区指针getcwd()返回值不为空。此外,您传递给getcwd() must not be null的缓冲区指针。

std::string getcwd() {
    char buf[FILENAME_MAX];
    char* succ = getcwd(buf, FILENAME_MAX);
    if( succ ) return std::string(succ);
    return "";  // raise a flag, throw an exception, ...
}

答案 3 :(得分:2)

我认为你应该使用符合ISO C ++标准的版本_getcwd。返回const string是没有意义的,您应该使用free来解除分配(至少根据MSDN):

string getcwd()
{
    char* a_cwd = _getcwd(NULL, 0);
    string s_cwd(a_cwd);
    free(a_cwd);
    return s_cwd;
}

当然,您还应该检查_getcwd()是否返回NULL。

答案 4 :(得分:1)

您需要检查a_cwd是否为NULL。然后它将适用于Mac,Windows,Linux。但是,它不符合POSIX。

编辑:perror不会退出程序,所以你应该退出,抛出异常或做某事。

答案 5 :(得分:1)

这个怎么样?它很短,异常安全,并且不会泄漏。

std::string getcwd() {
    std::string result(1024,'\0');
    while( getcwd(&result[0], result.size()) == 0) {
        if( errno != ERANGE ) {
          throw std::runtime_error(strerror(errno));
        }
        result.resize(result.size()*2);
    }   
    result.resize(result.find('\0'));
    return result;
}

答案 6 :(得分:0)

当“字符串构造函数”为您做一切时:

#include <stdio.h>  // defines FILENAME_MAX
#include <unistd.h> // for getcwd()

std::string GetCurrentWorkingDir()
{
    std::string cwd("\0",FILENAME_MAX+1);
    return getcwd(&cwd[0],cwd.capacity());
}