【问题标题】:Do C functions generally have more arguments than OOP languages' methods?C 函数通常比 OOP 语言的方法有更多的参数吗?
【发布时间】:2020-10-06 13:12:15
【问题描述】:

我必须编写一个有 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 代码?

【问题讨论】:

  • 如果你想让你的函数签名/调用更简洁,你可以传递一个struct
  • 不,只是一些(许多:))语言隐式地采用“对象”参数,即您没有在代码中明确指定它,但它仍然存在。如果您将数据组织在结构中(这样您就可以一起传递一些参数),您应该不会看到很大的(任何)差异。
  • @FiddlingBits 哦,我知道使用struct 会有什么帮助。谢谢。
  • @Al.G.好的谢谢你。如果我是正确的,那么在 C 中唯一可以做到这一点的数据结构是 struct?我会尝试更频繁地使用struct
  • 顺便说一句,您使用了省略号:...,这在 C(和 C++)中很重要,因为您的第二个参数使它成为 variadic function 原型,允许从一个,到许多相同类型的重复参数。在您的问题的背景下,这当然不是您要传达的意思。 :)

标签: c function arguments procedural-programming


【解决方案1】:

您对避免过多的全局变量的担忧是正确的,因为它们往往会变得无法维护,尤其是当它们变得越来越大时。所以让我们通过参数将数据传递给函数。

使用参数为您的函数提供执行所需的信息是一个好主意,原因有很多。它首先提供了代码的可读性,而且还使编写线程安全函数(可以从不同线程安全调用的函数)更容易。

无论如何,一般来说,定义一个具有参数太多的函数有时可能确实会导致效率低下在堆栈消耗方面(尤其是在嵌入式系统中,其中只有前 3 个或 4 个参数使用处理器寄存器,所有其他参数都复制到堆栈中。

解决方案是使用数据结构。使用structs(与在 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 语言而不是过程语言中传递的参数更少:因为在大多数情况下,调用对象将是一种程序语言。但是通过精心设计的结构化编程,这种差异会非常小。

【讨论】:

  • 不错的答案。我正在考虑将我的想法添加到您的答案中或自己写一个。
  • @klutt 谢谢,我很感激。谈到你的答案,如果你的想法足以建立一个补充答案,我认为没有理由保留它。但是,如果差异实际上很小(和/或您想纠正我糟糕的语法,尤其是当我必须处理“数据”时,我在选择复数或单数形式时总是把这个词弄得一团糟)请随意编辑它。 ;) 无论如何,我希望有答案选项。
  • 我选择了自己的答案。我想我有几件事要补充,如果你说的话感觉不好。
  • “结构化编程”不是指结构。它指的是使用 ifwhilefor 等控制流结构,与之前流行的意大利面条式 GOTO 形成鲜明对比。
  • @user2357112supportsMonica 我会检查这个词,但与此同时我改写了它。使用 那些 术语(尤其是如果它们不准确时)在我的回答的经济性中并不 那么 重要;)
【解决方案2】:

为了扩展 cmets 中的观点,当替代方案是大量单独的参数时,将单个 指针传递给 struct 的效率得到了提高,这仅仅是因为 sizeof 传递了单个指针与传递多个指针或表示单个参数的值相比,for as many members as you like(只要它小于 1023)很小。与超过 3 或 4 个参数的任何内容相比,阅读一个参数也容易得多。

因此,即使您的原型相对较小:void f1(int a, int b, int bar)(已删除省略号。)并给出 32 位目标,4bytes/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

【讨论】:

    【解决方案3】:

    在 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,但它非常混乱)。结构基本上是一个没有封装和支持私有成员的类。所以他们适合这个目的。

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

    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;
    }
    

    将结构体传递给函数并不常见,但也不奇怪。使用它你会发现它很有用。

    【讨论】:

      猜你喜欢
      • 2018-08-06
      • 1970-01-01
      • 2011-10-22
      • 1970-01-01
      • 1970-01-01
      • 2020-11-17
      • 2017-10-16
      • 1970-01-01
      相关资源
      最近更新 更多