考虑以下示例:
#include <iostream>
#include <sstream>
#include <vector>
#include <wchar.h>
#include <stdlib.h>
using namespace std;
struct odp {
int f;
wchar_t* pstr;
};
int main()
{
vector<odp> vec;
ostringstream ss;
wchar_t base[5];
wcscpy_s(base, L"1234");
for (int i = 0; i < 4; i++)
{
odp foo;
foo.f = i;
wchar_t loopStr[1];
foo.pstr = loopStr; // wchar_t* = wchar_t ? Why does this work?
foo.pstr[0] = base[i];
vec.push_back(foo);
}
for (vector<odp>::iterator iter = vec.begin(); iter != vec.end(); iter++)
{
cout << "Vec contains: " << iter->f << ", " << *(iter->pstr) << endl;
}
}
这会产生:
Vec contains: 0, 52
Vec contains: 1, 52
Vec contains: 2, 52
Vec contains: 3, 52
我希望每次iter->f
和iter->pstr
都会产生不同的结果。不幸的是,iter->pstr
总是一样的。
我怀疑每次循环时都会创建一个新的loopStr
。我只是复制指针而不是将其复制到结构中。指针写入的位置被覆盖。
我该如何避免这种情况?如果不在堆上分配内存,是否可以解决这个问题?
答案 0 :(得分:4)
你在这里得到的是未定义的行为。每次通过循环,您创建并销毁一个数组,然后将其地址分配给foo.pstr并将其推回到向量中。编译器恰好每次都在同一个地方创建该数组(这是合乎逻辑的但不是必需的)。当你打印出来时,你在技术上打印出已删除的数据,只是因为它不是受保护的空间,系统不是为了它而婊子。内存位置只具有最后分配的内容。
您可以通过停止使用原始字符指针和数组来解决此问题。
答案 1 :(得分:2)
看起来pstr
指向本地范围的变量loopStr
,因此结果未定义(在打印结果时,它似乎仍然具有最后存储的值)。如果你在循环中打印pstr的地址,我相信它对于循环中的每个位置都是一样的。
答案 2 :(得分:2)
...
odp foo;
foo.f = i;
wchar_t loopStr[1]; //A
foo.pstr = loopStr; //B
foo.pstr[0] = base[i]; //C
vec.push_back(foo);
...
A - 您将一个数组(大小为1)分配给堆栈
B - 指定foo.pstr指向堆栈上的数组
C - 将base [i]分配给数组的第一个元素(在堆栈中)
在for循环退出当前循环后,变量loopStr不再在范围内,其内容未定义。下一个循环迭代将最有可能重复使用相同的内存地址(因此,当您最后打印时,为什么会得到相同的值)。如果您启用了优化,您的C编译器可能会警告您关于获取局部变量的地址(尽管我对此表示怀疑)。
不使用任何堆分配我认为你唯一的选择是在odp中修复foo.pstr的大小,即
struct odp {
int f;
wchar_t pstr[1];
};
或在堆上分配数组作为odp初始化的一部分
...
odp foo;
foo.f = i;
foo.pstr = new wchar_t [1];
foo.pstr[0] = base[i];
vec.push_back(foo);
...
最好还是使用std :: wstring,因为你正在使用c ++,让它为你做内存分配和管理。
答案 3 :(得分:2)
foo.pstr = loopStr; // wchar_t* = wchar_t ? Why does this work?
这不起作用,至少不是你想要它的方式。 loopStr是一个数组,但你也可以像指针一样使用它。因此,当您指定指针foo.pstr时,它将获取loopStr中第一个元素的地址。这恰好是在堆栈上分配的局部变量,仅在for循环中有效。