2014-11-15 11 views
6

czytałem artykuł na temat wykorzystania size_t i ptrdiff_t typów danych here, gdy natknąłem się na ten przykład:Różnica w wynikach przy użyciu int i size_t

enter image description here

Kod:

int A = -2; 
unsigned B = 1; 
int array[5] = { 1, 2, 3, 4, 5 }; 
int *ptr = array + 3; 
ptr = ptr + (A + B); //Error 
printf("%i\n", *ptr); 

Nie jestem w stanie zrozumieć kilku rzeczy. Po pierwsze, w jaki sposób dodanie liczby signed i unsigned powoduje odrzucenie wyniku wprowadzenia do typu unsigned? Jeśli wynik rzeczywiście wynosi 0xFFFFFFFF typu unsigned, dlaczego w systemie 32-bitowym, dodając go z ptr, będzie interpretowany jako ptr-1, biorąc pod uwagę, że numer jest rzeczywiście typu unsigned, a początkowe 1 nie powinno oznaczać znaku?

Po drugie, dlaczego wynik jest inny w systemie 64-bitowym?

Czy ktoś może to wyjaśnić?

+3

kod pocztowy, a nie obraz z kodem. – chux

+0

Zobacz http://stackoverflow.com/questions/24864103/replacing-arrays-access-variables- with-right-integer-type – technosaurus

+0

Cieszę się, że ktoś zainteresowany moim artykułem. Na pytania już udzielono odpowiedzi. Trochę więcej szczegółów na temat arytmetyki adresu: http://www.viva64.com/en/l/0013/ I ogólnie wiele interesujących rzeczy na temat błędów 64-bitowych: http://www.viva64.com/ pl/l/full/ –

Odpowiedz

4

1. Nie jestem w stanie zrozumieć kilku rzeczy. Po pierwsze, w jaki sposób dodanie liczby podpisanej i niepodpisanej rzutuje na wynik niepodpisany?

Jest to zdefiniowane przez liczby całkowite i stopień konwersji liczby całkowitej.

6.3.1.8 P1: W przeciwnym razie, jeżeli argument operacji, który ma unsigned liczby całkowitej oceniającym większa lub równa rangi typu drugiego argumentu operacji, a następnie operand z podpisanego typu całkowitego przeprowadza się w typ argumentu z unsigned typu integer.

W tym przypadku niepodpisany ma wyższą rangę niż int, dlatego int jest promowane do unsigned.

konwersja int (-2) na unsigned przeprowadza się zgodnie z:

6.3.1.3 P2 przeciwnym razie, jeżeli nowy typ jest podpisany wartość jest przekształcany przez wielokrotne dodanie lub odjęcie jednej więcej niż maksymalne wartości, które mogą być reprezentowane w nowym typie aż wartość mieści się w przedziale od nowego typu

2. Jeżeli wynik jest rzeczywiście 0xFFFFFFFF typu unsigned, dlaczego w systemie 32-bitowym , podczas dodawania go za pomocą ptr, będzie interpretowane ed jako ptr-1, biorąc pod uwagę, że liczba jest rzeczywiście typu bez znaku, a znak wiodący 1 nie powinien oznaczać znaku?

Jest to niezdefiniowane zachowanie i nie należy na nim polegać, ponieważ C nie definiuje przekroczenia arytmetycznego wskaźnika.

6.5.6 P8: Jeśli zarówno wskaźnik operandów a punkt wynik do elementów tego samego obiektu tablicy lub jeden za ostatnim elementu obiektu tablicy, ocena nie sporządza przelewu; w przeciwnym razie zachowanie jest niezdefiniowane.

3. Po drugie, dlaczego wynik jest inny w systemie 64-bitowym?

(Zakłada (podobnie jak rysunku), które są unsigned int 4 bajty).

Wynik A i B jest taki sam jak opisano w 1., to wynik jest dodawany do wskaźnik. Ponieważ wskaźnik ma 8 bajtów i zakładając, że dodawanie się nie przepełnia (może nadal, jeśli ptr ma duży adres, dający takie samo niezdefiniowane zachowanie jak w 2.) wynikiem jest adres.

