15

当我阅读开源代码(Linux C 代码)时,我看到使用了很多函数而不是对 执行所有操作main(),例如:

int main(void ){
    function1();
    return 0;
}

void function() {
    // do something
    function2();
}

void function2(){
    function3();
    //do something
    function4();
}

void function3(){
    //do something
}
void function4(){
    //do something
}

你能告诉我尽可能使用函数的优点和缺点吗?

  • 易于添加/删除功能(或新操作)
  • 代码的可读性
  • 源效率(?)因为函数中的变量将被破坏(除非完成动态分配)
  • 嵌套函数会减慢代码流吗?
4

6 回答 6

11
  • 易于添加/删除功能(或新操作)

绝对 - 也很容易看到操作开始/结束的上下文在哪里。与源代码中任意范围的行相比,这种方式更容易看到。

  • 代码的可读性

你可以做得过火。在某些情况下,拥有或不拥有函数不会影响行数,但会影响可读性——这取决于一个人是否积极。

例如,如果你做了很多设置位操作,你会做:

some_variable = some_variable | (1 << bit_position)

一个函数?这会有帮助吗?

  • 源效率(?)由于函数中的变量被破坏(除非完成动态分配)

如果来源是合理的(例如,您没有在其真实上下文之外重用变量名),那么它应该没关系。编译器应该确切地知道值使用停止的位置以及可以忽略/销毁的位置。

  • 嵌套函数会减慢代码流吗?

在某些无法正确确定地址别名的情况下,它可以。但在大多数程序中,这在实践中并不重要。当它开始变得重要时,您可能会使用分析器检查您的应用程序并发现有问题的热点。

不过,如今编译器在内联函数方面做得相当不错。你可以相信他们至少能做一个体面的工作来摆脱调用开销与函数长度本身相当的所有情况。(以及许多其他情况)

于 2013-06-27T15:52:28.090 回答
4

随着您编写的代码量增加,这种使用函数的做法非常重要。这种分离到函数的做法提高了代码的卫生并使其更易于阅读。我在某处读到,如果只有您可以阅读,那么代码真的没有意义(在某些情况下,我假设没问题)。如果您希望您的代码继续存在,它必须是可维护的,而可维护性是通过以最简单的方式创建函数来创建的。还可以想象一下您的代码库在哪里超过 10 万行。这是很常见的,想象一下在 main 函数中都有这一切。维持这绝对是一场噩梦。将代码划分为功能有助于创建一定程度的可分离性,因此许多开发人员可以在代码库的不同部分上工作。所以基本上简短的回答是肯定的,

于 2013-06-27T15:50:15.967 回答
3

函数应该可以帮助您构建代码。基本思想是,当您确定代码中的某个位置可以以连贯、独立的方式进行描述时,您应该考虑将其放入函数中。

优点:

  • 代码重用。如果你多次执行某个序列的操作,为什么不写一次,多次使用呢?
  • strlen(st)可读性:比它更容易理解while (st[i++] != 0);
  • 正确性:上一行中的代码实际上是错误的。如果它分散在各处,您可能甚至看不到这个错误,如果您将它修复在一个地方,那么这个错误就会留在其他地方。但是在一个名为 的函数中给出这段代码strlen,你就会知道它应该做什么,并且你可以修复它一次。
  • 效率:有时,在某些情况下,编译器在编译函数内的代码时可能会做得更好。不过,您可能不会提前知道。

缺点:

  • 仅仅因为它是一件好事而将代码拆分为函数并不是一个好主意。如果你发现很难给函数起一个好名字(用你的母语,不仅在 C 语言中),那是可疑的。doThisAndThat()大概是两个功能,不是一个。part1()完全是错误的。
  • 函数调用可能会花费您执行时间和堆栈内存。这并不像听起来那么严重,大多数时候你不应该关心它,但它就在那里。
  • 当被滥用时,它可能会导致许多功能做部分工作并将其他部分从这里委托到那里。太多的论点也可能妨碍可读性。

基本上有两种类型的函数:执行一系列操作的函数(在某些情况下称为“过程”),以及执行某种形式的计算的函数。这两种类型通常混合在一个函数中,但记住这种区别会有所帮助。

各种函数之间还有另一个区别:保持状态的函数(如strtok)、可能具有副作用的函数(如printf)和“纯”函数(如sin)。类似函数strtok本质上是一种特殊的不同构造,在面向对象编程中称为对象。

于 2013-06-27T15:59:42.880 回答
1

您应该使用每个执行一个逻辑任务的函数,在抽象级别上,使每个函数的功能易于逻辑验证。例如:

void create_ui() {
    create_window();
    show_window();
}

void create_window() {
    create_border();
    create_menu_bar();
    create_body();
}

void create_menu_bar() {
    for(int i = 0; i < N_MENUS; i++) {
        create_menu(menus[i]);
    }
    assemble_menus();
}

void create_menu(arg) {
    ...
}

现在,就创建 UI 而言,这并不完全是一种方式(您可能希望传入并返回各种组件),但逻辑结构正是我想要强调的。将您的任务分解为几个子任务,并让每个子任务都有自己的功能。

不要试图避免优化功能。如果这样做是合理的,编译器将为您内联它们;如果没有,开销仍然很小。从中获得的可读性增益比将所有内容放在单一函数中可能获得的任何速度都重要得多。

至于你的标题问题,“尽可能”,没有。在合理的范围内,足以看到每个函数在舒适的抽象级别上做了什么,不多也不少。

于 2013-06-27T15:54:30.360 回答
0

您可以使用的一个条件:如果部分代码将被重用/重写,则将其放入函数中。

于 2013-06-27T15:44:30.260 回答
0

我想我会想到像乐高积木这样的功能。你有数百个可以组合成一个整体的小块。由于所有这些精心设计的通用小部件,您可以制作任何东西。如果你有一个看起来像整个房子的乐高积木,你就不能用它来建造飞机或火车。同样,一大段代码也没那么有用。

函数是您在设计项目时使用的积木。精心选择的将功能分离为小的、易于测试的、自包含的“功能”,使构建和维护整个项目变得容易。它们的好处 WAYYYYYYYY 超过了您可能认为存在的任何可能的效率问题。

老实说,编写任何大型项目的艺术在于如何将其分解为更小的部分,因此功能是其中的关键。

于 2013-06-27T15:56:00.443 回答