usar - variables locales y globales ventajas y desventajas
Diferentes variables globales estáticas comparten la misma dirección de memoria (3)
Las respuestas hasta ahora han demostrado que deberían funcionar como están escritas, pero la respuesta real está solo en los comentarios, así que la publicaré como respuesta.
Lo que estás viendo es un artefacto depurador, no la situación real. En mi experiencia, esta debe ser su primera suposición de cualquier observación verdaderamente extraña dentro del depurador. Verifique la observación en el programa en ejecución real antes de continuar. Por ejemplo, una antigua declaración de debug printf.
Resumen
Tengo varios archivos fuente de C que declaran variables globales estáticas con nombres idénticos. Mi entendimiento es que la variable global estática en cada archivo debe ser visible solo dentro de ese archivo y no debe tener una vinculación externa aplicada, pero de hecho puedo ver al depurar que las variables con nombres idénticos comparten la misma dirección de memoria.
Es como si la palabra clave static
se ignorara y las variables globales se trataran como extern
. ¿Por qué es esto?
Código de ejemplo
foo.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someFooFunc(void) {
myVar = VALUE_B;
}
bar.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someBarFunc(void) {
myVar = VALUE_C;
}
baz.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someBazFunc(void) {
myVar = VALUE_D;
}
Observaciones de depuración
- Establezca puntos de interrupción en la línea
myVar = ...
dentro de cada función. - Llame a
someFooFunc
,someBarFunc
ysomeBazFunc
en ese orden desde main. - Dentro de
someFooFunc
myVar
inicialmente se establece enVALUE_A
, después de pasar sobre la línea, se establece enVALUE_B
. - Dentro de
someBarFunc
myVar
está, por algún motivo, inicialmente configurado enVALUE_B
antes de pasar por encima de la línea, noVALUE_A
como esperaría, lo que indica que el vinculador puede haber fusionado las variables globales separadas basándose en que tienen un nombre idéntico. - Lo mismo ocurre con
someBazFunc
cuando se llama. - Si uso el depurador para evaluar el valor de
&myVar
cuando en cada punto de interrupción se da la misma dirección.
Herramientas y banderas
Cadena de herramientas: GNU ARM GCC (6.2 2016q4)
Opciones del compilador:
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mlong-calls -O1 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -DDEBUG -DTRACE -DOS_USE_TRACE_ITM -DSTM32L476xx -I"../include" -I"../system/include" -I"../system/include/cmsis" -I"../system/include/stm32l4xx" -I"../system/include/cmsis/device" -I"../foo/inc" -std=gnu11 -MMD -MP -MF"foo/src/foo.d" -MT"foo/src/foo.o" -c -o "foo/src/foo.o" "../foo/src/foo.c"
Opciones de enlazador:
arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mlong-calls -O1 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -T mem.ld -T libs.ld -T sections.ld -nostartfiles -Xlinker --gc-sections -L"../ldscripts" -Wl,-Map,"myProj.map" --specs=nano.specs -o ...
así hace feliz al enlazador
.globl _start
_start: b _start
one.c
static unsigned int hello = 4;
static unsigned int one = 5;
void fun1 ( void )
{
hello=5;
one=6;
}
dos.c
static unsigned int hello = 4;
static unsigned int two = 5;
void fun2 ( void )
{
hello=5;
two=6;
}
tres.c
static unsigned int hello = 4;
static unsigned int three = 5;
void fun3 ( void )
{
hello=5;
three=6;
}
En primer lugar, si optimiza, este es un código completamente muerto y no debe esperar ver ninguna de estas variables. Las funciones no son estáticas por lo que no desaparecen:
Disassembly of section .text:
08000000 <_start>:
8000000: eafffffe b 8000000 <_start>
08000004 <fun1>:
8000004: e12fff1e bx lr
08000008 <fun2>:
8000008: e12fff1e bx lr
0800000c <fun3>:
800000c: e12fff1e bx lr
Si no optimizas entonces
08000000 <_start>:
8000000: eafffffe b 8000000 <_start>
08000004 <fun1>:
8000004: e52db004 push {r11} ; (str r11, [sp, #-4]!)
8000008: e28db000 add r11, sp, #0
800000c: e59f3020 ldr r3, [pc, #32] ; 8000034 <fun1+0x30>
8000010: e3a02005 mov r2, #5
8000014: e5832000 str r2, [r3]
8000018: e59f3018 ldr r3, [pc, #24] ; 8000038 <fun1+0x34>
800001c: e3a02006 mov r2, #6
8000020: e5832000 str r2, [r3]
8000024: e1a00000 nop ; (mov r0, r0)
8000028: e28bd000 add sp, r11, #0
800002c: e49db004 pop {r11} ; (ldr r11, [sp], #4)
8000030: e12fff1e bx lr
8000034: 20000000 andcs r0, r0, r0
8000038: 20000004 andcs r0, r0, r4
0800003c <fun2>:
800003c: e52db004 push {r11} ; (str r11, [sp, #-4]!)
8000040: e28db000 add r11, sp, #0
8000044: e59f3020 ldr r3, [pc, #32] ; 800006c <fun2+0x30>
8000048: e3a02005 mov r2, #5
800004c: e5832000 str r2, [r3]
8000050: e59f3018 ldr r3, [pc, #24] ; 8000070 <fun2+0x34>
8000054: e3a02006 mov r2, #6
8000058: e5832000 str r2, [r3]
800005c: e1a00000 nop ; (mov r0, r0)
8000060: e28bd000 add sp, r11, #0
8000064: e49db004 pop {r11} ; (ldr r11, [sp], #4)
8000068: e12fff1e bx lr
800006c: 20000008 andcs r0, r0, r8
8000070: 2000000c andcs r0, r0, r12
08000074 <fun3>:
8000074: e52db004 push {r11} ; (str r11, [sp, #-4]!)
8000078: e28db000 add r11, sp, #0
800007c: e59f3020 ldr r3, [pc, #32] ; 80000a4 <fun3+0x30>
8000080: e3a02005 mov r2, #5
8000084: e5832000 str r2, [r3]
8000088: e59f3018 ldr r3, [pc, #24] ; 80000a8 <fun3+0x34>
800008c: e3a02006 mov r2, #6
8000090: e5832000 str r2, [r3]
8000094: e1a00000 nop ; (mov r0, r0)
8000098: e28bd000 add sp, r11, #0
800009c: e49db004 pop {r11} ; (ldr r11, [sp], #4)
80000a0: e12fff1e bx lr
80000a4: 20000010 andcs r0, r0, r0, lsl r0
80000a8: 20000014 andcs r0, r0, r4, lsl r0
Disassembly of section .data:
20000000 <hello>:
20000000: 00000004 andeq r0, r0, r4
20000004 <one>:
20000004: 00000005 andeq r0, r0, r5
20000008 <hello>:
20000008: 00000004 andeq r0, r0, r4
2000000c <two>:
2000000c: 00000005 andeq r0, r0, r5
20000010 <hello>:
20000010: 00000004 andeq r0, r0, r4
hay tres variables de saludo creadas (ya debe notar que no hay ninguna razón para iniciar el depurador; todo esto puede responderse simplemente examinando el compilador y la salida del vinculador, el depurador solo se interpone en el camino)
800000c: e59f3020 ldr r3, [pc, #32] ; 8000034 <fun1+0x30>
8000034: 20000000 andcs r0, r0, r0
8000044: e59f3020 ldr r3, [pc, #32] ; 800006c <fun2+0x30>
800006c: 20000008 andcs r0, r0, r8
800007c: e59f3020 ldr r3, [pc, #32] ; 80000a4 <fun3+0x30>
80000a4: 20000010 andcs r0, r0, r0, lsl r0
20000000 <hello>:
20000000: 00000004 andeq r0, r0, r4
20000008 <hello>:
20000008: 00000004 andeq r0, r0, r4
20000010 <hello>:
20000010: 00000004 andeq r0, r0, r4
Cada función está accediendo a su propia versión separada del global estático. No se combinan en una global compartida.
NOTA: Entiendo que la plataforma de destino de OP es ARM, pero aún así, estoy publicando una respuesta en términos de x86. La razón es que no tengo ningún backend ARM a mano, mientras que la pregunta no se limita a una arquitectura en particular.
Aquí hay un simple banco de pruebas. Tenga en cuenta que estoy usando int
lugar de enum
typedef personalizado, ya que no debería importar en absoluto.
foo.c
static int myVar = 1;
int someFooFunc(void)
{
myVar += 2;
return myVar;
}
bar.c
static int myVar = 1;
int someBarFunc(void)
{
myVar += 3;
return myVar;
}
C Principal
#include <stdio.h>
int someFooFunc(void);
int someBarFunc(void);
int main(int argc, char* argv[])
{
printf("%d/n", someFooFunc());
printf("%d/n", someBarFunc());
return 0;
}
Lo estoy compilando en x86_64 Ubuntu 14.04 con GCC 4.8.4:
$ g++ main.c foo.c bar.c
$ ./a.out
3
4
Obtener dichos resultados de manera efectiva significa que myVar
variables myVar
en foo.c
y bar.c
son diferentes. Si nos fijamos en el desmontaje (por objdump -D ./a.out
):
000000000040052d <_Z11someFooFuncv>:
40052d: 55 push %rbp
40052e: 48 89 e5 mov %rsp,%rbp
400531: 8b 05 09 0b 20 00 mov 0x200b09(%rip),%eax # 601040 <_ZL5myVar>
400537: 83 c0 02 add $0x2,%eax
40053a: 89 05 00 0b 20 00 mov %eax,0x200b00(%rip) # 601040 <_ZL5myVar>
400540: 8b 05 fa 0a 20 00 mov 0x200afa(%rip),%eax # 601040 <_ZL5myVar>
400546: 5d pop %rbp
400547: c3 retq
0000000000400548 <_Z11someBarFuncv>:
400548: 55 push %rbp
400549: 48 89 e5 mov %rsp,%rbp
40054c: 8b 05 f2 0a 20 00 mov 0x200af2(%rip),%eax # 601044 <_ZL5myVar>
400552: 83 c0 03 add $0x3,%eax
400555: 89 05 e9 0a 20 00 mov %eax,0x200ae9(%rip) # 601044 <_ZL5myVar>
40055b: 8b 05 e3 0a 20 00 mov 0x200ae3(%rip),%eax # 601044 <_ZL5myVar>
400561: 5d pop %rbp
400562: c3 retq
Puede ver que las direcciones reales de las variables estáticas en diferentes módulos son de hecho diferentes: 0x601040
para foo.c
y 0x601044
para bar.c
Sin embargo, están asociados con un solo símbolo _ZL5myVar
, que realmente _ZL5myVar
lógica GDB.
Puede verificar que por medio de objdump -t ./a.out
:
0000000000601040 l O .data 0000000000000004 _ZL5myVar
0000000000601044 l O .data 0000000000000004 _ZL5myVar
Una vez más, diferentes direcciones, mismos símbolos. Cómo GDB resolverá este conflicto es puramente dependiente de la implementación.
Creo firmemente que es tu caso también. Sin embargo, para estar seguro, es posible que desee probar estos pasos en su entorno.