Может кто-нибудь объяснить, как C обрабатывает объявления структуры в цикле for? (См пример) - PullRequest
2 голосов
/ 03 августа 2011

Я не слишком много работаю с C, и базовый языковой нюанс просто отбросил меня назад, и я хотел бы лучше понять его, чтобы избежать его в будущем.

Я использую найденную реализацию очередипо следующей ссылке.

http://www.planet -source-code.com / vb / scripts / ShowCode.asp? txtCodeId = 9202 & lngWId = 3

Это довольно распространенное явлениеочередь, в которой значения помещаются и выталкиваются(FIFO) Но по какой-то причине последнее значение, которое я помещаю в очередь, устанавливает все значения в очереди с одинаковым значением.

Я начал со следующего кода.

Queue q;
queue_init(&q);
for (int i=0;i<10;i++) {
    MyData *in_md;
    in_md->mNumber = i;
    queue_push(&q, in_md);

    MyData *h = (MyData *)q.head->data;
    MyData *t = (MyData *)q.tail->data;

    NSLog(@"in_md: %i", in_md->mNumber);
    NSLog(@"h->mNumber: %i", h->mNumber);
    NSLog(@"t->mNumber: %i", t->mNumber);
    if (q.head->link) {
        MyData *l = (MyData *)q.head->link->data;
        NSLog(@"l->mNumber: %i", l->mNumber);
    }
}

НоЯ обнаружил, что когда я вытолкнул значения из числа, значение было 9 для каждого экземпляра.Я думал, что у меня есть какая-то ошибка указателя и пытался исправить это некоторое время.Я подумал, что, поскольку я объявил in_md внутри блока цикла for, это будет уникальный экземпляр с уникальным адресом памяти.В конце концов обновил код до следующего ...

Queue q;
queue_init(&q);
for (int i=0;i<10;i++) {
    void *data = malloc(sizeof(MyData)); // key change
    MyData *in_md = (MyData *)data;
    in_md->mNumber = i;
    queue_push(&q, data);

    MyData *h = (MyData *)q.head->data;
    MyData *t = (MyData *)q.tail->data;

    NSLog(@"in_md: %i", in_md->mNumber);
    NSLog(@"h->mNumber: %i", h->mNumber);
    NSLog(@"t->mNumber: %i", t->mNumber);
    if (q.head->link) {
        MyData *l = (MyData *)q.head->link->data;
        NSLog(@"l->mNumber: %i", l->mNumber);
    }
}

Теперь мы гарантируем, что новый экземпляр создан, потому что используется malloc, и я приведу его к переменной, я могу установить значение int.Это создает вывод, как я ожидаю.

Я предполагаю, что именно так должен работать C, но я этого не ожидал.Обычно в других языках, таких как Java, C # и даже JavaScript, объявление переменной в блоке создает новый экземпляр переменной, чего я и ожидал.Но это не тот случай.

Что происходит в этом случае?

Ответы [ 3 ]

4 голосов
/ 03 августа 2011

В вашем коде:

Вы создали указатель:

MyData *in_md;

и без резервирования памяти для этого указателя (это то, что делает malloc во втором блоке кода), которому вы назначаете значенияэто члены:

in_md->mNumber = i;

Вы в основном обращались к неопределенной памяти.В вашем случае значение было «9», но вы могли также получить сбой приложения.

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

Имейте в виду, что вам нужно освободить память, используя метод free () , прежде чем окончательно освободить очередь.

3 голосов
/ 03 августа 2011

Объявление указателя создает указатель, но не указатель.Вам все еще нужно инициализировать указатель (так же, как вам нужно инициализировать любую другую переменную), но в случае указателя его нужно инициализировать по адресу памяти, что можно сделать с помощью new (C ++)или malloc (C) для выделения памяти (также можно назначить адрес памяти существующей переменной через &).

Как правило, это хорошая практика программирования - объединять объявление с инициализацией.Гарантируя, что вы никогда не используете:

Type identifier;

И всегда используйте:

Type identifier = initializing_expression;

Гарантирует, что вы не столкнетесь с такими проблемами, и избавит вас от небольшой боли.Обратите внимание, что это не сильно отличается от других языков (в Java язык вообще запрещает использование неинициализированных переменных, в то время как в JavaScript объявление без инициализации приведет к тому, что оно будет иметь значение undefined).

2 голосов
/ 03 августа 2011

Ваша вторая реализация - действительно путь.

Часть, на которой, я думаю, вы зациклены, - это выполнение "MyData * in_md;"в некотором смысле действительно создаст «новый экземпляр», но он не создаст новый экземпляр MyData, он создаст новый экземпляр указателя MyData.Тем не менее, указатель не очень хорошо работает, пока он на самом деле не укажет на что-то полезное, отсюда и вызов malloc.

Так что, если в Java у вас будет MyData in_md = new MyData(), он автоматически выделит место для MyData, но вC у вас есть MyData *in_md = (MyData*)malloc(sizeof(MyData));, потому что у вас гораздо более низкий уровень управления памятью.После этого вы можете работать с объектом так же, как в Java или C #.Единственное, что следует помнить, это то, что в Java, если выделение памяти было неудачным, это, вероятно, приведет к приятной ошибке, но это не так в C, поэтому может быть хорошей идеей убедиться, что in_md неNULL, в зависимости от приложения.Наконец, в C отсутствует сборка мусора, поэтому важно убедиться, что вы освободили () элемент, который вы поместили malloc'ом после его удаления из очереди.

Queue q;
queue_init(&q);
for (int i=0;i<10;i++) {
    MyData *in_md = (MyData *)malloc(sizeof(MyData));
    in_md->mNumber = i;
    queue_push(&q, (void*)in_md);

    MyData *h = (MyData *)q.head->data;
    MyData *t = (MyData *)q.tail->data;

    NSLog(@"in_md: %i", in_md->mNumber);
    NSLog(@"h->mNumber: %i", h->mNumber);
    NSLog(@"t->mNumber: %i", t->mNumber);
    if (q.head->link) {
        MyData *l = (MyData *)q.head->link->data;
        NSLog(@"l->mNumber: %i", l->mNumber);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...