2010-06-23 24 views
17

Rozważmy następujący kod:Dlaczego jest -1> sizeof (int)?

template<bool> class StaticAssert; 
template<> class StaticAssert<true> {}; 
StaticAssert< (-1 < sizeof(int)) > xyz1; // Compile error 
StaticAssert< (-1 > sizeof(int)) > xyz2; // OK 

Dlaczego -1 > sizeof(int) prawda?

  1. Czy to prawda, że ​​-1 jest promowany do unsigned(-1) a następnie unsigned(-1) > sizeof(int).
  2. Czy to prawda, że ​​-1 > sizeof(int) jest równoważne -1 > size_t(4), jeśli sizeof (int) wynosi 4. Jeśli tak, to dlaczego -1 > size_t(4) jest fałszywe?

Czy to C++ standardowy comformant?

Odpowiedz

14

Poniższy sposób standardowy (ISO 14882) opisano przerwania "1 sizeof (int)

operatora porównania`>”definiuje 5,9 (expr.rel/2)

zwykłe konwersje arytmetyczne wykonywane na argumentach arytmetyki lub typu wyliczenia. ...

zwykłej arytmetycznych konwersji jest określona w 5 (wyrażenie/9)

... Wzór nazywa się zwykle arytmetyczne konwersji, które są zdefiniowane w następujący sposób:

  • Jeśli któryś z operandów jest typu double długo ...
  • w przeciwnym razie, jeśli którykolwiek z operandów jest Dobule ...
  • w przeciwnym razie, jeśli którykolwiek z operandów jest typu float, ...
  • W przeciwnym razie całkowe awanse będą wykonywane na obu operandach.
  • ...

Integralne promocji jest zdefiniowana w 4.5 (Conv.Bal/1)

rvalue typu znak, podpisany znak, unsigned char, krótkie int lub unsigned krótkim Int można przekształcić do rvalue typu int jeśli można Int oznaczają wszystkie wartości źródło typu ; w przeciwnym razie wartość rinue źródła może zostać przekonwertowana na wartość r type of unsigned int.

Wynikiem sizeof jest zdefiniowana w 5.3.3 (expr.sizeof/6)

Wynikiem tego jest stałe typu size_t

size_t jest zdefiniowany w standardzie C (ISO 9899), który jest liczbą całkowitą typu bez znaku.

Tak więc dla -1 > sizeof(int),> uruchamia zwykle konwersję arytmetyczną. Zwykła konwersja arytmetyczna konwertuje -1 na unsigned int, ponieważ int nie może reprezentować całej wartości size_t. -1 staje się bardzo dużą liczbą w zależności od platformy. Tak więc -1 > sizeof(int) jest true.

+2

To może być tylko literówka, ale 'size_t' to _an_ unsigned typ całkowity i nie musi to być przypadek, w którym' int' nie może reprezentować wszystkich wartości 'size_t' (' size_t' może być 'unsigned short '), chociaż oczywiście nie może na platformie pytającego pytającego. –

+2

'(unsigned T) -1' nie jest tylko dużą wartością, jest * * największą wartością, jaką' unsigned T' może pomieścić. – GManNickG

+1

Dobrze wiem, na co pozwala standard. :) -1 jest zawsze największa, przeczytaj zasady konwersji. Lub ten http://stackoverflow.com/questions/809227/is-it-safe-to-use-1-to-set-all-bits-to-true/809341#809341 – GManNickG

14

Ponieważ unsigned jest silniejsza następnie podpisany i -1 konwertowane na wartości unsigned dniem size_t, więc faktycznie -1 == 0xFFFFFFFF > 4

ten sposób powinien działać zgodnie z C++ standardowy

+0

nie kompilatory wydają ostrzeżenia dla takich przypadków? – kriss

+0

to nie wyjaśnia, dlaczego -1 <(size_t) 4, sizeof powinien używać zwracanego typu size_t .. –

+0

@kriss - Różne kompilatory wydają różne ostrzeżenia. Również ostrzeżenia mogą być stłumione przez opcje wiersza poleceń kompilatora i/lub przez pragmy w kodzie źródłowym; i/lub mogą być zignorowane przez programistę. – ChrisW

4

ponieważ -1 dostaje lanego do size_t i jest to niepodpisany typ danych - czyli (size_t)-1 == 4294967295 (w systemie 32-bitowym), który jest zdecydowanie większy niż 4

jeśli dodasz -Wall do ustawień gcc, na przykład pojawi się ostrzeżenie, że jesteś Porównując podpisane i nieoznaczoną typ danych

+0

Czy można bezpiecznie założyć, że sizeof (size_t)> = sizeof (int) - IOW: czy jest standaryzowany? – Wolf

2

To proste i smutne. W C/C++:

  1. większość czasu, niepodpisane typy całkowite mieć semantyczny liczb całkowitych modułowych (stanowią one klas równoważności)
  2. porównania niepodpisane typów całkowitych mają semantyczna od zwykłej całkowitej zamawiającego, dzięki czemu 1U < 2U (IOW 0U jest najmniejsza wartość unsigned)
  3. sizeof ma typ size_t
  4. size_t jest unsigned całkowitą
  5. punktu (1) oznacza, że ​​m ixed obliczenia arytmetyczne obejmujące liczbę całkowitą podpisaną i niepodpisaną są wykonywane w niepodpisanej, arytmetyce modułowej: jest to jedyna możliwość bez naruszenia reguły "unsigned mean modular". Przejście na liczbę całkowitą do równoważnej liczby liczb całkowitych jest równoważne. (O ile będzie w inny sposób wymaga Wybór liczby całkowitej do reprezentowania klasy równoważności.)
  6. Punkt (5) wynika, że ​​-1 < 1U jest interpretowany jako unsigned(-1) < 1U i unsigned(-1) = - 1U i oczywiście - 1U < 1U, więc -1 < 1U jest prawdą.
  7. Punkty (1,3,4) sugerują, że sizeof something działa (głównie) jako równoważna klasa (!!!).
  8. Wszystko to oznacza, że ​​-1 < sizeof something

Wniosek: jest to błąd konstrukcja odziedziczone C

Reguła:

używać tylko niepodpisanych typów dla modułowego arytmetyki, bity manipulacje (&, |, ^, <<, >>, ~ operatorów), manipulacje bajt (unsigned char oznacza "bajt" w C/C++) , a znaki (unsigned char oznaczają znak w C/C++).

Nie należy używać typów bez znaku do wykonywania arytmetyki.

Jeśli funkcja oczekuje wartości całkowitej, która nigdy nie powinna być ujemna, weź liczbę całkowitą ze znakiem i opcjonalnie sprawdź, czy wartość znajduje się w zakresie.

+0

Znalazłem punkt (6) nieco mylące, może '==' zawarte w * 'unsigned (-1)' = '- 1U' * byłoby lepiej – Wolf