Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 避免重复代码_C_C99 - Fatal编程技术网

C 避免重复代码

C 避免重复代码,c,c99,C,C99,假设我有: switch( choice ) { case A: stmt; do_stmt_related2A; break; case B: stmt; do_stmt_related2B; break; case C: something_different(); ... } 如何避免重复stmt代码 但是有什么解决办法吗? gcc扩展标签作为值在这种情况下看起来非常好 switch( choice

假设我有:

  switch( choice ) {
  case A:   
     stmt;
     do_stmt_related2A;
  break;

  case B:
     stmt;
     do_stmt_related2B;
  break;

  case C: something_different();
   ...
  }
如何避免重复stmt代码

但是有什么解决办法吗? gcc扩展标签作为值在这种情况下看起来非常好

   switch( choice ) {
     do {
     case A:  ptr = &&A_label;
     break;
     case B:  ptr = &&B_label;
     } while(0);
              stmt;
              goto *ptr;
     case C: ...
在ANSI-C中有什么技巧可以做到同样的事情吗? 编辑:当然我想到了函数/宏/内联。还有别的吗? 这也与性能无关。只是为了教育目的

如何避免重复stmt代码

通过将其放入函数并调用它


而且,不,您不知道这是否会降低您的应用程序的速度,直到您分析它并发现它是瓶颈。(如果确实是这样,使用宏,或者,如果是C99,将函数内联)

为什么不重构
stmt
(我假设这是一大块指令,而不是一行)到它自己的函数
do_stmt()
,然后调用它呢?比如:

switch( choice ) {
    case A:
        do_stmt();
        do_stmt_related2A;
        break;
    case B:
        do_stmt();
        do_stmt_related2B;
        break;
    case C: something_different();
        ...
}
gcc的伎俩真是太可怕了。我宁愿有可读的代码,而不是这些怪物

您应该始终假设继承您代码的程序员是一个杀人狂,他知道您住在哪里:-)

无论哪种方式都会有一些代码-您可以使用重复的代码,或者使用避免重复的代码。所以我很好奇
stmt有多复杂代码确实是

简单、干净的解决方案是将共享部分(
stmt
)移动到单独的函数中

void do_shared_stmt(void) {
 stmt;
}
/* .... */
swtich(choise) {
case A:
  do_shared_stmt();
  do_stmt_related2A();
  break;
case B:
  do_shared_stmt();
  do_stmt_related2B();
  break;
case C:
  something_different();
/* ... */
}
另一种解决方案(可能是可以接受的,取决于您的情况)是嵌套分支语句:

swtich(choise) {
case A:
case B:
  stmt;
  if(choise == A) {
    do_stmt_related2A();
  } else {̈́
    do_stmt_related2B();
  }
  break;
case C:
  something_different();
/* ... */
}
isA=false;
switch( choice ) { 
  case A:    
    isA=true;
  //nobreak
  case B: 
    stmt; 
    isA ? do_stmt_related2A : do_stmt_related2B;
  break; 
  case C: 
    something_different(); 
  break;
} 

我喜欢我在这里附加的东西。当然,您可能自己也想到了一个嵌套的switch语句,没有什么新的。此外,它只计算一次
choice

它还避免了标签地址的gcc构造,因此这里没有间接寻址。一个好的编译器应该能够很好地优化这样的东西

还要注意,
my_choice
是一个
intmax\u t
,因此它应该与
choice
可能具有的任何类型兼容

(为
输入
只是为了好玩,显然,它只适用于C99。您可以在内容周围添加一个{}来代替它,只需声明C89的
我的选择
。)


使用goto指针可能会导致代码速度变慢,因为它会关闭gcc的一些其他优化(或者在我上次阅读它时)。Gcc基本上认为,要跟上可能发生的事情可能太复杂了,并假设有更多的分支指令可以针对每个goto标签,而事实并非如此。如果您坚持尝试使用此方法,我建议您尝试使用整数和另一个开关/大小写,而不是goto。Gcc希望能够理解这一点

除此之外,对于许多声明来说,这可能不值得做这些工作,或者它实际上可能工作得更好。这在很大程度上取决于stmt实际上是什么

如果
stmt
确实昂贵或代码庞大,那么将
stmt
重构为
静态
函数可能会产生良好的结果

如果可以的话,您可以尝试的另一件事是将
stmt
从开关/机箱中取出,然后始终执行它。有时这是最便宜的方法,但这确实取决于
stmt
的实际功能

您可能要做的另一件事是重构所有的
stmt
do\u stmt\u related2A
,和
do_stmt\u related2A
全部进入文件
静态
函数,如下所示:

// args in this is just a psudocode place holder for whatever arguments are needed, and 
// not valide C code.
static void stmt_f(void (*continuation)(arg_list), args) {
   stmt; // This corresponds almost exactly to stmt in your code
   continuation(args);
}
static void do_stmt_related2A_f(args) {
    do_stmt_related2A;
}
static void do_stmp_related2B_f(args) {
    do_stmt_related2B;
}

...
    switch (condition) {
       case A:
          stmt_f(do_stmt_related2A_f, args);
          break;
       case B:
    ...
if (can_fly(choice))
   {
   stmt;
   }

switch( choice )
  {
  case A:   
     do_stmt_related2A;
     break;
  case B:
     do_stmt_related2B;
     break;
  case C:
     something_different();
     break;
  }
stmt_f末尾对continuation函数的调用是尾部调用,很可能会变成一个跳转而不是真正的调用。因为所有这些都是静态的,所以编译器可能会看到整个值集,这些值可以是连续函数,并进行更多优化,但我不知道这一点

除非stmt非常大,否则这很可能是一个不值得的微观优化。如果您真的想知道,那么您应该编译成汇编,并尝试查看编译器对原始代码的真正作用。它很可能比你想象的做得更好


哦,最后一件你可以尝试的事情是,如果你能控制A,B,C的实际值。。然后,您可以确保具有相似前缀的值具有相邻的值。如果A和B确实彼此相邻,并且如果编译器决定需要将开关/大小写分解为几个不同的跳转表,那么它可能会将A和B放在同一个跳转表中,并且还可以看到它们具有相同的前缀,并为您提取代码。如果不共享该前缀的C与A或B不相邻,则更可能出现这种情况,但总体代码可能更糟。

您只需要两个控制结构。一个用于指令执行一阶指令,另一个用于指令执行二阶指令

switch (state) {
    case A:
    case B:
        stmt;

        switch (state) {
            case A:
                do_stmt_related2A;
                break;
            case B:
                do_stmt_related2B;
                break;
        }

        break;

    case C:
        something_different();
        ...
}

还值得注意的是,
开关
不太适合二阶控制结构,您可能希望使用更传统的条件分支。最后,关于goto标签指针的问题的真正答案是,这就是子程序链接的用途。如果您的指令比单个表达式更复杂,那么您可以也应该使用函数。

我可能会这样做:

void do_shared_stmt(void) {
 stmt;
}
/* .... */
swtich(choise) {
case A:
  do_shared_stmt();
  do_stmt_related2A();
  break;
case B:
  do_shared_stmt();
  do_stmt_related2B();
  break;
case C:
  something_different();
/* ... */
}
void do_stmt(int choice)
{
    stmt;
    switch(choice)
    {
         case A:
             do_stmt_related2A;
             break;
         case B:
             do_stmt_related2B;
             break;
    }  
}
/* ... */
switch(choice)
{
    case A:
    case B:
        do_stmt(choice);
        break;
    case C:
         something_different();
...

下面是函数调用或辅助开关语句的一种替代方法:

swtich(choise) {
case A:
case B:
  stmt;
  if(choise == A) {
    do_stmt_related2A();
  } else {̈́
    do_stmt_related2B();
  }
  break;
case C:
  something_different();
/* ... */
}
isA=false;
switch( choice ) { 
  case A:    
    isA=true;
  //nobreak
  case B: 
    stmt; 
    isA ? do_stmt_related2A : do_stmt_related2B;
  break; 
  case C: 
    something_different(); 
  break;
} 

但是,我不能说我真的推荐它作为一种编码风格。

除了将通用代码重构为子函数的常见想法外,使用标准C功能重构代码的最简单方法可能是:

if (choice == A || choice == B) {
    stmt;
}

switch( choice ) {
  case A:   
    do_stmt_related2A;
    break;

  case B:
    do_stmt_related2B;
    break;

  case C:
    something_different();
    ...
}
它清晰地表达了您想要做的事情,并且避免了开关内部的开关,这种开关会阻止某些编译器有效地优化代码。

如果您需要 L1: val | |------------------- | | | A B C | | | stmt stmt crap | | Afunk Bfunk L2: val | |------------------- stmt | |--------- | | | | A B C | | | Afunk Bfunk crap
// function pointer to B function
void (*func)(void) = do_stmt_related2B;
switch(choice) {
case A:
    func = do_stmt_related2A; // change function pointer to A function
case B:
    stmt;
    func(); // call function pointer
    break;

case C:
    something_different();
    ...
}