2015-06-14 16 views
13

W C99 istnieją pewne (opcjonalne) typy, takie jak int8_t, int16_t i tym podobne, z gwarancją, że mają dokładnie określoną szerokość i brak bitów dopełniających i reprezentują liczby w dopełnieniu dwójki (7.18.1.1). W 6.2.6.2 sygnowane przekroczenie liczby całkowitej jest wymienione w przypisach 44) i 45), a mianowicie może skutkować wartościami pułapkowania w bitach dopełniających.Czy przepełnienie intN_t jest dobrze zdefiniowane?

Jako że intN_t nie ma żadnych bitów dopełniających, a na pewno są one uzupełnieniem dwójki, czy oznacza to, że ich przepełnienie nie generuje niezdefiniowanych zachowań? Jaki byłby rezultat np. przepełnione mnożenie? A co z dodatkiem? Czy wynik jest mniejszy modulo 2^N jak dla typów bez znaku?

+4

Nie, nadal UB ... –

+1

IANALL, ale mimo że są one reprezentowane za pomocą uzupełnienia 2, nadal nie przestrzegają praw arytmetycznego modulo 2^n, więc wynik przepełnienia nie jest zdefiniowany matematycznie. Standard mówi: "Jeśli podczas oceny wyrażenia wynik nie jest zdefiniowany matematycznie lub nie mieści się w zakresie reprezentowalnych wartości dla jego typu, zachowanie jest niezdefiniowane.". – tux3

+3

Nawet dla 'uintN_t', przepełnienie nie jest (ogólnie) dobrze zdefiniowane. Zarówno 'intN_t' jak i' uintN_t' nadal podlegają promocjom całkowitym i możliwe jest pomnożenie dwóch wartości 'uint16_t', w systemach, w których' uint16_t' promuje do 'int', aby wytworzyć wynik, który nie może być reprezentowany w' int '. – hvd

Odpowiedz

7

Przypisy nie są normatywne. Jeśli przypis wskazuje, że przepełnienie może spowodować przechwytywanie wartości w bitach dopełniających, nie jest tak źle, ale widzę, jak to jest nieco mylące. Tekst normatywny mówi jedynie, że zachowanie jest niezdefiniowane. Umieszczanie wartości pułapkowania w bitach dopełniających jest jedną z możliwych konsekwencji niezdefiniowanego zachowania, ale nie jedyną.

Nie, to nie oznacza, że ​​zdefiniowano przepełnienie. Możliwe jest przepełnienie operacji z operandami intN_t/uintN_t, a to przepełnienie powoduje niezdefiniowane zachowanie.

Coś takiego jak int8_t i = 127; ++i; nie ma UB. int8_t podlega stałym promocjom, więc dodawanie odbywa się tak, jak gdybyś napisał i = (int8_t) ((int) i + 1);. Sam dodatek nie przepełnia się, a konwersja z powrotem do int8_t generuje wynik określony przez implementację.

Coś takiego jak uint16_t u = 65535; u *= u; ma UB na bieżących typowych implementacjach, gdzie int ma 32 bity znak/wartość. uint16_t też podlega ciągłym promocjom, więc mnożenie odbywa się tak, jak gdybyś napisał u = (uint16_t) ((int) u * (int) u);. Mnożenie się przepełnia, a zachowanie jest niezdefiniowane.

Coś takiego jak int64_t i = 9223372036854775807; ++i; ma UB na prawie wszystkich implementacjach. Sam dodatek się przepełnia.

+0

Czy 'u' nie zostanie awansowany do' unsigned int' zamiast 'int'? –

+1

@OliverCharlesworth Tylko jeśli 'int' nie może reprezentować wszystkich wartości typu. Na przykład, jeśli 'uint16_t' jest' unsigned short', a 'unsigned short' i' unsigned int' mają ten sam rozmiar i zasięg. Ale jeśli 'int' jest wystarczająco duże, to zostanie użyte. Kiedyś dawno temu było inaczej, ale o ile wiem, że było to przed pierwszym standardem C, pierwszy standard C wymagał, aby promocje działały tak, jak tu opisuję i od tego czasu nigdy się nie zmieniły. – hvd

+0

Przypuszczam, że z 'uint16_t u = 65535;', 'u * = u' ->' u * = u * 1U; 'wyeliminowałoby UB. I _think_ sztuczka '* 1U' pomaga również w rozszerzeniu' uintN_t'. – chux

1

Nie, nie jest dobrze zdefiniowany, ponieważ ... w ogóle nie jest zdefiniowany. Po prostu nie ma tekstu w standardzie, który dałby semantyczną wartość nadpisanej liczby całkowitej.

Nie należy przeceniać terminu "niezdefiniowane zachowanie" jako czegoś tajemniczego, ale przyjąć jego bezpośredni sens. Nie ma definicji zachowania, więc standard nie określa niczego, co powinno się zdarzyć, a żaden przenośny kod nie powinien polegać na takiej funkcji.

+0

Standard określa, że ​​typy "intXX_t" powinny być przechowywane przy użyciu reprezentacji uzupełniania dwójkowego i bez dopełnień, a także określa, że ​​konwersja 'int' na mniejszy' int' typu, który nie jest w stanie utrzymać swojej wartości, powinna zachowywać się w Sposób implementacji - Zdefiniowany. Przypuszczam, że implementacja może wykorzystywać uzupełnienie dwójkowe, ale zdefiniować coś innego niż redukcja dwóch elementów uzupełniających dla konwersji, ale byłoby to bardzo nietypowe. Wynikiem tego nie jest UB, jednak w każdym przypadku, gdy dobrze zdefiniowane 'int' jest konwertowane na mniejszy rozmiar. – supercat

+0

@supercat, pytanie dotyczy przepełnienia, a nie konwersji. –

+0

Podano 'int16_t x = 182;' w systemie gdzie INT_MAX ma 65535 lub więcej, fakt, że 'x * = x;' spowodowałby, że x równy -32412 nie byłby "przepełnieniem", ponieważ termin jest zdefiniowany w Standardzie, ale jeśli 'x' miał zachowywać się jak liczba, naturalnie wydawałoby się, że przepełnił się. – supercat