2009-08-14 14 views
5

Mam następujący przykład skompilowany w VS2005, ostrzegając Poziom 4:Dlaczego wartość * = różni się od utraty danych podczas konwersji?

int main(int argc, char *argv[]) 
{ 
    short s = 2; 
    short t = 3; 

    t *= s; // warning C4244: '*=' : conversion from 'int' to 'short', possible loss of data 
    t = t * s; 
} 

Nie wydaje mi się, że powinno być przestrogą na każdej linii.

Czy * = utworzyć niejawną konwersję na int z jakiegoś powodu?

EDIT:

Wygląda na brak ostrzeżenia w drugiej linii (oraz w VS2008) są prawdziwe pytań.

Dzięki za odpowiedzi.

+0

Nie stanie się to w 2008 roku. –

+0

Używanie VC2008 zbudowanego za pomocą (/ W4) -> Nie otrzymuję ostrzeżenia! – AraK

+0

Nie otrzymałem ostrzeżenia w VS2005. Być może możesz opublikować swoje opcje kompilatora? –

Odpowiedz

12

Tak. Wszystkie operatory arytmetyczne w C++ są zdefiniowane na int i szersze. Po pomnożeniu dwóch wartości short s (nie ma znaczenia, jeśli korzystasz z * lub *=), są one najpierw konwertowane na int. Jest to objęte ISO C++ 5 [wyrażenie/9:

Wiele operatorów binarnych, którzy oczekują operandów arytmetycznych lub typów wyliczeniowych, powoduje konwersję i daje wyniki w podobny sposób. Celem jest uzyskanie wspólnego typu, który jest także typem wyniku. Ten wzorzec nazywa się zwykłymi konwersjami arytmetycznymi, które są zdefiniowane w następujący sposób:

  • Jeśli jeden z argumentów jest typu długiego podwójny, drugi zostanie przekształcony na długi podwójny.
  • W przeciwnym razie, jeśli jeden z argumentów jest podwójny, drugi zostanie przekonwertowany na podwójny.
  • W przeciwnym razie, jeśli dowolny operand jest zmiennoprzecinkowy, drugi będzie konwertowany na zmienny.
  • W przeciwnym razie, całkowe promocje (4.5) będą wykonywane na obu operandach.
  • Następnie, jeśli któryś z argumentów jest niepodpisany, drugi będzie konwertowany na niepodpisany.
  • W przeciwnym razie, jeśli jeden operand jest długim int, a drugi unsigned int, wtedy jeśli long int może reprezentować wszystkie wartości unsigned int, unsigned int zostanie zamienione na long int; w przeciwnym przypadku oba operandy zostaną przekonwertowane na unsigned long int.
  • W przeciwnym razie, jeśli jeden z operandów jest długi, drugi będzie konwertowany na długi.
  • W przeciwnym razie, jeśli któryś z argumentów jest niepodpisany, drugi zostanie skonwertowany na niepodpisany.

[Uwaga: W przeciwnym razie, jedyne przypadku jest to, że oba argumenty są Int]

i 4,5 [conv.prom]

1 rvalue typu char podpisany char, unsigned char, short int lub unsigned short int można przekonwertować na wartość r typu int, jeśli int może reprezentować wszystkie wartości typu źródła; w przeciwnym razie wartość rvalue źródła może zostać przekonwertowana na wartość typu unsigned int.

2 Wartość rdzenia typu wchar_t (3.9.1) lub typ wyliczenia (7.2) może zostać przekonwertowana na wartość rurnue pierwszego z następujących typów, które mogą reprezentować wszystkie wartości swojego typu: int, unsigned int , długie lub niepodpisane.

3 Wartość rwartość dla zintegrowanego pola bitowego (9.6) może zostać przekonwertowana na wartość rrr typu int, jeśli int może reprezentować wszystkie wartości pola bitowego; w przeciwnym razie można go przekonwertować na unsigned int, jeśli unsigned int może reprezentować wszystkie wartości pola bitowego. Jeśli pole bitowe jest jeszcze większe, nie ma do niego zastosowania integralna promocja. Jeśli pole bitowe ma typ wyliczeniowy, jest traktowane jak każda inna wartość tego typu w celach promocyjnych.

4 Wartość rdzenia typu bool może zostać przekonwertowana na wartość typu int, przy czym wartość false staje się równa zero, a wartość true staje się równa.

5 Konwersje te nazywane są promocjami integralnymi.

Powoduje to jednak, że ostrzeżenie pojawia się tylko w jednej linii, ale nie w obu.

3

Uważam, że odpowiedź brzmi: here.

żądanie:

Większość operatorów C wykonać Type konwersji przynieść operandy ekspresyjnym do wspólnego typu lub rozciągają krótkich wartości do liczby całkowitej wielkości stosowanej w operacji maszynowych.

4

Wynik krótkiego * skrótu może spowodować przepełnienie krótkiego, więc prawdopodobnie zapisuje wynik pośredni w int w drugim przykładzie. Jeśli cokolwiek, obaj powinni mieć ostrzeżenia.

Pierwsza z nich prawdopodobnie narzeka, ponieważ zapisuje wynik bezpośrednio w pierwotnej krótkiej wartości, podczas gdy druga tworzy pośrednie int, a następnie rzuca ją na krótki (wciąż jest to operacja stratna, ale wiele kompilatorów nie będzie skarżyć się).

0

To wydaje się być bardzo dziwnym ostrzeżeniem. Niektóre obserwacje:

short s = 12345; 
s *= 0xffff;  // no warning 
s *= (int)0xffff; // no warning 
s *= 0x10000;  // C4244 
s *= -0x8000;  // no warning 
s *= -0x8001;  // C4244 

short t = -12345; 
s *= t;   // no warning 
s *= (int)t;  // no warning 
s *= (unsigned)t; // no warning 

s *= (int)t * 0xffff; // no warning 
s *= (int)t * 0x10000; // C4244 

Nie wydaje się, aby dbać o konkretnych operatorów - wszystko +-*/ dają takie same wyniki. Wydaje się, że nie patrzy się na same typy ekspresji, ale także na odnośniki literackie, a gdy tylko przekroczysz magiczny próg, pojawi się ostrzeżenie.

Powiązane problemy