字符串文字与整数中的C ++ constexpr vs宏

Posted

技术标签:

【中文标题】字符串文字与整数中的C ++ constexpr vs宏【英文标题】:C++ constexpr vs macro in string literal vs integer 【发布时间】:2016-11-04 08:24:54 【问题描述】:

我正试图找出使用constexprpreprocessor macro 定义整数和字符串字面量之间的“幕后”区别。

#define FIRST_STRING "first_stringer"
constexpr char second_string[] = "second_stringer";

#define FIRST_INT 1234
constexpr int second_int = 12345;

int main () 
       
    printf("%s\n", second_string);
    printf("%s\n", FIRST_STRING);

    printf("%d\n", FIRST_INT);
    printf("%d\n", second_int);
    return 0;


void hello() 
    printf("%s\n", second_string);
    printf("%s\n", FIRST_STRING);

    printf("%d\n", FIRST_INT);
    printf("%d\n", second_int);

使用g++ -S main.cpp -std=c++11 编译时会给出以下汇编输出

    .file   "main.cpp"
    .section    .rodata
.LC0:
    .string "first_stringer"
.LC1:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $_ZL13second_string, %edi
    call    puts
    movl    $.LC0, %edi
    call    puts
    movl    $1234, %esi
    movl    $.LC1, %edi
    movl    $0, %eax
    call    printf
    movl    $12345, %esi
    movl    $.LC1, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .globl  _Z5hellov
    .type   _Z5hellov, @function
_Z5hellov:
.LFB1:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $_ZL13second_string, %edi
    call    puts
    movl    $.LC0, %edi
    call    puts
    movl    $1234, %esi
    movl    $.LC1, %edi
    movl    $0, %eax
    call    printf
    movl    $12345, %esi
    movl    $.LC1, %edi
    movl    $0, %eax
    call    printf
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE1:
    .size   _Z5hellov, .-_Z5hellov
    .section    .rodata
    .align 16
    .type   _ZL13second_string, @object
    .size   _ZL13second_string, 16
_ZL13second_string:
    .string "second_stringer"
    .align 4
    .type   _ZL10second_int, @object
    .size   _ZL10second_int, 4
_ZL10second_int:
    .long   12345
    .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
    .section    .note.GNU-stack,"",@progbits

在检查汇编代码时,我们可以观察到在两个函数中我们都有指令movl $1234, %esimovl $12345, %esi。 IE。宏整数文字和constexp int 之间没有明显的区别,即使constexpr int 存储在单独的部分_ZL10second_int

另一方面,对于字符串文字,我们看到指令movl $_ZL13second_string, %edimovl $.LC0, %edi 将它们各自的字符串文字映射到两个不同的部分。

这两个部分有什么区别?加载可执行文件后,它们是否映射到主内存的不同部分?如果是,访问某个部分是否比另一部分更快?我知道我可以分析性能影响,但我想了解这两个部分之间的理论原因和区别。

【问题讨论】:

如果两次使用字符串#define,则必须依赖编译器对字符串文字进行去重,否则会浪费可执行空间。 constexpr 保证唯一性。 【参考方案1】:

这些在功能上是等效的。请注意,两种情况下的实际数据都是使用.string 指令声明的。唯一的区别在于标签名称,其中实际上是 C++ 对象 (second_string) 的名称有一个错误名称,而宏只有一个通用标签名称。

如果您在 Linux 中的可执行文件上运行 objdump,您会注意到两个字符串都存储在 .rodata 部分中:

String dump of section '.rodata':
  [     4]  %s^J
  [     8]  first_stringer
  [    17]  %d^J
  [    20]  second_stringer

【讨论】:

【参考方案2】:

一个原因是宏由预处理器处理,而 constexpr 由编译器处理。所以 constexpr 不仅仅是一个字符串替换,它还检查类型,例如。

所以即使汇编是一样的,在我看来,constexpr 是一个更好的选择

【讨论】:

以上是关于字符串文字与整数中的C ++ constexpr vs宏的主要内容,如果未能解决你的问题,请参考以下文章

使用 constexpr 函数替代 reinterpret_cast

C零基础课程-09-文字数据的定义和使用

constexpr 数组与双端队列,内存利用率

c++11中的constexpr概念

constexpr 和不推荐使用的转换警告

为啥 C 字符文字是整数而不是字符?