Допустимо ли передавать составной литерал в функцию? - PullRequest
1 голос
/ 11 марта 2020

У меня есть тип структуры в качестве параметра, и мне нужно передать его в функцию.

Весь код приведен ниже:

void insert(struct node *newt) {
    struct node *node = head, *prev = NULL;
    while (node != NULL && node->data < newt->data) {
        prev = node;
        node = node->next;
    }
    newt->next = node;
    if (prev == NULL)
        head = newt;
    else
        prev->next = newt;
}
void print(){
    struct  node *tmp = head;
    while(tmp){
        printf("%d->",tmp->data);
        tmp = tmp->next;
    }
    printf("\n");
}
int main(){
    /*
    * Experiment for naive-insert
    */
    for(int i = 1; i < 4; i++){
        insert(&(struct node){.data = i, .next = NULL});
    }
    //insert(&(struct node){.data = 1, .next = NULL});
    //insert(&(struct node){.data = 2, .next = NULL});
    //insert(&(struct node){.data = 3, .next = NULL});
    print();

}

Если я вызываю insert in для l oop , он напечатает .....-> 3-> 3-> 3 .... (не могу остановиться)

Но если я просто заменю l oop на

insert(&(struct node){.data = 1, .next = NULL});
insert(&(struct node){.data = 2, .next = NULL});
insert(&(struct node){.data = 3, .next = NULL});

будет действовать нормально. Мне интересно, что происходит с моим кодом версии для -l oop.

Ответы [ 3 ]

3 голосов
/ 11 марта 2020

[Редактировать: В первоначальном черновике я был не прав относительно природы хранилища, созданного с использованием сравнительных литералов. Комментарии Джонатана Леффлера и ответ Эри c Постпищила безупречны и доказали, что моя начальная точка неверна. Предложенная мною альтернатива созданию связанных списков действительна, но больше не нужна. Я исправил свой ансер.]]

Код в вашем l oop эквивалентен:

for (int i = 0; i < 3; i++) {
    struct node tmp = {.data = i, .next = NULL};

    insert(&tmp);
}

Компилятор создает временный объект и передает его адрес вашей функции списка , Этот временный объект действителен только в теле l oop и выходит из области видимости сразу после вставки. Доступ к недействительным объектам после l oop через указатели в списке при печати списка - Неопределенное поведение.)

Код без al oop эквивалентен:

struct node tmp1 = {.data = 1, .next = NULL};
struct node tmp2 = {.data = 2, .next = NULL};
struct node tmp3 = {.data = 3, .next = NULL};

insert(&tmp1);
insert(&tmp2);
insert(&tmp3);

Ваш связанный список хорош, пока переменные tmpX не выводят go из области видимости.

2 голосов
/ 11 марта 2020

C 2018 6.5.2.5 5 говорит, что составной литерал внутри функции имеет автоматическое c срок хранения:

… Если составной литерал находится вне тела функции, объект имеет срок хранения c; в противном случае он имеет автоматическую c продолжительность хранения, связанную с вмещающим блоком.

Это означает, что память зарезервирована для составного литерала с момента, когда выполнение программы достигает своего объявления, пока не завершится выполнение вмещающего блока .

Когда код написан:

int main(void)
{
   …
    insert(&(struct node){.data = 1, .next = NULL});
    insert(&(struct node){.data = 2, .next = NULL});
    insert(&(struct node){.data = 3, .next = NULL});
    print();
}

, тогда включающий блок - это { … }, который формирует тело main, и составные литералы существуют до выполнения main заканчивается. Таким образом, они все еще существуют (то есть для них зарезервировано хранилище для каждого из них), пока print выполняется).

В этом коде:

for(int i = 1; i < 4; i++){
        insert(&(struct node){.data = i, .next = NULL});
    }

включающий блок является { … } это тело для l oop. Выполнение этого блока заканчивается каждый раз, когда итерация for l oop. Таким образом, когда позже вызывается подпрограмма print, ни один из этих составных литералов больше не существует (хранилище не зарезервировано ни для одного из них, и, вероятно, оно повторно использовалось для каждого из них последовательно).

2 голосов
/ 11 марта 2020

Вы пытаетесь инициализировать новые узлы в связанном списке с временными значениями - это плохо кончится !!

for(int i = 0; i < 3; i++){
        insert(&(struct node){.data = i, .next = NULL});
    }

Здесь явно происходит то, что компилятор перезапустил ту же память на стек для генерации временной переменной. По сути, предыдущий узел такой же, как и следующий, так что в конечном итоге тот же узел указывает на себя (последнее значение которого равно 2).

Вы можете увидеть это, добавив следующую строку в начале вставки:

    printf("%p\n", newt);

Вы действительно должны использовать mallo c вместо этого.

...