字符串文字的全局变量与宏扩展

时间:2013-05-19 02:14:38

标签: c c-preprocessor string-literals

我正在尝试理解预处理器和 C 编译器(特别是gnu gcc)和字符串文字的一些复杂性。为字符串文字分配一个全局变量,只占用内存中的一个位置与使用#define预处理程序指令相比,效率更高吗?

如本示例所示,字符串文字在内存中并且可以多次访问:

#include <stdio.h>
#include <string.h>
char OUTPUT[20] = "Hello, world!!!";
int main (){
    printf("%s is %d characters long.\n", OUTPUT, strlen(OUTPUT));
    return 0;
}

vs使用预处理器:

#include <stdio.h>
#include <string.h>
#define OUTPUT "Hello, world!!!"

int main (){
    printf("%s is %d characters long.\n", OUTPUT, (int) strlen(OUTPUT));
    return 0;
}

翻译为:

#include <stdio.h>
#include <string.h>
#define OUTPUT "Hello, world!!!"

int main (){
    printf("%s is %d characters long.\n", "Hello, world!!!", (int) strlen("Hello, world!!!"));
    return 0;
}

我真正要问的是在使用预处理器的最后两个示例示例中,编译器是否有两个单独的“Hello,world !!!”实例在两个独立的内存位置,还是编译器足够智能,使其成为一个内存位置?

如果它是两个独立的内存位置,那么对于程序常量使用全局变量而不是宏扩展是不是更加资源友好?

1 个答案:

答案 0 :(得分:4)

您的编译器应该足够智能以存储该字符串的一个实例。您可以通过检查程序的汇编输出来验证这一点。

例如,使用GCC:

假设您的第一个示例名为“global.c”。

gcc -Wall -S global.c

.file   "global.c"
.globl  OUTPUT
.data
.align 16
.type   OUTPUT, @object
.size   OUTPUT, 20
OUTPUT:
.string "Hello, world!!!"
.zero   4
.section    .rodata
.LC0:
.string "%s is %d characters long.\n"
.text
.globl  main
.type   main, @function
main: 
// More code...

假设您的预处理器示例名为“preproc.c”。

gcc -Wall -S preproc.c
.file   "preproc.c"
.section    .rodata
.LC0:
.string "%s is %d characters long.\n"
.LC1:
.string "Hello, world!!!"
.text
.globl  main
.type   main, @function
main:
// More code...

在这两种情况下,只有一份“Hello,world !!!”并且“%s是%d个字符长。\ n”存在。在第一个示例中,您必须节省20个字符的空间,因为您的代码具有可修改的数组。如果你改变了这个

char OUTPUT[20] = "Hello, world!!!";

const char * const OUTPUT = "Hello, world!!!";

你会得到:

.file   "global.c"
.globl  OUTPUT
.section    .rodata
.LC0:
.string "Hello, world!!!"
.align 8
.type   OUTPUT, @object
.size   OUTPUT, 8
OUTPUT:
.quad   .LC0
.LC1:
.string "%s is %d characters long.\n"
.text
.globl  main
.type   main, @function
main:
// More code...

现在您只为指针和字符串节省空间。

在这种情况下哪种方式更好可以忽略不计,但我建议使用预处理器,以便字符串的范围保持在主函数内。

两者都通过优化发出几乎相同的代码。

Global.c with(const char * const OUTPUT):

gcc -Wall -O3 -S global.c

.file   "global.c"
.section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Hello, world!!!"
.LC1:
.string "%s is %d characters long.\n"
.section    .text.startup,"ax",@progbits
.p2align 4,,15
.globl  main
.type   main, @function
main:
.LFB44:
.cfi_startproc
subq    $8, %rsp
.cfi_def_cfa_offset 16
movl    $15, %ecx
movl    $.LC0, %edx
movl    $.LC1, %esi
movl    $1, %edi
xorl    %eax, %eax
call    __printf_chk
xorl    %eax, %eax
addq    $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE44:
.size   main, .-main
.globl  OUTPUT
.section    .rodata
.align 8
.type   OUTPUT, @object
.size   OUTPUT, 8
OUTPUT:
.quad   .LC0
.ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section    .note.GNU-stack,"",@progbits

Preproc.c

gcc -Wall -O3 -S preproc.c

    .file   "preproc.c"
.section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Hello, world!!!"
.LC1:
.string "%s is %d characters long.\n"
.section    .text.startup,"ax",@progbits
.p2align 4,,15
.globl  main
.type   main, @function
main:
.LFB44:
.cfi_startproc
subq    $8, %rsp
.cfi_def_cfa_offset 16
movl    $15, %ecx
movl    $.LC0, %edx
movl    $.LC1, %esi
movl    $1, %edi
xorl    %eax, %eax
call    __printf_chk
xorl    %eax, %eax
addq    $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE44:
.size   main, .-main
.ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section    .note.GNU-stack,"",@progbits

查看两个main函数,您可以看到说明相同。