2015-02-17 18 views
9

Jestem obecnie naukę w klasie CS107 co czyni następujące założenia:Pointer arytmetyka wokół cast

  • sizeof(int) == 4
  • sizeof(short) == 2
  • sizeof(char) == 1
  • duży endianness

Mój profesor pokazał następujący kod:

int arr[5]; 
((short*)(((char*) (&arr[1])) + 8))[3] = 100; 

Oto 20 bajtów reprezentujących arr:

|....|....|....|....|....| 

Mój profesor stwierdza, że ​​&arr[1] punkty tutaj, co się zgadzam.

|....|....|....|....|....| 
    x 

teraz zrozumieć, że (char*) sprawia wskaźnik szerokości Char (jeden bajt), zamiast szerokość int (4 bajty).

Co nie rozumiem jest + 8, co mówi mój profesor punkty tutaj:

|....|....|....|....|....| 
         x 

Ale nie powinno to podkreślić tutaj, ponieważ to jedzie do przodu 8 razy większy od char (1 bajt)?

|....|....|....|....|....| 
       x 
+6

Masz rację.Napisz kod, aby wydrukować wartości wskaźników, aby pokazać profesorowi, że masz rację. –

+3

Wszystko zależy od 'sizeof (int)', które niekoniecznie musi być '4'. –

+2

@BlagovestBuyukliev Mój profesor stwierdził, że przyjmuje się, że 'sizeof (int)' to 4 na czas trwania klasy. Przepraszam, powinienem był o tym wspomnieć. – Alexey

Odpowiedz

4

Zróbmy to krok po kroku. Twój wyraz można rozłożyć tak:

((short*)(((char*) (&arr[1])) + 8))[3] 
----------------------------------------------------- 
char *base = (char *) &arr[1]; 
char *base_plus_offset = base + 8; 
short *cast_into_short = (short *) base_plus_offset; 
cast_into_short[3] = 100; 

base_plus_offset punktów w lokalizacji bajtów 12 w tablicy. cast_into_short[3] odnosi się do wartości short w lokalizacji 12 + sizeof(short) * 3, która w Twoim przypadku to 18.

+3

I dlatego kod taki jak ten nie powinien być zapisany w jednej linii. Tutaj jest jasne, co się dzieje. W kodzie w pytaniu zakładam, że profesor myli się ze zbyt wieloma nawiasami. – gnasher729

+1

@ gnasher729: A przynajmniej unikaj całkowicie bezużytecznych parensów, takich jak '(char *) (& arr [1])' (co wydaje się być tutaj dla osób nie wiedzących, że obsada może przyjąć jednoznaczne wyrażenie, co, gdyby nie było dozwolone, nie skompilowałoby się bez parenów, wątpliwość, że to "wyjaśnia" nawet nie może być wyrażone w kategoriach pierwszeństwa). Chociaż zgadzam się, że prawdopodobnie należy podzielić w tym przypadku. – mafso

+1

@mafso: Druga para nawiasów jest również bezużyteczna, więc nie-ładunkową wersją tego wyrażenia byłoby: '((short *) ((char *) & arr [1] + 8)) [3]' . –

1

Oto niektóre kodu, który może pokazać, który bajt zostanie zmodyfikowany w systemie, wraz z podziałem na to, co się dzieje:

#include <stdio.h> 

int main(int argc, char* argv[]) 
{ 
    int arr[5]; 
    int i; 

    for(i = 0; i < 5; i++) 
     arr[i] = 0; 

    printf("Before: "); 

    for(i = 0; i < sizeof(int)*5; i++) 
     printf("%2.2X ", ((char*)arr)[i]); 

    printf("\n"); 

    ((short*)(((char*) (&arr[1])) + 8))[3] = 100; 

    printf("After: "); 

    for(i = 0; i < sizeof(int)*5; i++) 
     printf("%2.2X ", ((char*)arr)[i]); 
    printf("\n"); 

    return 0; 
} 

zacząć od wewnętrznej najbardziej:

int wskaźnik do (arr + 4)

