为什么某些功能非常长? (学术研究需要的想法!)

时间:2009-07-14 19:52:36

标签: c function code-structure mcc

我正在写一个关于极长功能的小型学术研究项目。显然,我不是在寻找examples for bad programming,而是寻找100,200和600行长函数的例子。

我将使用为Master's degree written in the Hebrew University编写的脚本调查Linux内核源代码,该脚本测量不同的参数,如代码行数,function complexity(由MCC测量)和其他好东西。顺便说一下,这是一个关于代码分析和推荐阅读材料的简洁研究。

我很感兴趣,如果你可以想出任何函数应该特别长的任何理由吗?我会研究C,但是任何语言的例子和参数都会有很大的用处。

15 个答案:

答案 0 :(得分:18)

我可能会因此而感到害怕,但可读性。高度串行但独立的执行可以分解为N个函数调用(其他地方使用的函数)并没有真正受益于分解。除非你计算在功能长度上满足任意最大值作为一种好处。

我宁愿滚动N个函数大小的代码块,而不是浏览整个文件,点击N个函数。

答案 1 :(得分:14)

switch语句中有很多值?

答案 2 :(得分:11)

从其他源生成的任何内容,即来自解析器生成器或类似的有限状态机。如果它不是供人类消费的,则美学或可维护性问题无关紧要。

答案 3 :(得分:4)

随着时间的推移,函数会变得更长,特别是如果它们被许多开发人员修改过。

例证:我最近(约1年或2年前)重构了2001年左右的一些遗留图像处理代码,其中包含几个几千行功能。不是几千行文件 - 几个几千行功能。

多年来,他们添加了如此多的功能,而没有真正努力正确地重构它们。

答案 4 :(得分:3)

阅读关于子程序的McConnell代码完成章节,它有关于何时应该将功能分解为函数的指南和指针。如果你有一些不适用这些规则的算法,那么这可能是一个长函数的好理由。

答案 5 :(得分:2)

生成的代码可以生成非常长的函数。

答案 6 :(得分:2)

我最近编写的唯一内容是它们在使它们变小或者使代码可读性降低方面没有太大成效。超过一定长度的函数在某种程度上本质上是坏的概念只是盲目的教条。像任何盲目应用的教条一样,在任何特定情况下都可以让追随者真正想到适用的内容......

最近的例子......

解析并将具有简单name = value结构的配置文件验证到一个数组中,在找到它时转换每个值,这是一个庞大的switch语句,每个配置选项一个案例。为什么?我本来可以分成许多对5/6行琐碎函数的调用。这将为我班增加约20名私人会员。它们都不会在其他任何地方重复使用。将它分解成较小的块只是没有增加足够的价值值得它,所以它自原型以来一直如此。如果我想要另一个选项,请添加另一个案例。

另一种情况是同一个应用程序中的客户端和服务器通信代码及其客户端。大量的读/写调用可能会失败,在这种情况下我会保释并返回false。所以这个函数基本上是线性的,几乎每次调用后都有保释点(如果失败,返回)。再一次,没有什么可以让它变得更小,也没有办法让它变得更小。

我还应该补充一点,我的大多数功能都是一些“屏幕”,我在更复杂的领域努力将它保持为一个“屏幕”,因为我可以立即查看整个功能。对于本质上基本上是线性的并且没有很多复杂的循环或条件的函数来说,这是好的,所以流程很简单。 作为最后一点,我更倾向于在决定重构哪些代码时应用成本效益推理,并相应地优先考虑。有助于避免永久性的半成品项目。

答案 7 :(得分:1)

有时我会发现自己正在编写一个平面文件(供第三方使用),其中包含所有链接的标题,预告片和详细记录。为计算摘要而设置一个长函数比设计一些通过许多小函数来回传递值的方案更容易。

答案 8 :(得分:1)

我认为有一点意思是不同的语言和工具具有与功能相关的不同词汇范围。

例如,Java允许您使用注释来抑制警告。可能需要限制注释的范围,因此您可以将该功能保持为此目的。在另一种语言中,将该部分分解为它自己的功能可能完全是任意的。

有争议:在JavaScript中,我倾向于仅为了重用代码而创建函数。如果一个片段只在一个地方执行,我发现在函数引用的意大利面后跳转文件会很麻烦。我认为闭包有助于加强[父]功能。由于JS是一种解释型语言,实际代码是通过线路发送的,因此保持代码长度很小是很好的 - 创建匹配的声明和引用没有帮助(这可能被认为是过早的优化)。在我决定将其删除以达到“保持功能简短”的明确目的之前,函数必须在JS中使用相当长的时间。

再次在JS中,有时整个“类”在技术上是一个具有许多封闭子函数的函数,但有一些工具可以帮助处理它。