Jest to niezdefiniowane zachowanie, ponieważ wskaźnik wskazuje drogę poza granicami tablicy.

1

Na większości 64-bitów, int są 32-bitowe, ale w systemach 32-bitowych wskaźniki są również 32-bitowe.

Przypomnijmy, że w 32-bitową arytmetyki - na two's complement sprzętu opartego dodając 0xFFFFFFFF jest prawie taka sama jak odjęcie 1: przelewa i masz ten sam numer minus 1 (to samo zjawisko podczas dodawania 9 do liczby pomiędzy 0 a 9, otrzymasz niż liczba minus 1 i przeniesienie). W przypadku tego typu sprzętu kodowanie -1 jest w rzeczywistości tą samą wartością, co 0xFFFFFFFF, tylko operacja jest inna (podpisane dodawanie w porównaniu z niepodpisanym dodatkiem), a zatem przeniesienie zostanie wykonane w niepodpisanym przypadku.

Na wskaźnikach 64-bitowych znajduje się ... 64bit. Dodanie 32-bitowej wartości do 64-bitowej wymaga rozszerzenia tej 32-bitowej wartości do 64. unsigned wartości są zerowe rozszerzone (tj. Brakujące bity są po prostu wypełnione zerami), podczas gdy podpisane wartości są rozszerzone znakiem (tzn. Brakujące bity są wypełniane wartość bitowa znaku).

W takim przypadku dodanie wartości niepodpisanej (która nie będzie przedłużana znakiem) nie spowoduje przepełnienia, co spowoduje bardzo różną wartość od oryginału.

+0

Nigdy nie jestem dobry z tymi manipulacjami. :(Aby było jasne, przy dodawaniu wynikiem jest oryginalna liczba -1, zaniedbane przenoszenie, a tym samym liczba staje się oryginalną liczbą -1, czy to prawda? Dlaczego występuje tylko w systemach 32-bitowych? – SexyBeast

+1

u64 + u32 może nadal przepełniać. – 2501

+0

@ 2501 Teraz powinno być wyraźniej. – didierc

2

Argumenty ekspresyjnych A + B podlegają zwykle arytmetyczna konwersji pokryte C11 (n1570) 6.3.1.8 P1:

[...]

Inaczej, [który Gorące całkowitymi pozostaw int i unsigned int niezmieniony] są wykonywane na obu operandach. Wówczas stosuje się następujące zasady do promowanych Argumenty:

  • Jeśli oba argumenty mają ten sam typ, [...]
  • W przeciwnym razie, jeśli oba operandy podpisali typy całkowite lub oba mają niepodpisanych typów całkowitych [ ...]
  • W przeciwnym razie, jeśli operand, który ma typ liczby bez znaku ma stopień większy lub równy rangi typu drugiego argumentu, to operand ze znakiem typu integer jest konwertowany na typ argumentu bez znaku typ całkowity.
  • [...]

Rodzaje int i unsigned int mają taką samą rangę (ibid. 6.3.1.1 p1, 4 th punktor); wynik dodania ma typ unsigned int.

W systemach 32-bitowych int i wskaźniki zazwyczaj mają ten sam rozmiar (32-bitowy). Z punktu widzenia sprzętowego (i przy założeniu uzupełnienia 2) odjęcie 1 i dodanie -1u jest takie samo (dodanie dla typów podpisanych i niepodpisanych jest takie samo!), Więc wydaje się, że dostęp do elementu tablicy działa. To jest niezdefiniowane zachowanie, ponieważ array nie zawiera elementu 0x100000003 rd.

Na 64-bitowym, int zwykle ma nadal 32 bity, ale wskaźniki mają 64 bity. Tak więc nie ma żadnego zawijania i żadnej równoważności odejmowania 1 (z punktu widzenia sprzętowo-centrycznego, zachowanie jest niezdefiniowane w obu przypadkach).

Aby zilustrować, powiedzmy ptr jest 0xabcd0123, dodając plony 0xffffffff

abcd
+ ffffffff 

1abcd0122 
^-- The 1 is truncated for a 32-bit calculation, but not for 64-bit. 
Powiązane problemy