2012-06-20 9 views
15
#include <limits.h> 
#include <stdio.h> 
int main() { 
    long ival = 0; 
    printf("ival: %li, min: %i, max: %i, too big: %i, too small: %i\n", 
      ival, INT_MIN, INT_MAX, ival > INT_MAX, ival < INT_MIN); 
} 

Esto le da a la salida:extraña C comparación desigualdad resultado Entero

ival: 0, min: -2147483648, max: 2147483647, too big: 0, too small: 1 

¿Cómo es posible?

(que en realidad fue golpeado por este problema/error en CPython 2.7.3 en getargs.c:. convertsimple Si se mira el código, en case 'i', existe el cheque ival < INT_MIN que siempre era cierto para mí Ver también la test case source with further references. .)


Bueno, he probado algunos compiladores diferentes ahora. GCC/Clang, compilado para x86, todos devuelven lo esperado (demasiado pequeño: 0). La salida inesperada es de Clang en la cadena de herramientas de Xcode cuando se compila para armv7.


Si se quiere reproducir:

Este es el comando de compilación exacta: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk test-int.c

Ésta es Xcode 4.3.2.

Copié el resultante a.out en mi iPhone y lo ejecuté.

Si alguien está interesado en el código ensamblador generado por este:

.section __TEXT,__text,regular,pure_instructions 
    .section __TEXT,__textcoal_nt,coalesced,pure_instructions 
    .section __TEXT,__const_coal,coalesced 
    .section __TEXT,__picsymbolstub4,symbol_stubs,none,16 
    .section __TEXT,__StaticInit,regular,pure_instructions 
    .syntax unified 
    .section __TEXT,__text,regular,pure_instructions 
    .globl _main 
    .align 2 
    .code 16 
    .thumb_func _main 
_main: 
    push {r7, lr} 
    mov r7, sp 
    sub sp, #20 
    movw r0, #65535 
    movt r0, #32767 
    movs r1, #0 
    movt r1, #0 
    str r1, [sp, #16] 
    str r1, [sp, #12] 
    ldr r1, [sp, #12] 
    ldr r2, [sp, #12] 
    cmp r2, r0 
    movw r0, #0 
    it gt 
    movgt r0, #1 
    and r0, r0, #1 
    ldr r2, [sp, #12] 
    cmn.w r2, #-2147483648 
    movw r2, #0 
    it lt 
    movlt r2, #1 
    and r2, r2, #1 
    mov r3, sp 
    str r2, [r3, #4] 
    str r0, [r3] 
    mov.w r2, #-2147483648 
    mvn r3, #-2147483648 
    movw r0, :lower16:(L_.str-(LPC0_0+4)) 
    movt r0, :upper16:(L_.str-(LPC0_0+4)) 
LPC0_0: 
    add r0, pc 
    blx _printf 
    ldr r1, [sp, #16] 
    str r0, [sp, #8] 
    mov r0, r1 
    add sp, #20 
    pop {r7, pc} 

    .section __TEXT,__cstring,cstring_literals 
L_.str: 
    .asciz "ival: %li, min: %i, max: %i, too big: %i, too small: %i\n" 


.subsections_via_symbols 
+0

¿Podría ser una rareza de lanzamiento? Es posible que esté lanzando INT_MIN a largo y no manejando correctamente el signo? ¿O viceversa? O.o – TheZ

+0

@Albert interesante, obtengo 'ival: 0, min: -2147483648, max: 2147483647, demasiado grande: 0, demasiado pequeño: 0' –

+0

No creo que haya un formato% i para printf. Probablemente quieras% d. Y es una buena costumbre emitir explícitamente los argumentos para funciones varargs como printf to int. (en este caso no es necesario, porque el valor para (a> b) se establece por defecto en el tipo int) – wildplasser

Respuesta

5

Esto es un error. No hay espacio en la norma C para too small a ser algo distinto de 0. Así es como funciona:

  1. Desde INT_MIN es un int, que se convierte a long durante las "conversiones aritméticas habituales". Esto sucede porque long tiene una clasificación más alta que int (y ambos son tipos firmados). No se producen promociones, ya que todos los operandos tienen al menos int rango. No se invoca ningún comportamiento indefinido o especificado por la implementación.

  2. Durante la conversión, se conserva el valor de INT_MIN.Dado que se está convirtiendo de int a long, y se garantiza que long tiene al menos el rango de int, el valor de INT_MIN debe conservarse durante la conversión. No se invoca ningún comportamiento indefinido o especificado por la implementación. No se permiten conversiones modulares, solo para tipos sin firmar.

  3. El resultado de la comparación debe ser 0.

No hay margen de maniobra para la extensión de la señal u otras cosas similares. Además, dado que la llamada a printf es correcta, no hay ningún problema allí.

Si puede reproducirlo en otro sistema o enviarlo a otra persona que pueda reproducirlo, debe informar el error directamente a su proveedor de la cadena de herramientas.

Los intentos de reproducir el error: yo no era capaz de reproducir el comportamiento en cualquiera de las siguientes combinaciones, todas tanto con la optimización de encendido y apagado:

  • GCC 4.0, PPC + PPC64
  • GCC 4.2, PPC + PPC64
  • GCC 4.3, x64
  • GCC 4.4, x64
  • Clang 3,0, x64
+4

También solo puedo reproducirlo con el Clang desde dentro de la cadena de herramientas Xcode (4.3.2) para arch armv7 con -O0. Con optimizaciones, obtengo el resultado esperado. Parece un error real. Lo reporté – Albert

1

Lo que hace esta impresión?

#include <limits.h> 

printf("%016ld\n", LONG_MAX); 

long l_int_min = (long)INT_MIN; 
printf("%016lx\n", l_int_min); 

Me pregunto si INT_MIN está siendo forzado a long sin ser signo extendido. Eso haría que 0 sea más pequeño que el valor resultante.

EDIT: Bueno, el resultado de la primera printf() era 0000002147483647, lo que significa que long es de 32 bits en esa plataforma, al igual que int. Así que lanzar un int a un long no debería cambiar nada.

Intento reservar "es un error del compilador" como último recurso, pero esto me parece un error del compilador.

+0

Convertir 'int' a' long' sin conservar el signo es un error, de acuerdo con el estándar C. Ninguna implementación conforme a C puede hacer tal cosa. –

+0

'0000002147483647'' 0000000080000000' – Albert

+0

El formato% 16lx espera un argumento int largo sin signo. El l_int_min se pasa como un largo firmado. Su valor se inicializó usando INT_MIN, que se extendió de signo antes de su uso. – wildplasser

Cuestiones relacionadas