Kurze Verständnisfrage (Adresse int array in C)

mogbo

Banned
Hallo,
eben was in einem Forum gelesen und ein wenig rumprobiert

Code:
#include <stdio.h>

int
main()
{
        int a[] = { 1, 2, 3 };
        printf("&a    = %p\n", &a);
        printf(" a    = %p\n", a);
        printf("&a[0] = %p\n", &a);

        printf("**(&a) = %i\n", **(&a));
        printf(" *(&a) = %i\n", *(&a)); // undefined behavior?
        printf(" *( a) = %i\n", *(a));

        return 0;
}
&a , a und &a[0] ergeben den gleichen Wert, also sollte doch eigentlich *(&a) und *a den gleichen Wert ausgeben?

Mir ist klar, dass wir hier von 2 unterschiedlichen Werten reden, einmal der Adresse und den Inhalt, der nur "zufällig" gleich ist. Ist es nun undefiniertes Verhalten zu erwarten, dass man mit *(&a) und *a den gleichen Wert erwartet? Ich kapiere den Denkfehler dahinter, aber nicht warums der Compiler nicht trotzdem ausgibt?

Output:
Code:
&a    = 0x7f7ffffc94f8
a    = 0x7f7ffffc94f8
&a[0] = 0x7f7ffffc94f8
**(&a) = 1
*(&a) = -224008
*( a) = 1
Warnings:
Code:
cc -O2 -pipe    -o test6 test6.c
test6.c:12:33: warning: format specifies type 'int' but the argument has type 'int *' [-Wformat]
        printf(" *(&a) = %i\n", *(&a)); // undefined behavior?
                         ~~     ^~~~~
1 warning generated.
 
Glaube ich kanns mir selbst beantworten, dem Compiler ists egal ob die Zahlen gleich sind, er weiß das die eine Zahl eine Addresse ist und die andere ein Inhalt und arbeitet dann dementsprechend, auch wenns ausgeschrieben auf dem Blatt die gleiche Zahl ist.

Ist es normal das bei int a[] = {1, 2, 3} a == &a[0] == &a ist? Ist dies allgemein für arrays gültig deren Speicher nicht mit malloc() erzeugt wird?
 
*(&a)) .... ist welcher Typ?
Nein - es ist nicht der Typ des 1. Elements vom int-Array - sondern meint das komplette Array - im Gegensatz zu *(a))

Schau dir die Größen mit size_t an - statt dem Inhalt
 
sizeof(*(&a)) == sizeof(a)

Mit dem Addressoperator (&) lässt du dir die Adresse des Feldes geben. Mit dem Dereferenzierungsoperator (*) wird das hinter der Adresse liegende Feld angesprochen. Die beiden Operationen heben sich also auf.

Rob
 
sizeof(*(&a)) == sizeof(a)

Mit dem Addressoperator (&) lässt du dir die Adresse des Feldes geben. Mit dem Dereferenzierungsoperator (*) wird das hinter der Adresse liegende Feld angesprochen. Die beiden Operationen heben sich also auf.

Rob
Schon klar, mir gings nur um die Info:
Code:
int a[] = {1, 2, 3}; // a hat die Adresse 0x1
Wenn ich nun *(a) steht innerhalb der Klammer prinipiell 0x1 und ich Zeige darauf, somit sehe ich den Wert 1 (a[0]). Wenn ich *(&a) mache steht in der Klammer prinzipiell auch 0x1, jedoch Zeige ich nicht auf a[0]. Das in diesem Fall Adresse == Wert ist war für mich komisch, vorallem da *(a) und *(&a) unterschiedliche Informationen ausgeben:
*(&a) = -224008
*( a) = 1
siehe oben
 
Der "Trick" ist in Sektion 6.3.2.1 des C11 Standards:

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue.

Im Normalfall konvertiert der Compiler dein Array int a[] in einen Pointer auf das erste Element des Arrays. Das passiert aber nicht, wenn dein Array der Operand des & Operators ist. Also:

  • *(a): a wird hier in einen Pointer auf das erste Element konvertiert, der Pointer danach dereferenziert und du siehst den Inhalt des ersten Array-Elements.
  • *(&a): Hier ist a der Operand des & Operators, es erfolgt also keine Konvertierung in einen Pointer. Damit gibt dir &a eine rohe Speicheradresse zurück, die du zum Pointer machst. Der Compiler meckert korrekterweise über Undefined Behaviour, da du einen nicht dereferenzierten Pointer (eine Speicheradresse) als Integer ausgeben willst. Auf einem 64 Bit System geht das schief und du siehst Müll. Probiere es mal mit %p, dann wird's deutlich.
  • **(&a): Das gleiche Konstrukt wie im Fall hierüber, nur das du deinen Pointer dereferenzierst und daher wieder den Inhalt des ersten Array-Elements siehst.
Das Standarddokument: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
 
Hi,


eine Speicheradresse ist unsigned, du konvertierst diese hier in einen (signed) int, dieser läuft über, daher der negative Wert.

Code:
#include <stdio.h>

int
main()
{
        int a[] = { 1, 2, 3 };
        printf("&a = %p\n", &a);
        printf(" a = %lu\n", a);
        printf("&a = %lu\n", &a);
        return 0;
}

Code:
&a = 0x7ffd49c85220
 a = 140737302790640
&a = 140737302790640

0x7ffd49c85220 == 140737302790640

Rob
 
eine Speicheradresse ist unsigned,
Nein, eine Adresse ist eine Adresse und kein Integer, egal ob signed oder unsigned.

Code:
        int a[] = { 1, 2, 3 };
        printf("&a = %p\n", &a);
        printf(" a = %lu\n", a);
        printf("&a = %lu\n", &a);
Das ist alles undefiniertes Verhalten:
"%lu" erwartet einen unsigned long, aber a hat bei Verwendung (wie Yamagi erklärt hat) den Typ int* und &a den Typ int(*)[3], also beides nicht unsigned long.
"%p" hingegen erwartet einen void*.
Ebenso haben 4 der 6 printf() im Originalbeitrag von mogbo undefiniertes Verhalten. (bis auf das 4. und 6.)

Vril hat hier völlig korrekt auf den Typ von &a hingewiesen, denn der ist int(*)[3], also Zeiger auf eine Reihung mit 3 Elementen mit Elementtyp int.
Das ist etwas anderes als &a[0] (oder äquivalent &*a), welcher int* ist, also Zeiger auf einen int.
Nur a hingegen hat einfach den Typ int[3], also eine Reihung von 3 int.
Wie Yamagi zutreffend angemerkt hat, degeneriert das jedoch in fast allen Kontexten sofort zu einem Zeiger auf das erste Element darauf.
Es sei noch darauf hingewiesen, dass eine Reihung kein Zeiger ist und ein Zeiger keine Reihung.

Zuletzt noch die korrekte Version der Ausgabe:
Code:
printf("%p\n", (void*)&a);
printf("%p\n", (void*)a);
printf("%p\n", (void*)&a[0]);
 
Zurück
Oben