Ваш компилятор должен быть достаточно умным, чтобы хранить один экземпляр строки. Вы можете проверить это, проверив выходы сборки для своих программ.
Например, с помощью 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
функций, вы можете увидеть, что инструкции идентичны.
Ваша глобальная переменная по-прежнему нуждается в одном для литерала, а другая для переменной. Компилятор, вероятно, будет использовать одинаковое расположение памяти для обоих литералов с #define. – chris
Да, но скажу, что я использовал переменную 100 раз и макрос 100 раз, тогда будет значительная разница. Строковые литералы будут 100 раз в программе со 100 различными местоположениями, но у переменной будет только одна ячейка памяти (плюс одно место для переменной), не так ли (если компилятор не был эффективным)? – narnie
Это зависит от того, должен ли тот же литерал храниться в том же месте. По крайней мере, это на C++. Я бы предположил, что это то же самое в C. – chris