2012-06-16 15 views
8

Tengo el siguiente programa. Me pregunto por qué produce -4 en la siguiente máquina de 64 bits. ¿Cuál de mis suposiciones salió mal?c & gcc: Crecimiento y alineación de la pila: para una máquina de 64 bits

[ubuntu Linux 3.2.0-23-generiC# 36-Ubuntu SMP Tue Apr 10 20:39:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux]

  1. En el arriba de la máquina y del compilador gcc, por defecto b debe presionarse primero y un segundo. La pila crece hacia abajo. Entonces, b debería tener una dirección más alta y una dirección más baja. Entonces el resultado debería ser positivo. Pero tengo -4. ¿Alguien puede explicar esto?

  2. Los argumentos son dos caracteres que ocupan 2 bytes en el marco de la pila. Pero vi la diferencia como 4 donde estoy esperando 1. Incluso si alguien dice que es debido a la alineación, me pregunto si una estructura con 2 caracteres no está alineada en 4 bytes.

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 

void CompareAddress(char a, char b) 
{ 
    printf("Differs=%ld\n", (intptr_t)&b - (intptr_t)&a); 
} 

int main() 
{ 
    CompareAddress('a','b'); 
    return 0; 
} 

/* Differs= -4 */ 

Respuesta

5

La mejor manera de responder este tipo de preguntas (sobre el comportamiento de un compilador específico en una plataforma específica) es mirar al ensamblador. Puede obtener gcc para volcar su ensamblador pasando el indicador -S (y el indicador -fverbose-asm también es bueno). Correr

gcc -S -fverbose-asm file.c 

da una file.s que se ve un poco como (He quitado todos los bits irrelevantes, y los bits entre paréntesis son mis notas):

CompareAddress: 
     # ("allocate" memory on the stack for local variables) 
     subq $16, %rsp  
     # (put a and b onto the stack) 
     movl %edi, %edx  # a, tmp62 
     movl %esi, %eax  # b, tmp63 
     movb %dl, -4(%rbp) # tmp62, a 
     movb %al, -8(%rbp) # tmp63, b 
     # (get their addresses) 
     leaq -8(%rbp), %rdx #, b.0 
     leaq -4(%rbp), %rax #, a.1 
     subq %rax, %rdx  # a.1, D.4597 (&b - &a) 
     # (set up the parameters for the printf call) 
     movl $.LC0, %eax  #, D.4598 
     movq %rdx, %rsi  # D.4597, 
     movq %rax, %rdi  # D.4598, 
     movl $0, %eax  #, 
     call printf # 

main: 
     # (put 'a' and 'b' into the registers for the function call) 
     movl $98, %esi  #, 
     movl $97, %edi  #, 
     call CompareAddress 

(This question explica muy bien lo que [re]bp y [re]sp son).

La razón por la que la diferencia es negativa es que la pila crece hacia abajo: es decir,si inserta dos cosas en la pila, la que empuja primero tendrá una dirección más grande y se empujará a antes de b.

La razón es -4 en lugar de -1 es el compilador ha decidido que la alineación de los argumentos a límites de 4 bytes es "mejor", probablemente debido a 32 bits/64 ofertas de CPU bits con 4 bytes en el momento mejor de lo que se encarga sola bytes.

(También, mirando el ensamblador muestra el efecto que -mpreferred-stack-boundary tiene: En esencia, significa que la memoria en la pila atribuida en diferentes trozos del tamaño.)

9

Aquí es mi suposición:

En Linux en x64, el calling convention afirma que los primeros parámetros se pasan por el registro.

Por lo tanto, en su caso, tanto a como b se pasan por el registro en lugar de en la pila. Sin embargo, dado que toma su dirección, el compilador la almacenará en algún lugar de la pila después de se llama a la función.
(No es necesario en orden descendente)

También es posible que la función esté simplemente en línea.

En cualquier caso, el compilador crea espacio de pila temporal para almacenar las variables. Esos pueden estar en cualquier orden y sujetos a optimizaciones. Por lo tanto, es posible que no estén en el orden particular que podría esperar.

+1

En caso de que alguien está interesado, para salvar a tener que llevar a cabo el cita de wikipedia, el documento oficial AMD64 ABI se puede encontrar aquí: http://www.x86-64.org/documentation/abi.pdf –

0

Creo que la respuesta que el programa que se le da es correcta, el límite preferido de la pila preferida de GCC es 4, puede establecer -mpreferred-stack-boundary=num en las opciones de GCC para cambiar la pila boudary, luego el programa le dará la respuesta diferente según su conjunto.

+0

Intenté -mpreferred-stack-boundary para los valores 4 a 12. Pero el resultado fue el mismo –

Cuestiones relacionadas