C中效率的"静态const"与"#define"

use*_*276 21 c performance constants c-preprocessor

我最近想知道什么之间的区别#define,并static constç为什么两种方法存在做同样的事情.我发现有些人在这里有类似的问题:

很多人谈论最佳实践和约定,并给出使用一个在另一个上的实际原因,例如需要将指针传递给常量,我可以用a static const而不是a #define.但是我还没有找到任何人谈论两者效率的比较.

根据我对C预处理器的理解,如果我有这样的声明:

#define CONSTANT 6
Run Code Online (Sandbox Code Playgroud)

我创建一个可以像这样使用的常量值

char[CONSTANT]char[6]在实际编译之前, 它实际上将被替换为此语句.

这对我来说似乎比使用a更有效, static const constant = 6;因为这将创建一个名为constant的变量,它将存在于堆栈中,我认为它会带来更多的行李而不是a #define.假设我需要一个常量,我可以选择使用预处理器#definestatic const语句,没有明显的理由选择一个而不是另一个,哪个更有效?我将如何自己测试呢?

Ven*_*esh 35

考虑以下2个测试文件

Test1.c:使用静态const foo.

// Test1.c uses static const..

#include <stdio.h>

static const foo = 6;

int main() {
    printf("%d", foo);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Test2.c:使用宏.

// Test2.c uses macro..

#include <stdio.h>

#define foo 6

int main() {
    printf("%d", foo);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

使用gcc -O0(默认)时相应的程序集等价如下,

Test1.c的程序集:

  0000000000000000 <main>:
   0:   55                      push   rbp
   1:   48 89 e5                mov    rbp,rsp
   4:   48 83 ec 20             sub    rsp,0x20
   8:   e8 00 00 00 00          call   d <main+0xd>
   d:   b8 06 00 00 00          mov    eax,0x6
  12:   89 c2                   mov    edx,eax
  14:   48 8d 0d 04 00 00 00    lea    rcx,[rip+0x4]        # 1f <main+0x1f>
  1b:   e8 00 00 00 00          call   20 <main+0x20>
  20:   b8 00 00 00 00          mov    eax,0x0
  25:   48 83 c4 20             add    rsp,0x20
  29:   5d                      pop    rbp
  2a:   c3                      ret
  2b:   90                      nop
Run Code Online (Sandbox Code Playgroud)

Test2.c的程序集:

  0000000000000000 <main>:
   0:   55                      push   rbp
   1:   48 89 e5                mov    rbp,rsp
   4:   48 83 ec 20             sub    rsp,0x20
   8:   e8 00 00 00 00          call   d <main+0xd>
   d:   ba 06 00 00 00          mov    edx,0x6
  12:   48 8d 0d 00 00 00 00    lea    rcx,[rip+0x0]        # 19 <main+0x19>
  19:   e8 00 00 00 00          call   1e <main+0x1e>
  1e:   b8 00 00 00 00          mov    eax,0x0
  23:   48 83 c4 20             add    rsp,0x20
  27:   5d                      pop    rbp
  28:   c3                      ret
  29:   90                      nop
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,都没有使用外部存储器.但不同之处在于,#definefoo值替换static const是指令,因此它将指令指针递增到下一条指令,并使用1个额外的寄存器来存储该值.

通过这个,我们可以说宏比静态常量好,但差异是最小的.

编辑:当使用-O3编译选项(即在优化时),test1.c和test2.c都评估相同.

0000000000000000 <main>:
   0:   48 83 ec 28             sub    rsp,0x28
   4:   e8 00 00 00 00          call   9 <main+0x9>
   9:   48 8d 0d 00 00 00 00    lea    rcx,[rip+0x0]        # 10 <main+0x10>
  10:   ba 06 00 00 00          mov    edx,0x6
  15:   e8 00 00 00 00          call   1a <main+0x1a>
  1a:   31 c0                   xor    eax,eax
  1c:   48 83 c4 28             add    rsp,0x28
  20:   c3                      ret
  21:   90                      nop
Run Code Online (Sandbox Code Playgroud)

因此,在优化时gcc对待它们static const#define同样对待它们.

  • 我认为通过优化来完成此测试并不是很有意义.我刚刚在我的机器上使用`-O2`编译,两个程序都生成**完全相同的程序集.所以不,我认为我们可以说宏不一定更好. (2认同)

Sha*_*our 6

测试简单优化问题的快速方法是使用Godbolt

对于您的特定问题,现代优化编译器应该能够为这两种情况生成相同的代码,并且实际上只是将它们优化为常量。我们可以通过以下程序看到这一点(现场观看):

#include <stdio.h>

#define CONSTANT 6
static const int  constant = 6;

void func()
{
  printf( "%d\n", constant ) ;
  printf( "%d\n", CONSTANT ) ;
}
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,访问都减少到以下内容:

movl    $6, %esi    #,
Run Code Online (Sandbox Code Playgroud)