У C функций обычно больше аргументов, чем OOP языковых методов? - PullRequest
2 голосов
/ 16 июня 2020

Мне пришлось написать программу C с более чем 600 строками и примерно 25 функциями. Это самый длинный код C, который я написал.

Я заметил, что некоторые функции имеют более 5 аргументов. Те, которые вызываются напрямую из main (), имеют больше аргументов. Чем дальше от main (), тем меньше.

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

Таким образом, это будет выглядеть примерно так:

void f1(int a, int b,..., int bar){
    int foo = f2(bar); // the only time 'bar' is used in f1
    .
    .
    .
}

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

Я не опытный программист, но когда я программировал на Java, я не думаю, что мне когда-либо приходилось писать метод с более чем 5 аргументами .

Нормально ли в C передавать намного больше аргументов, чем в других языках? Это просто природа процедурного программирования против OOP? Или я просто пишу плохой код C?

Ответы [ 3 ]

5 голосов
/ 16 июня 2020

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

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

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

Решение - использование структур данных. С помощью struct s (не , поэтому отличается от того, что в OOP языках, таких как Java, вы бы назвали объектами ), вы можете сгруппировать свои глобальные данные в логические объекты . Это позволяет определить все ваши данные вместе, изменить их поля и передать их вашим функциям. И передача его с помощью указателя делает возможным его изменение в исходном месте.

Таким образом, ваши примерные функции станут

typedef struct
{
    int a;
    int b;
    int bar;
} MyData_t;

void f1( MyData_t *data )
{
    int foo = f2( data->bar );

    /* ... */
}

int main( void )
{
    MyData_t d = { 5, 7, 9 };

    f1( &d );

    return 0;
}

Вам нужно изменить f2() передав его больше параметров? Ну, перенаправьте на него указатель на MyData_t, и вы получите доступ ко всем нужным вам переменным.


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

3 голосов
/ 17 июня 2020

Нормально ли в C передавать больше аргументов, чем в других языках? Это просто природа процедурного программирования против OOP? Или я просто пишу плохие C коды?

Это очень обычное дело. Это потому, что в C у нас нет объектов (да, есть, но это нечто совершенно иное, чем то, что Java люди назвали бы объектами), а вместо этого просто переменные (объекты в C).

Таким образом, вы передаете эквивалент класса C функциям, просто передавая функции каждый атрибут этого класса. Матрицы инвертирования функций будут иметь такую ​​сигнатуру:

void inverse (const double *input, size_t ix, size iy, 
              double *output, size_t ox, size_t oy);

В Java или C ++ это будет выглядеть примерно так:

void inverse(const Matrix &input, Matrix &output);

(Я не очень разбираюсь в C ++ кодировщик, так что простите меня за ошибки)

Дело в том, что объект Matrix содержит внутри себя размерные переменные-члены. Что-то, что не одобряется в OOP -языках, - это классы данных, то есть классы без методов и c переменных-членов.

В C есть эквивалент, и это структура. Нет поддержки функций-членов, если вы не используете указатели на функции (вы можете использовать OOP в C, но это очень беспорядочно). По сути, структура - это просто класс без инкапсуляции и поддержки частных членов. Так что они подходят для своей цели.

В отличие от массивов, вам не нужно передавать их как указатели. Это прекрасно:

struct myStruct {
    int a;
    char b;
    double c[2];
    void **;
};

bool intIsFortyTwo(struct myStruct args) {
    return args.a == 42;
}

Вы также можете возвращать структуры:

struct myStruct initStruct() {
    struct myStruct ret = {.a=22, .b='a'};
    return ret;
}

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

bool intIsFortyTwo(struct myStruct *args) {
    return args->a == 42;
}

Передача структур функциям не очень распространена, но это тоже не странно. Используйте, если сочтете это полезным.

1 голос
/ 16 июня 2020

Чтобы расширить точку, изложенную в комментариях, эффективность передачи одного указателя на структуру , когда альтернативой является большое количество отдельных аргументов, улучшается просто потому, что передается sizeof единственный указатель для любого количества членов (если оно меньше 1023) является малым по сравнению с передачей количества указателей или значений, представляющих отдельные аргументы. Также намного легче прочитать один аргумент, чем что-либо с более чем 3 или 4 аргументами.

Итак, даже с вашим примером относительно небольшого прототипа: void f1(int a, int b, int bar) (без многоточия.) И заданная 32-битная цель с 4 байтами / int

sizeof a + sizeof b + sizeof bar == 12bytes

По сравнению с размером указателя на структуру с более чем 1000 членов

typedef struct {
    int a;
    int b;
    ... say 1000 more ints
    int bar;
}data_t;

data_t *pData 
pData = &data_t;//point pointer back to location t_data.

sizeof pData == 8 bytes
...