C 函数通常比 OOP 语言的方法有更多的参数吗?

Do C functions generally have more arguments than OOP languages' methods?

我不得不编写一个 600 多行和大约 25 个函数的 C 程序。这是我写过的最长的 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 或 4 个参数使用处理器寄存器,所有其他参数都复制到堆栈)。

解决方案是使用数据结构。使用 structs(不是 so 与什么不同,在诸如 Java 的 OOP 语言中,您将调用 objects)您可以将全局数据分组到 逻辑实体 中。这使得可以一起定义所有数据,更改其字段并将其传递给您的函数。并通过指针 传递它 可以在其原始位置修改它。

因此您的示例函数将变为

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 语言中传递的参数比在过程语言中传递的参数少:只是因为在大多数语言中调用对象将是过程语言中的参数。但是如果设计良好的结构化程序,这个差异就会非常小。

为了扩展评论中提出的观点,当备选方案是大量单独的参数时,将单个 指针传递给结构 的效率得到了提高,这仅仅是因为 sizeof 与传递多个指针或表示单个参数的值相比,传递单个指针 for as many members as you like(只要它小于 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

Is it normal in C to pass way more arguments than in other languages? Is it just the nature of Procedural Programming vs OOP? Or am I just writing bad C codes?

这是很常见的事情。这是因为在 C 中,我们没有对象(是的,我们有,但这与 Java 人们所说的对象完全不同)而只有变量(C 中的对象)。

因此,您只需将 class 中的每个属性传递给函数,就可以将 class 的等价物传递给 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 语言中不受欢迎的东西是 dataclasses,它是没有方法和 public 成员变量的 classes。

在 C 中有一个等价物,那就是结构。不支持成员函数,除非您使用函数指针(您可以在 C 中执行 OOP,但它非常混乱)。结构基本上是什么,只是一个 class 没有封装和对私有成员的支持。所以他们适合这个目的。

与数组不同,您不必将它们作为指针传递。这很好:

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

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

你也可以return结构:

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

通常,出于性能原因,建议使用指针。如果您不使用指针,则会创建该结构的完整副本。第一个函数可以是

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

将结构传递给函数并不是很常见,但也不奇怪。觉得有用就用吧。