【问题标题】:if else code blocks ... or one if with multiple OR ( | |) conditions within it ? in Cif else 代码块...或者一个 if 中有多个 OR (| |) 条件?在 C 中
【发布时间】:2013-05-20 19:49:36
【问题描述】:

我正在用 C 编写一个小型文本匹配程序,它主要检查某些字符串中是否存在一些字符(以 char 数组的形式)。它现在可以工作了,我有一个这样的代码块:

if (c == 'A') return 1;
else if (c == 'B') return 1;
else if (c == 'C') return 1;
....
....
else if (c == 'Z') return 1;
else return 0;

上面的块更快吗?或者这会更快吗?

if (c == 'A' || c == 'B' || c == 'C' ||....|| c == 'Z') return 1;
else return 0;

快速是指字面上的快速,即如果我从程序开始到结束运行一个简单的计时器,这可能会缩短执行时间?

【问题讨论】:

  • 在一个优秀的优化编译器眼里都是一样的。
  • 只需运行基准测试并亲自查看。
  • 顺便说一句,char 是一个类型的关键字。您不应将其用作标识符。
  • 我的母语不是英语。我的意思是禁止使用char作为变量标识符。
  • @BasileStarynkevitch:对不起,我不是在批评你的语言,只是对 OP 声称使用 char 作为标识符表示怀疑。我对发帖者不复制和粘贴他们提交给编译器的实际代码的频率感到沮丧,而是在新制造的代码中引入错误。当然,在 1970 年代甚至 1980 年代,我们不得不重新输入内容,但现在不是已经过时了吗?

标签: c string performance if-statement


【解决方案1】:

我建议执行以下操作:

#include <ctype.h>

...

return isupper(c)

而不是手动检查所有这些。标准 C 库函数相当快,因此性能应该可以接受。

【讨论】:

  • +1,但在请求使用 ctype.h 中的 is*() 函数之前,请记住 Basile Starynkevitch 的说明。
  • 我实际上并没有检查字母是大写还是小写,它只是代表我正在尝试做的事情。无论如何,谢谢。
【解决方案2】:

经验法则是,如果您不确定这些小性能问题是否真的值得,则不必担心。

在任何情况下,如果您想检查任何 A-Z 字母,那么这个 (请注意,这是对所用字符编码的假设,在 AZ 之间不应有任何外部符号否则这将不起作用)

if (c >= 'A' && c <= 'Z')

肯定是一种更简单的方法。

不要忘记长时间 if else 链接表达式值,您甚至可以使用switch 语句:

switch (exp) {
  case 'A':
  case 'B':
  case 'C':
  ...
    return 1;
  default: return 0;
}

switch 在某些情况下可能会稍微快一些,因为根据编译器的不同,它可以使用查找表,但我们实际上是在谈论微秒。

为了完整起见,C标准库有两个方法isupperisaplha可以使用:

if (isupper(c)) // c is an alphabetic uppercase character

【讨论】:

  • C 标准不保证除大写字母以外的任何字符都没有介于'A''Z' 之间的值,因此除非对字符集有额外保证,否则以这种方式比较是不合适的在使用的特定 C 实现中。此外,标识符不能具有名称 char,因为它是保留关键字。
  • @JensGustedt:在 gcc 4.2.1 上使用 -std=c99 编译和工作正常,没有任何空语句 ;,您指的是哪个标准?
  • @EricPostpischil:虽然在技术上是正确的,但如今几乎没有人编写与此类系统兼容的代码(例如 EBCDIC)。出于所有实际目的,这是一个非常安全的假设,即 'A''Z' 之间的字符正好是 26 个大写字母,仅此而已。
  • @AdamRosenfield:这是一个相当安全的假设,但考虑到isupper() 的存在,这不是一个必要的假设(尽管isupper() 的行为可能会因当前的语言环境)。
  • @EricPostpischil:这只是挑剔,没有理由这样做。此代码适用于 99.5% 的情况,在编写有关此代码的代码时必须在外部完成编码的假设,但必须使用在 AZ 之间具有其他符号的编码是相当不寻常的
【解决方案3】:

优化编译器将同样处理这两种形式。

将此类微优化留给编译器。同样重要的是源代码的可读性。

(当然你需要启用优化,对于GCC -e.g. 最近的 GCC 4.8 - 你将使用 gcc -O2 编译)。

而且您确实需要进行基准测试以确定(因为大量其他因素也很重要:特别是缓存位置)什么是最好的。你甚至可以使用一些更花哨的算法(例如,在测试更频繁的字母,如E,然后测试罕见的字母,如Z)。查找search trees 了解更多信息。

例如查看生成的汇编代码(使用gcc -O2 -fverbose-asm -S 获取它)或查看内部表示,例如使用the MELT probe(或传递-fdump-tree-all - 给你许多转储文件- 给gcc)。

