2015-02-17 6 views
9

ich derzeit in einer CS107 Klasse eingeschrieben bin, die folgenden Annahmen macht:Pointer-Arithmetik umgossen

  • sizeof(int) == 4
  • sizeof(short) == 2
  • sizeof(char) == 1
  • big Endian

Mein Professor zeigte der folgende Code:

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

Hier sind die 20 Bytes darstellen arr:

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

Mein Professor erklärt, dass &arr[1] Punkte hier, die ich mit einigen.

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

verstehe ich jetzt, dass (char*) den Zeiger macht die Breite eines char (1 Byte) anstelle von der Breite eines int (4 Bytes).

Was ich nicht verstehe, ist die + 8, die mein Professor sagt Punkte hier:

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

Aber sollte es hier nicht darauf, da es nach vorn 8 mal die Größe eines char wird (1 Byte)?

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

Sie haben Recht.Schreiben Sie einen Code, um die Werte der Zeiger zu drucken, um Ihrem Professor zu zeigen, dass Sie Recht haben. –

+3

Es hängt alles von 'sizeof (int)' ab, was nicht unbedingt '4' ist. –

+2

@BlagovestBuyukliev Mein Professor sagte, dass "sizeof (int)" für die Dauer der Klasse 4 ist. Entschuldigung, das hätte ich erwähnen sollen. – Alexey

Antwort

4

Nehmen wir es Schritt für Schritt.

((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 Punkte an Byteort 12 innerhalb des Arrays: Ihr Ausdruck kann wie folgt zerlegt werden. cast_into_short[3] bezieht sich auf einen short Wert am Standort 12 + sizeof(short) * 3, der in Ihrem Fall 18 ist.

+3

Und deshalb sollte Code wie dieser nicht in einer Zeile geschrieben werden. Hier ist es kristallklar was vor sich geht. In dem Code in der Frage würde ich annehmen, dass der Professor sich mit zu vielen Klammern verwirrte. – gnasher729

+1

@ gnasher729: Oder vermeiden Sie zumindest völlig nutzlose Parens wie '(char *) (& arr [1])' (die hier für Leute zu sein scheinen, die nicht wissen, dass ein Cast einen unären Ausdruck annehmen kann, was, wenn nicht erlaubt, würde ohne die Paren sowieso nicht kompilieren, der Zweifel, der dies "klärt", kann sogar nicht in der Präzedenz ausgedrückt werden). Obwohl ich stimme zu, dass es wahrscheinlich in diesem Fall geteilt werden sollte. – mafso

+1

@mafso: Das zweite Klammerpaar ist auch nutzlos, also wäre die Nicht-Fracht-Kult-Version dieses Ausdrucks: '((short *) ((char *) & arr [1] + 8)) [3]' . –

1

Hier einige Code, der Ihnen zeigen kann, das Byte auf dem System geändert wird, zusammen mit einer Aufschlüsselung dessen, was geschieht:

#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; 
} 

Start aus dem innersten:

int Zeiger auf (arr + 4)

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

char Zeiger (arr + 4)

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

char Zeiger (arr + 4 + 8)

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

kurze Zeiger auf (arr + 4 + 8)

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

kurz bei (arr + 4 + 8 + (3 * 2)) (dies ist ein Array-Index)

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

Genau welches Byte auf dem Endianess Ihres Systems hängt hier geändert wird. Auf meinem kleinen Endian x86 bekomme ich folgende Ausgabe:

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 

Viel Glück mit deinem Kurs.

+1

Ihre Analyse des Ausdrucks ist korrekt, aber Sie scheinen die Behauptung des OP falsch verstanden zu haben, als Sie sagen, dass er nicht recht hat. Sein (bisher einziger) Schnitt macht keinen Unterschied, da die hinzugefügte Besetzung zu "short *" nicht Teil des Teilausdrucks ist, über den er sich erkundigt. –

+0

@JohnBollinger Danke, ich habe diese Aussage korrigiert! Fügen Sie auch die Zeigerposition und die Speichernutzung hinzu. –

1

Der Ausdruck wird die zwei Bytes 18 Bytes nach dem Beginn der arr auf den Wert 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]); 

    } 

} 

Mögliche Ausgang eingestellt:

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) 

In allen Professoren Spielereien er Ihnen 1 integer verschoben ist , 8 Zeichen/Bytes und 3 Kurzzeichen, die 4 + 8 + 6 = 18 Bytes. Bingo.

Beachten Sie, dass diese Ausgabe die Maschine zeigt, die ich ausgeführt habe, um 4 Byte Integer, 2 Byte kurz (common) und little-endian zu haben, da die letzten beiden Bytes des Arrays auf 0x64 bzw. 0x00 gesetzt wurden.

Ich finde Ihre Diagramme schrecklich verwirrend, weil es nicht sehr klar ist, wenn Sie das '|' Adressen sein oder nicht.

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

    ^ 1^ ^2 
A X  C  S B 

Fügen Sie die Stäbe ('|') A ist der Beginn Arr und B 'eine über das Ende' (ein Rechtsbegriff in C).

X ist die Adresse, auf die sich der Ausdruck & Arr [1] bezieht. C durch den Ausdruck (((char *) (& arr [1])) + 8). S durch den ganzen Ausdruck. S und das folgende Byte werden zugewiesen und was das bedeutet hängt von der Endian-Ness Ihrer Plattform ab.

Ich lasse es als eine Übung, um festzustellen, was die Ausgabe auf einer ähnlichen aber Big-Endian-Plattform, die ausgegeben werden. Jemand? Ich merke aus den Kommentaren du bist Big-Endian und ich bin Little-Endian (aufhören zu kichern). Sie müssen nur eine Zeile des Ausgangs ändern.