2013-05-19 4 views
3

Я пытаюсь понять некоторые из тонкостей препроцессора и компилятора 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; 
} 

против делать это с препроцессора:

#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 !!!» в двух отдельных ячейках памяти или является достаточно компилятором, чтобы сделать его одним местом памяти?

Если это две отдельные ячейки памяти, то разве не удобнее использовать глобальную переменную, а не расширение макросов для констант программы?

+0

Ваша глобальная переменная по-прежнему нуждается в одном для литерала, а другая для переменной. Компилятор, вероятно, будет использовать одинаковое расположение памяти для обоих литералов с #define. – chris

+0

Да, но скажу, что я использовал переменную 100 раз и макрос 100 раз, тогда будет значительная разница. Строковые литералы будут 100 раз в программе со 100 различными местоположениями, но у переменной будет только одна ячейка памяти (плюс одно место для переменной), не так ли (если компилятор не был эффективным)? – narnie

+0

Это зависит от того, должен ли тот же литерал храниться в том же месте. По крайней мере, это на C++. Я бы предположил, что это то же самое в C. – chris

ответ

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 с (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 

препроцессор.с

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 функций, вы можете увидеть, что инструкции идентичны.

+0

Что отличная идея для проверки. Хотел бы я подумать об этом. У меня есть vim, созданный для генерации файла .asm, когда я хочу видеть сборку. Не думал об этом, когда я размышлял над этим вопросом. Большое спасибо. Я запустил код в своей системе и посмотрю, делает ли он то же самое (я использую Linux Mint Debian Edition). – narnie

Смежные вопросы