Разница между изменением параметра функции и изменением локальной переменной - PullRequest
0 голосов
/ 14 июля 2020

Я видел функцию, которая принимает список и указатель на функцию и применяет эту функцию к каждому элементу списка.

ft_list.h

#ifndef LIST_H
#define LIST_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

typedef struct  s_list
{
    struct s_list *next;
    void    *data;
}       t_list;
#endif

ft_list_foreach. c

void    ft_list_foreach(t_list *begin_list, void (*f) (void *))
{
    t_list  *curr;

    curr = begin_list;
    while (curr)
    {
        (*f)(curr->data);
        curr = curr->next;
    }
}

Но зачем объявлять t_list * curr? А не просто увеличить begin_list до конца связанного списка (NULL)?

Ответы [ 2 ]

3 голосов
/ 14 июля 2020

Хотя в этом случае объявление новой переменной, содержащей указатель на список, не имеет значения, я бы сказал, что это хорошая практика - никогда не изменять параметры функции, только когда этого требует природа функции (т.е. когда у вас есть параметр inout, например, буфер). Это неписаное правило избавит вас от множества проблем в будущем при работе с более сложными функциями.

Рассмотрим простой случай, когда вам по какой-то причине необходимо также уничтожить и освободить содержимое этого списка после применения функция f. Конечно, в этом простом примере вы легко заметите проблему, но бывают случаи, когда это не так очевидно.

Итак, опять же, хотя здесь это не имеет значения, я настоятельно рекомендую всегда напишите свои функции, используя этот шаблон, и это позволит избежать множества странных и неожиданных проблем.

1 голос
/ 14 июля 2020

Функция может быть спроектирована так, чтобы возвращать значение, хотя технически нет явной необходимости в этом, при передаче указателя на объект в качестве аргумента для его изменения в функции.

Причина этого в том, что когда у вас есть критическая операция внутри функции, и в этой операции есть ошибка, вы можете вернуть определенное значение c вызывающему, чтобы обозначить, что ошибка произошла внутри функции.

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

Это метод, при котором вы возвращаете переданный указатель, когда операции в функции были успешными, и например, NULL при ошибке.

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

Поскольку вы увеличиваете указатель в функции, было бы не очень хорошо возвращать увеличенный указатель, так как можно было бы переназначить переданный указатель с возвращаемым значением, например:

int *p = //anything ;
p = foo(p);

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

OR

сохранить значение переданного указателя в другую переменную локального указателя, увеличить указатель параметра и вернуть указатель «копировать».

Поэтому может иметь смысл использовать curr вместо begin_list для увеличения и используйте begin_list в качестве возвращаемого значения в случае успеха.

В противном случае NULL, если произошла ошибка.

Обратите внимание, что вам нужно изменить тип возвращаемого значения с void на t_list *, тогда .

Также:

Разница между изменением параметра функции и изменением локальной переменной

A параметр является локальной переменной.

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