&arr[1] 
|...|...|...|...|... 
    Xxxx 

znak wskaźnik (aRR +4)

(char*)(&arr[1]) 
|...|...|...|...|... 
    X 

znak wskaźnik (ARR + 4 + 8)

((char*)(&arr[1])) + 8) 
|...|...|...|...|... 
      X 

krótkim wskaźnik do (ARR + 4 + 8)

(short*)((char*)(&arr[1])) + 8) 
|...|...|...|...|... 
      Xx 

krótki w (ARR + 4 + 8 + (3 x 2)) (jest to indeks tablicy)

((short*)((char*)(&arr[1])) + 8))[3] 
|...|...|...|...|... 
        Xx 

Dokładnie jaki bajt zostanie zmodyfikowany w tym miejscu, zależy od nieodłączalności systemu. Na moim małym endianie x86 otrzymuję następujące dane wyjściowe:

Before: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
After: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 00 

Powodzenia z kursu.

+1

Twoja analiza wyrażenia jest prawidłowa, ale wydaje się, że błędnie zrozumiałeś twierdzenie PO, kiedy mówisz, że nie ma racji. Jego (jedna, jak dotąd) edycja nie robi różnicy, ponieważ dodany rzut do 'short *' nie jest częścią podekspresji, o którą pyta. –

+0

@JohnBollinger Dzięki, poprawiłem to stwierdzenie! Dodaj także pozycję wskaźnika i użycie pamięci. –

1

Wyrażenie ustawi dwa bajty 18 bajtów po rozpoczęciu arr do wartości 100.

#include <stdio.h> 

int main() { 

    int arr[5]; 

    char* start=(char*)&arr; 
    char* end=(char*)&((short*)(((char*) (&arr[1])) + 8))[3]; 

    printf("sizeof(int)=%zu\n",sizeof(int)); 
    printf("sizeof(short)=%zu\n",sizeof(short)); 
    printf("offset=%td <- THIS IS THE ANSWER\n",(end-start)); 
    printf("100=%04x (hex)\n",100); 

    for(size_t i=0;i<5;++i){ 

     printf("arr[%zu]=%d (%08x hex)\n",i,arr[i],arr[i]); 

    } 

} 

Możliwa wyjściowa:

sizeof(int)=4 
sizeof(short)=2 
offset=18 <- THIS IS THE ANSWER 
100=0064 (hex) 
arr[0]=0 (00000000 hex) 
arr[1]=0 (00000000 hex) 
arr[2]=0 (00000000 hex) 
arr[3]=0 (00000000 hex) 
arr[4]=6553600 (00640000 hex) 

We wszystkich profesorów shenanigans on przeniósł cię 1 całkowitą , 8 znaków/bajtów i 3 szorty, które 4 + 8 + 6 = 18 bajtów. Bingo.

Zauważ, że to wyjście ujawnia komputer, na którym to uruchomiłem, aby mieć 4-bajtowe liczby całkowite, 2 bajty krótkie (często) i być małe-endianowe, ponieważ ostatnie dwa bajty tablicy zostały ustawione odpowiednio na 0x64 i 0x00.

Uważam, że diagramy są strasznie zagmatwane, ponieważ nie jest jasne, czy masz na myśli "|" adresować, czy nie.

|....|....|....|....| 

    ^ 1^ ^2 
A X  C  S B 

Dołącz barów ('|') A jest początkiem Arr i B jest 'jeden za końcem' (pojęciem prawnym w C).

X jest adresem, o którym mowa w wyrażeniu & Arr [1]. C za pomocą wyrażenia (((znak *) (& arr [1])) + 8). S przez całe wyrażenie. S i bajt po są przypisane, a to zależy od stopnia zaawansowania platformy.

Zostawiam to jako ćwiczenie, aby określić, jaki wynik na podobnej, ale wielkiej endianowej platformie, która generuje. Ktoś? Zauważyłem z komentarzy, że jesteś big-endianinem i jestem little-endian (przestań się śmiać). Należy zmienić tylko jedną linię danych wyjściowych.