Pregunta

#include <stdio.h>

int main(void)
{
        double resd = 0.000116;
        long long resi = 0;

        printf("%lld %f %lld %f\n", resd, resd, resi, resi);
        return 0;
}

da (Linux, gcc, x64)

0 0.000116 0 0.000116
             ^^^^^^^^ odd, since the memory for resi is zeroed

En realidad, compilado con g ++ da resultados aleatorios en lugar de la segunda 0.

Yo entiendo que di especificadores no válidos a printf y que desencadena no especificado comportamiento indefinido, pero me pregunto por qué se produce esta corrupción específica, ya long long y double tienen el mismo tamaño.

¿Fue útil?

Solución

Me conseguir los mismos resultados que lo hace en mi máquina (Mac OS X, por lo que Linux ABI AMD /). Los parámetros de punto flotante se pasan en registros XMM y los parámetros enteros en registros enteros. Cuando printf los agarra usando va_arg, se tira del XMM cuando se ve el formato %f, y de los otros registros cuando ve %lld. Aquí está el desmontaje para su programa compilado (-O0) en mi máquina:

 1 _main:
 2   pushq   %rbp
 3   movq    %rsp,%rbp
 4   subq    $0x20,%rsp
 5   movq    $0x3f1e68a0d349be90,%rax
 6   move    %rax,0xf8(%rbp)
 7   movq    $0x00000000,0xf0(%rbp)
 8   movq    0xf0(%rbp),%rdx
 9   movq    0xf0(%rbp),%rsi
10   movsd   0xf8(%rbp),%xmm0
11   movq    0xf8(%rbp),%rax
12   movapd  %xmm0,%xmm1
13   movq    %rax,0xe8(%rbp)
14   movsd   0xe8(%rbp),%xmm0
15   lea     0x0000001d(%rip),%rdi
16   movl    $0x00000002,%eax
17   callq   0x100000f22    ; symbol stub for: _printf
18   movl    $0x00000000,%eax
19   leave
20   ret

No se puede ver lo que está pasando - la cadena de formato se pasa en %rdi, a continuación, se pasan los parámetros (en orden) en: %xmm0, %xmm1, %rsi y %rdx. cuando printf los consigue, se les aparece fuera en un orden diferente (el orden especificado en la cadena de formato). Eso significa que ellos aparece: %rsi, %xmm0, %rdx, %xmm1, dando los resultados que se ven. El 2 en %eax es indicar el número de argumentos de coma flotante pasado.

Editar:

Esta es una versión optimizada - en este caso el código más corto podría ser más fácil de entender. La explicación es la misma que la anterior, pero con un poco de ruido repetitivo menos. El valor de punto flotante se carga por el movsd en la línea 4.

 1 _main:
 2    pushq   %rbp
 3    movq    %rsp,%rbp
 4    movsd   0x00000038(%rip),%xmm0
 5    xorl    %edx,%edx
 6    xorl    %esi,%esi
 7    movaps  %xmm0,%xmm1
 8    leaq    0x00000018(%rip),%rdi
 9    movb    $0x02,%al
10    callq   0x100000f18   ; symbol stub for: _printf
11    xorl    %eax,%eax
12    leave
13    ret

Otros consejos

Es porque bajo la x86_64 C convenciones de llamada en su plataforma, los dos primeros argumentos de coma flotante se pasan en xmm0 y xmm1, y los primeros dos argumentos enteros se pasan en GPRS (rsi y rdx si estás en Linux o OS X), independientemente del orden en el que aparecen.

Usted está confundido porque usted está esperando que los parámetros se pasan en la memoria; no lo son.

  • El primer número debe ser un alto valor ya que usted está pasando por un doble como un entero. Debe ser% f.
  • ¿Qué pasa con el período después de la "0" en "Resi"? Que lo convertirá en un doble, por lo que está tratando de cargar un doble dentro de un número entero. Eso te dará una advertencia del compilador.
  • Algunas implementaciones podría estar basada en registros, por lo que ya que estás arruinando los tipos de argumentos, se confunde.

¿En qué plataforma estás compilando en? Ventanas?

No te ves en el desmontaje para ver lo que realmente empuja en la pila? Es más, ¿los empuja en la pila, o de los registros de uso?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top