使用 GCC 扩展,对于您的具体示例,您甚至可以像这样编写 case ranges

 switch (c) {
    case 'A' ... 'Z': return 1;
    default: return 0;
 }

上述情况范围假定字符编码是ASCII 的超集。它不适用于EBCDIC

其实开关优化很复杂。见this paper等......

实际上,您想使用 &lt;ctype.h&gt; 中的 isalpha(3)(在 C99 标准中)。

测试什么是字母并不是那么简单:éИ 适合你吗?对我来说是一个(它们都是元音,并且都需要 UTF8 中的一个以上字节)

注意常见的UTF-8 编码:某些字母(尤其是来自非英语语言或字母表的字母)使用多个字节进行编码。看例如Glib Unicode Manipulation 函数。

【讨论】:

    【解决方案4】:

    如果你有更多的条件,开关块会更好:

    switch(c) {
       case 'A':
       case 'B':
       // (...)
       case 'Z': return 1;
       default: return 0;
    }
    

    【讨论】:

    • 答案中的switch语句是正确的;您可以有多个 case 标签后跟一个语句。您在其他答案的 cmets 中犯了同样的错误。
    • 哎呀,对不起,我把事情弄糊涂了。
    【解决方案5】:

    在不涉及编译器优化、程序集生成和更好的算法的情况下,其他发帖人错过的是具有多个测试用例和多个 OR 的单个 if (||) 在第一次匹配后会短路。实际上,代码的第一个和第二个 sn-ps 将在速度方面生成等效的 ASM。

    【讨论】:

      【解决方案6】:

      正如其他提到的,没有什么不同。在两种ifs 条件下都会生成相同数量的cmp 指令(不考虑编译器的任何优化)

      编辑:

      考虑一下这个C函数:

      int is_letter2(int c)
      {
        if(c == 'A') return 1;
        else if(c == 'B') return 1;
        else if(c == 'C') return 1;
        else if(c == 'D') return 1;
        else if(c == 'E') return 1;
        else if(c == 'F') return 1;
        else if(c == 'G') return 1;
        else if(c == 'H') return 1;
        else if(c == 'I') return 1;
        else if(c == 'J') return 1;
        else if(c == 'K') return 1;
        else if(c == 'L') return 1;
        else if(c == 'M') return 1;
        else if(c == 'N') return 1;
        else if(c == 'O') return 1;
        else if(c == 'P') return 1;
        else if(c == 'Q') return 1;
        else if(c == 'R') return 1;
        else if(c == 'S') return 1;
        else if(c == 'T') return 1;
        else if(c == 'U') return 1;
        else if(c == 'V') return 1;
        else if(c == 'W') return 1;
        else if(c == 'X') return 1;
        else if(c == 'Y') return 1;
        else if(c == 'Z') return 1;
        else return 0;
      }
      
      int is_letter(int c)
      {
        if(c == 'A' ||c == 'B' ||c == 'C' ||c == 'D' ||c == 'E' ||c == 'F' ||c == 'G' ||c == 'H' ||c == 'I' ||c == 'J' ||c == 'K' ||c == 'L' ||c == 'M' ||c == 'N' ||c == 'O' ||c == 'P' ||c == 'Q' ||c == 'R' ||c == 'S' ||c == 'T' ||c == 'U' ||c == 'V' ||c == 'W' ||c == 'X' ||c == 'Y' ||c == 'Z')
          return 1;
       else return 0;
      }
      

      assembly 输出:几乎是 same。签出:

      is_letter2:
      .LFB1:
          .cfi_startproc
          pushl   %ebp
          .cfi_def_cfa_offset 8
          .cfi_offset 5, -8
          movl    %esp, %ebp
          .cfi_def_cfa_register 5
          cmpl    $65, 8(%ebp)
          jne .L3
          movl    $1, %eax
          jmp .L4
      .L3:
          cmpl    $66, 8(%ebp)
          jne .L5
          movl    $1, %eax
          jmp .L4
      .L5:
          cmpl    $67, 8(%ebp)
          jne .L6
          movl    $1, %eax
          jmp .L4
      .L6:
          cmpl    $68, 8(%ebp)
          jne .L7
          movl    $1, %eax
          jmp .L4
      .L7:
          cmpl    $69, 8(%ebp)
          jne .L8
          movl    $1, %eax
          jmp .L4
      .L8:
          cmpl    $70, 8(%ebp)
          jne .L9
          movl    $1, %eax
          jmp .L4
      .L9:
          cmpl    $71, 8(%ebp)
          jne .L10
          movl    $1, %eax
          jmp .L4
      .L10:
          cmpl    $72, 8(%ebp)
          jne .L11
          movl    $1, %eax
          jmp .L4
      .L11:
          cmpl    $73, 8(%ebp)
          jne .L12
          movl    $1, %eax
          jmp .L4
      .L12:
          cmpl    $74, 8(%ebp)
          jne .L13
          movl    $1, %eax
          jmp .L4
      .L13:
          cmpl    $75, 8(%ebp)
          jne .L14
          movl    $1, %eax
          jmp .L4
      .L14:
          cmpl    $76, 8(%ebp)
          jne .L15
          movl    $1, %eax
          jmp .L4
      .L15:
          cmpl    $77, 8(%ebp)
          jne .L16
          movl    $1, %eax
          jmp .L4
      .L16:
          cmpl    $78, 8(%ebp)
          jne .L17
          movl    $1, %eax
          jmp .L4
      .L17:
          cmpl    $79, 8(%ebp)
          jne .L18
          movl    $1, %eax
          jmp .L4
      .L18:
          cmpl    $80, 8(%ebp)
          jne .L19
          movl    $1, %eax
          jmp .L4
      .L19:
          cmpl    $81, 8(%ebp)
          jne .L20
          movl    $1, %eax
          jmp .L4
      .L20:
          cmpl    $82, 8(%ebp)
          jne .L21
          movl    $1, %eax
          jmp .L4
      .L21:
          cmpl    $83, 8(%ebp)
          jne .L22
          movl    $1, %eax
          jmp .L4
      .L22:
          cmpl    $84, 8(%ebp)
          jne .L23
          movl    $1, %eax
          jmp .L4
      .L23:
          cmpl    $85, 8(%ebp)
          jne .L24
          movl    $1, %eax
          jmp .L4
      .L24:
          cmpl    $86, 8(%ebp)
          jne .L25
          movl    $1, %eax
          jmp .L4
      .L25:
          cmpl    $87, 8(%ebp)
          jne .L26
          movl    $1, %eax
          jmp .L4
      .L26:
          cmpl    $88, 8(%ebp)
          jne .L27
          movl    $1, %eax
          jmp .L4
      .L27:
          cmpl    $89, 8(%ebp)
          jne .L28
          movl    $1, %eax
          jmp .L4
      .L28:
          cmpl    $90, 8(%ebp)
          jne .L29
          movl    $1, %eax
          jmp .L4
      .L29:
          movl    $0, %eax
      .L4:
          popl    %ebp
          .cfi_def_cfa 4, 4
          .cfi_restore 5
          ret
          .cfi_endproc
      .LFE1:
          .size   is_letter2, .-is_letter2
          .globl  is_letter
          .type   is_letter, @function
      is_letter:
      .LFB2:
          .cfi_startproc
          pushl   %ebp
          .cfi_def_cfa_offset 8
          .cfi_offset 5, -8
          movl    %esp, %ebp
          .cfi_def_cfa_register 5
          cmpl    $65, 8(%ebp)
          je  .L31
          cmpl    $66, 8(%ebp)
          je  .L31
          cmpl    $67, 8(%ebp)
          je  .L31
          cmpl    $68, 8(%ebp)
          je  .L31
          cmpl    $69, 8(%ebp)
          je  .L31
          cmpl    $70, 8(%ebp)
          je  .L31
          cmpl    $71, 8(%ebp)
          je  .L31
          cmpl    $72, 8(%ebp)
          je  .L31
          cmpl    $73, 8(%ebp)
          je  .L31
          cmpl    $74, 8(%ebp)
          je  .L31
          cmpl    $75, 8(%ebp)
          je  .L31
          cmpl    $76, 8(%ebp)
          je  .L31
          cmpl    $77, 8(%ebp)
          je  .L31
          cmpl    $78, 8(%ebp)
          je  .L31
          cmpl    $79, 8(%ebp)
          je  .L31
          cmpl    $80, 8(%ebp)
          je  .L31
          cmpl    $81, 8(%ebp)
          je  .L31
          cmpl    $82, 8(%ebp)
          je  .L31
          cmpl    $83, 8(%ebp)
          je  .L31
          cmpl    $84, 8(%ebp)
          je  .L31
          cmpl    $85, 8(%ebp)
          je  .L31
          cmpl    $86, 8(%ebp)
          je  .L31
          cmpl    $87, 8(%ebp)
          je  .L31
          cmpl    $88, 8(%ebp)
          je  .L31
          cmpl    $89, 8(%ebp)
          je  .L31
          cmpl    $90, 8(%ebp)
          jne .L32
      .L31:
          movl    $1, %eax
          jmp .L33
      .L32:
          movl    $0, %eax
      .L33:
          popl    %ebp
          .cfi_def_cfa 4, 4
          .cfi_restore 5
          ret
          .cfi_endproc
      

      在简历中,是同一个程序。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-27
        • 2017-07-29
        • 2020-05-31
        • 1970-01-01
        • 1970-01-01
        • 2014-12-04
        相关资源
        最近更新 更多