另一方面,在JS中,变量具有函数长度的范围,因此这是一个可能限制给定函数长度的因素。

答案 9 :(得分:1)

我遇到的很长时间的功能都不是用C语言写的,所以你必须决定这是否适用于你的研究。我想到的是一些PowerBuilder函数,这些函数长达数百行,原因如下:

  • 它们是在十多年前由当时没有编码标准的人写的。
  • 开发环境使创建函数变得有点困难。几乎没有一个好的借口,但这是有时会阻碍你正常工作的小事之一,我想有人只是懒得。
  • 这些功能随着时间的推移不断发展,增加了代码和复杂性。
  • 函数包含巨大的循环,每次迭代可能以不同的方式处理不同类型的数据。使用数十(!)的局部变量,一些成员变量和一些全局变量,它们变得非常复杂。
  • 那么古老而丑陋,没有人敢把它们重构成更小的部分。处理了这么多特殊案件,将它们分开就是在寻找麻烦。

这是另一个显而易见的错误编程实践符合现实的地方。虽然任何一年级的CS学生都可以说这些野兽很糟糕,但是没有人愿意花钱让它们看起来更漂亮(考虑到至少目前它们仍然可以提供)。

答案 10 :(得分:1)

到目前为止,我看/写的最常见的是长switch语句或if / else半开关语句,用于不能在这种语言的switch语句中使用的类型(已经提到过几次)。生成的代码是一个有趣的案例,但我在这里专注于人工编写的代码。看看我目前的项目,上面没有包含的唯一真正长的功能(296 LOC / 650 LOT)是我用的一些牛仔代码,作为我计划将来使用的代码生成器的输出的早期评估。我肯定会重构它,将其从此列表中删除。

许多年前,我正在研究一些具有长期功能的科学计算软件。该方法使用大量局部变量并重构该方法,从而导致每次分析产生可测量的差异。即使这部分代码的1%的改进也节省了数小时的计算时间,因此功能保持很长时间。从那以后我学到了很多东西,所以我不能说我今天如何处理这种情况。

答案 11 :(得分:1)

速度:

  • 调用函数意味着推送到堆栈,然后跳转,然后再次存储在堆栈中,然后再次跳转。如果你对函数使用参数,你通常会再推几次。

考虑一个循环:

for...
   func1

在循环中,所有这些推动和跳跃都可能是一个因素。

这很大程度上是通过Inline FunctionsC99上的呈现以及之前非正式地解决的,但是之前编写的一些代码,或者考虑到兼容性而创建的代码可能已经很久了。< / p>

Inline也有流量,有些在Inline Functions link上有描述。

修改

作为调用函数如何使程序变慢的示例:

4         static void
5 do_printf()
6 {
7         printf("hi");
8 }
9         int
10 main()
11 {
12         int i=0;
13         for(i=0;i<1000;++i)
14                 do_printf();
15 }

这产生(GCC 4.2.4):

 .
 . 
 jmp    .L4
 .L5:
call    do_printf
addl    $1, -8(%ebp)
 .L4:
cmpl    $999, -8(%ebp)
jle .L5

 .
 .
do_printf:
pushl   %ebp
movl    %esp, %ebp
subl    $8, %esp
movl    $.LC0, (%esp)
call    printf
leave
ret

针对:

         int
 main()
 {
         int i=0;
         for(i=0;i<1000;++i)
                 printf("hi");
 }

或反对:

 4         static inline void __attribute__((always_inline)) //This is GCC specific!
 5 do_printf()
 6 {
 7         printf("hi");
 8 }

两者都生产(GCC 4.2.4):

jmp .L2
.L3:
movl    $.LC0, (%esp)
call    printf
addl    $1, -8(%ebp)
.L2:
cmpl    $999, -8(%ebp)
jle .L3

哪个更快。

答案 12 :(得分:0)

XML解析代码通常在一个设置函数中有大量的转义字符处理。

答案 13 :(得分:0)

我处理的函数(不是写入)变得很长,因为它们被扩展和扩展,没有人花时间重新考虑函数。他们只是不断添加逻辑功能而不考虑大局。

我处理了很多cut-n-paste开发......

因此,对于论文而言,要考虑的一个方面是糟糕的维护计划/周期等。

答案 14 :(得分:0)

尚未明确提及的一些想法:

  • 重复性任务,例如该函数读取一个包含190列的数据库表,并且必须将它们作为平面文件输出(假设需要单独处理列,因此不会对所有列进行简单的循环)。当然你可以创建19个函数,每个函数输出10列,但这不会使程序更好。
  • 复杂,冗长的API,如Oracle's OCI。当看似简单的动作需要大量代码时,很难将其分解为任何有意义的小函数。