代码之家  ›  专栏  ›  技术社区  ›  Nick Heiner

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

  •  0
  • Nick Heiner  · 技术社区  · 14 年前

    考虑以下示例:

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

    我的怀疑是,每次通过循环,一个新的 loopStr

    我怎样才能避免这种情况?不在堆上分配内存就可以解决这个问题吗?

    4 回复  |  直到 14 年前
        1
  •  2
  •   Akusete    14 年前
        ...
        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)分配给堆栈

    指定foo.pstr指向堆栈上的数组

    C—将基[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++,让它为您进行内存分配和管理。

        2
  •  4
  •   Edward Strange    14 年前

    你在这里得到的是未定义的行为。每次通过循环创建和销毁一个数组,然后将其地址分配给foo.pstr并将其推回到向量中。编译器只是碰巧每次都在同一个位置创建该数组(这是合乎逻辑的,但不是必需的)。当你把它打印出来的时候,从技术上讲,你是在打印删除的数据,只是系统没有因为它不是受保护的空间而打你一巴掌。内存位置只包含上次分配的内容。

        3
  •  2
  •   Mark Wilkins    14 年前

    看起来像 pstr 指向局部作用域变量 loopStr ,因此结果未定义(在打印结果时,它似乎仍包含最后存储的值)。如果在循环中打印pstr的地址,我相信循环中的每个位置都是相同的。

        4
  •  2
  •   Karmastan    14 年前
    foo.pstr = loopStr; // wchar_t* = wchar_t ? Why does this work?
    

    这不管用,至少不是你想要的方式。loopStr是一个数组,但也可以像指针一样使用它。因此,当您分配给指针foo.pstr时,它将获得loopStr中第一个元素的地址。这恰好是在堆栈上分配的局部变量,并且仅在for循环内有效。