C ++: проблема с указателями, переменными цикла и структурами - PullRequest
0 голосов
/ 17 июня 2010

Рассмотрим следующий пример:

#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. Вместо того, чтобы копировать его в структуру, я только копирую указатель. Местоположение, в которое записывает указатель, перезаписывается.

Как я могу избежать этого? Можно ли решить эту проблему без выделения памяти в куче?

Ответы [ 4 ]

4 голосов
/ 17 июня 2010

То, что у вас здесь есть, это неопределенное поведение. Каждый раз в цикле вы создаете и уничтожаете массив, а затем присваиваете его адрес foo.pstr и помещаете его обратно в свой вектор. Просто случается, что компилятор каждый раз создает этот массив в одном и том же месте (что логично, но не обязательно). Когда вы распечатываете это, вы технически распечатываете удаленные данные, просто система не суетится, потому что это не защищенное пространство. В ячейке памяти просто есть то, что было назначено последним.

Вы исправили это, прекратив использование необработанных указателей и массивов символов.

2 голосов
/ 17 июня 2010
foo.pstr = loopStr; // wchar_t* = wchar_t ? Why does this work?

Это не работает, по крайней мере, не так, как вы этого хотите. loopStr - это массив, но вы также можете использовать его как указатель. Поэтому, когда вы присваиваете указатель foo.pstr, он получает адрес первого элемента в loopStr. Это локальная переменная, размещенная в стеке, и она действительна только внутри цикла for.

2 голосов
/ 17 июня 2010
    ...
    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 может предупредить вас о получении адресов локальных переменных (хотя я сомневаюсь в этом).

Без использования выделения кучи, я думаю, что единственный вариант - это исправить размер foo.pstr в odp, т.е.

 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 голосов
/ 17 июня 2010

Похоже, что pstr указывает на переменную локальной области действия loopStr, поэтому результаты не определены (кажется, что при печати результатов в нем по-прежнему сохраняется последнее сохраненное значение). Если вы напечатали адрес pstr в цикле, я думаю, он будет одинаковым для каждой позиции в цикле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...