为什么fstream.open(filename)与文字一起使用但不与生成的字符串一起使用?

时间:2014-01-28 04:00:20

标签: c++ string fstream

在使用MS Visual Studio 2003从C ++编译的DLL中,我通过从SHGetFolderPath获取字符串并附加文件名,然后尝试打开该文件进行输出来生成文件路径。

我有一些全局变量,一个用于设置文件路径的函数,另一个用于打开文件的函数,如下所示。
Logger类声明未显示,但它有一个声明为ofstream *ofs的成员,它是在Logger的构造函数中创建的文件对象。

constructorBase()中,如果我致电initLogFN()设置文件路径,则ofs->open会失败。
但是,如果我注释掉initLogFN()调用并取消注释将logPath设置为硬编码字符串文字的行,则ofs->open会成功。

在任何一种情况下进行调试时,Visual Studio会在调用logPath之前正确显示ofs->open的值为“C:\ Users \ sleis \ AppData \ Roaming \ Address-IT.log”。登记/> 但是,在失败的情况下,在ofs->open调用之后,logPath的值会更改为六个字符,这些字符似乎是随机的,但每次运行我的测试应用时都是相同的。

为什么这两种情况的行为不同,我如何解决失败的情况呢?


#if defined (WIN32)
const char dirSep[]="\\";
#else
const char dirSep[]="/";
#endif

const char logFN[]="Address-IT.log";
char *logPath=0;

bool initLogFN()
{
    if (logPath!=0) return false;

#if defined (_DEBUG)
#if defined (WIN32)
    char path[MAX_PATH];
    HRESULT result=SHGetFolderPath(NULL,CSIDL_APPDATA,NULL,SHGFP_TYPE_CURRENT,path);
    if (result!=S_OK) return false;
    strcat(path,dirSep);
    strcat(path,logFN);
    logPath=path;
    return true;
#endif
#endif

    return false;
}

void Logger::constructorBase()
{
    if (!initLogFN()) return;
    //logPath="C:\\Users\\sleis\\AppData\\Roaming\\Address-IT.log";

    ofs->open(logPath,ios_base::out | ios_base::app);

    if ((ofs->rdstate() & std::ifstream::failbit)!=0) {
        std::cout << std::endl << "Error opening log file.";
    } else if (ofs->is_open()) {
        std::cout << std::endl << "Log file opened successfully.";
    }
}

3 个答案:

答案 0 :(得分:2)

数组pathinitLogFN本地的自动变量。设置logPath=path时,会导致logPath变量指向此数组。但是当函数返回时,该数组超出范围,这意味着它占用的内存可能随后被覆盖。现在path指向垃圾。

永远不要返回指向局部变量的指针。

您实际上有三个选项可以从函数中返回C风格的字符串:

  1. 让函数使用malloc或(在C ++中)new为字符串动态分配内存。如果你这样做,那么调用者必须最终调用freedelete以避免内存泄漏。

  2. char*作为参数传递给该功能。调用者应该为字符串预先分配内存,然后将指针传递给函数。该函数写入指针指向的位置。

  3. 使用全局变量,并返回指向该内容的指针。

  4. 在这些方法中,#2是最常见的。 C和C ++标准库采用这种方法(例如fgets),并且通常需要另一个参数来指示传入的缓冲区的大小,因此该函数知道不要写入已分配内存的末尾。

    方法#3的缺点是它不可重入。一些旧的Unix库函数采用方法#3(例如ttyname),但后来引入了采用方法#2的可重入版本(例如,ttyname_r)。

答案 1 :(得分:2)

问题是你试图引用一个不再存在的变量( char路径[MAX_PATH] )。

首先在initLogFN中创建此变量。然后指定logPath指向它的地址。但是在initLogFN的范围之外(也就是它返回之后),内存logPath现在指向的是完全垃圾。

在调试器 BEFORE 中调用下一个函数仍然看起来没问题的原因是因为它存在于堆栈中。但是,通过调用下一个函数会覆盖堆栈。而你的字符串充满了垃圾。它每次都填充相同的六个垃圾字符的原因?我们称之为运气。

有几种方法可以解决这个问题。

  1. 您可以动态分配字符数组,例如说char* path = new char[MAX_PATH]。如果您这样做,请确保在完成后通过说delete path
  2. 进行清理
  3. 您可以在构造函数库中声明路径,然后将其传递到initLogFN。例如,您的新initLogFN将具有此签名bool initLogFN(char* path),您可以这样调用它(来自Logger :: constructorBase)char path[MAX_PATH]; initLogFN(path);
  4. 第三种方法是删除这个看似不必要的辅助函数(initLogFN),然后将该代码放入constructorBase函数中。
  5. 选择权归你自己和你的选择!祝好运!请记住,如果你不动态分配一些东西(使用 new malloc ),那么不要试图存储一个全局指针,绝对不要尝试在声明它的范围之外引用它!

答案 2 :(得分:2)

语句logPath=path;将指针logPath设置为指向与path相同的位置。函数退出后,路径的内存(以及logPath的内存)将被释放,内存将被重新分配并被其他函数调用覆盖。

您需要为logPath分配数据并使用strcpy将数据从路径复制到logpath以防止这种情况发生。