在使用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.";
}
}
答案 0 :(得分:2)
数组path
是initLogFN
本地的自动变量。设置logPath=path
时,会导致logPath
变量指向此数组。但是当函数返回时,该数组超出范围,这意味着它占用的内存可能随后被覆盖。现在path
指向垃圾。
永远不要返回指向局部变量的指针。
您实际上有三个选项可以从函数中返回C风格的字符串:
让函数使用malloc
或(在C ++中)new
为字符串动态分配内存。如果你这样做,那么调用者必须最终调用free
或delete
以避免内存泄漏。
将char*
作为参数传递给该功能。调用者应该为字符串预先分配内存,然后将指针传递给函数。该函数写入指针指向的位置。
使用全局变量,并返回指向该内容的指针。
在这些方法中,#2是最常见的。 C和C ++标准库采用这种方法(例如fgets
),并且通常需要另一个参数来指示传入的缓冲区的大小,因此该函数知道不要写入已分配内存的末尾。
方法#3的缺点是它不可重入。一些旧的Unix库函数采用方法#3(例如ttyname
),但后来引入了采用方法#2的可重入版本(例如,ttyname_r
)。
答案 1 :(得分:2)
问题是你试图引用一个不再存在的变量( char路径[MAX_PATH] )。
首先在initLogFN中创建此变量。然后指定logPath指向它的地址。但是在initLogFN的范围之外(也就是它返回之后),内存logPath现在指向的是完全垃圾。
在调试器 BEFORE 中调用下一个函数仍然看起来没问题的原因是因为它存在于堆栈中。但是,通过调用下一个函数会覆盖堆栈。而你的字符串充满了垃圾。它每次都填充相同的六个垃圾字符的原因?我们称之为运气。
有几种方法可以解决这个问题。
char* path = new char[MAX_PATH]
。如果您这样做,请确保在完成后通过说delete path
。bool initLogFN(char* path)
,您可以这样调用它(来自Logger :: constructorBase)char path[MAX_PATH]; initLogFN(path);
选择权归你自己和你的选择!祝好运!请记住,如果你不动态分配一些东西(使用 new 或 malloc ),那么不要试图存储一个全局指针,绝对不要尝试在声明它的范围之外引用它!
答案 2 :(得分:2)
语句logPath=path;
将指针logPath设置为指向与path相同的位置。函数退出后,路径的内存(以及logPath的内存)将被释放,内存将被重新分配并被其他函数调用覆盖。
您需要为logPath分配数据并使用strcpy将数据从路径复制到logpath以防止这种情况发生。