C ++:指针,循环变量和结构的麻烦

时间:2010-06-16 23:53:12

标签: c++ memory-management pointers loops

考虑以下示例:

#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->fiter->pstr都会产生不同的结果。不幸的是,iter->pstr总是一样的。

我怀疑每次循环时都会创建一个新的loopStr。我只是复制指针而不是将其复制到结构中。指针写入的位置被覆盖。

我该如何避免这种情况?如果不在堆上分配内存,是否可以解决这个问题?

4 个答案:

答案 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循环中有效。