2012-06-20 15 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); 
} 

Daje wyjście:dziwne C porównanie całkowitą nierówność spowodować

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

Jak to możliwe?

(I rzeczywiście dostał ten problem/bug w CPython 2.7.3 w getargs.c. convertsimple Jeśli przyjrzeć się kod, w case 'i', jest sprawdzenie ival < INT_MIN który zawsze był dla mnie prawdziwy Zobacz także test case source with further references. .)


Cóż, testowałem teraz kilka różnych kompilatorów. GCC/Clang, skompilowane dla x86 wszystkie zwracają oczekiwane (zbyt małe: 0). Nieoczekiwane dane wyjściowe pochodzą z Clang w łańcuchu Xcode toolchain po kompilacji dla armv7.


Jeśli chcesz odtworzyć:

Jest to dokładna komenda kompilacji: /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

To Xcode 4.3.2.

Skopiowałem wynikowy kod a.out do mojego iPhone'a i wykonałem go.

Jeśli ktoś jest zainteresowany w kodzie asemblera generowanego przez to:

.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

Może być dziwactwem odlewania? Możliwe, że rzuca INT_MIN na długo i nie obsługuje poprawnie znaku? Lub odwrotnie? O.o – TheZ

+0

@Albert interesujący, otrzymuję 'ival: 0, min: -2147483648, max: 2147483647, zbyt duży: 0, zbyt mały: 0' –

+0

Nie sądzę, że istnieje format% i dla printf. Prawdopodobnie chcesz% d. I to jest dobry nawyk, aby wyraźnie rzucić argumenty na funkcje varargs takie jak printf do int. (w tym przypadku nie jest to konieczne, ponieważ wartość dla (a> b) jest domyślna dla typu int) – wildplasser

Odpowiedz

5

To jest błąd. Nie ma miejsca w standardzie C dla too small być coś innego niż 0. Oto jak to działa:

  1. Od INT_MIN jest int, zostaje przekształcony do long podczas „zwykłych arytmetycznych konwersji”. Dzieje się tak, ponieważ long ma wyższą pozycję niż int (oba typy są podpisane). Brak promocji, ponieważ wszystkie operandy mają co najmniej pozycję int. Nie wywołuje się nieokreślonego lub określonego przez implementację zachowania.

  2. Podczas konwersji zostanie zachowana wartość INT_MIN.Ponieważ jest przekształcany z int na long i jest zagwarantowane, że long ma co najmniej zakres int, wartość INT_MIN musi być zachowana podczas konwersji. Nie wywołuje się nieokreślonego lub określonego przez implementację zachowania. Żadne konwersje modułowe są niedozwolone, dotyczy to tylko typów bez znaku.

  3. Wynik porównania powinien być 0.

Nie ma miejsca na przesuwanie podpisów ani innych podobnych rzeczy. Ponadto, ponieważ wywołanie printf jest poprawne, nie ma problemu.

Jeśli można go odtworzyć w innym systemie lub wysłać komuś innemu, kto może je odtworzyć, należy zgłosić błąd bezpośrednio sprzedawcy narzędzi.

Próby odtworzenia błędu: I nie był w stanie odtworzyć zachowanie na którykolwiek z następujących kombinacji, wszystko zarówno z optymalizacji i wyłączanie:

  • GCC 4.0, PPC + PPC64
  • GCC 4,2 PPC + PPC64
  • GCC 4.3 64
  • GCC 4.4 64
  • Clang 3,0, 64
+4

Mogę także odtworzyć go tylko za pomocą Clanga z łańcucha narzędzi Xcode (4.3.2) dla ARV armv7 z -O0. Dzięki optymalizacji uzyskuję oczekiwany rezultat. Wygląda jak prawdziwy błąd. Zgłosiłem to. – Albert

1

Co to wydrukować?

#include <limits.h> 

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

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

zastanawiam czy INT_MIN jest uzyskiwanie zmuszany do long bez logowania przedłużony. To spowodowałoby 0 mniejsze niż wynikowa wartość.

EDIT: Dobra, wynik pierwszego printf() był 0000002147483647, co oznacza, że ​​long jest 32-bitowy na tej platformie, podobnie jak int. Więc rzutowanie int na long nie powinno niczego zmieniać.

Próbuję zarezerwować "to błąd kompilatora" w ostateczności, ale jest to dla mnie błąd kompilatora.

+0

Konwersja 'int' na' long' bez zachowania znaku jest błędem, zgodnie ze standardem C. Żadna zgodna implementacja C nie może zrobić czegoś takiego. –

+0

'0000002147483647'' 0000000080000000' – Albert

+0

Format% 16lx oczekuje niepodpisanego długiego argumentu int. L_int_min jest przekazywana jako podpisana długo. Jego wartość została zainicjowana za pomocą INT_MIN, która została przedłużona przed użyciem. – wildplasser