Moin,
ich probiere mich gerade in der ARM-Programmierung und ich habe den
folgenden Code geschrieben:
1
constuint8_tcursorBitmap[]={
2
8,8,0x7F,0x7F,0x00,0x7F,0x7F,0x3E,0x1C,0x08,// >
3
8,8,0x08,0x1C,0x3E,0x7F,0x7F,0x00,0x7F,0x7F// <
4
};
5
6
7
intmain(void)
8
{
9
putBitmap(&(cursorBitmap)+10);
10
putBitmap(&cursorBitmap[10]);
11
12
while(1);
13
return0;
14
}
Programmiert wird mit dem GCC. Wenn ich mich mit dem Debugger in die
Funktion einklinke, wird sie einmal mit dem Parameter "10" und einmal
mit dem Parameter "20" aufgerufen, obwohl sie eigentlich beide Mal den
gleichen Parameter haben sollte!!!
Was passiert hier???
cursorBitmap[] ist das Array von Bytes.
cursorBitmap liefert die Adresse des Arrays.
&cursorBitmap ergibt für mich nicht wirklich Sinn, da cursorBitmap
bereits die Adresse des Arrays liefert.
cursorBitmap+10 addiert 10 zum Zeiger auf das Array. Wenn die Bytes ohne
Lücke/Füllbytes ausgerichtet sind (das ist meistens so, muss aber nicht
zwingend der Fall sein, siehe Compiler Optionen), dann erhälst du so
einen Zeiger auf das elfte Element des Arrays.
cursorBitmap[10] liefert den Wert des elften Elementes des Arrays, egal
wie die Daten ausgerichtet sind.
&cursorBitmap[10] liefert die Adresse des elften Elementes des Array,
egal wie die Daten ausgerichtet sind.
Das stimmt nicht ganz, "&cursorBitmap[10]" ist immer absolut identisch
zu "cursorBitmap + 10", vollkommen unabhängig von
Ausrichtung/Padding/Größe der Elemente. Der Compiler rechnet hier
Basisadresse + sizeof(Element) * 10.
Arrays selbst haben gar kein Padding, das wurde hier schon besprochen:
Beitrag "Re: 32 Bitter: Einen Array packen"
Höchstens die Elemente selbst können Füllbytes enthalten, was sich auch
in sizeof() niederschlägt.
Danke für die Aufklärung.
Viel interessanter fände ich eine Erklärung, was denn
> &cursorBitmap
bewirken soll. Ich habe keine Ahnung, aber ein zufälliges Ergebnis wird
wohl kaum dabei heraus kommen.
Stefan U. schrieb:> Wenn die Bytes ohne> Lücke/Füllbytes ausgerichtet sind (das ist meistens so, muss aber nicht> zwingend der Fall sein, siehe Compiler Optionen),
Doch, das muss zwingend so sein bei arrays.
Stefan U. schrieb:> Viel interessanter fände ich eine Erklärung, was denn>>> &cursorBitmap>> bewirken soll.
Im Ausdruck cursorBitmap+10 ist cursorBitmap ein Zeiger auf uint8_t,
entsprechend wird bei der Adressberechnung die 10 mit sizeof(uint8_t)=1
multipliziert.
In &cursorBitmap+10 ist cursorBitmap ein Zeiger auf uint8_t[20] (das
Array hat auf Grund der Initialisierung 20 Werte). Die 10 wird nun nicht
mit 1, sondern mit sizeof(uint8_t[20])=20 multipliziert.
&cursorBitmap+10 zeigt somit auf eine Position, die 200 Bytes nach dem
Arrayanfang liegt. Da das Array selbst nur 20 Byte groß ist, bewirkt
eine Dereferenzierung des Ergebniszeigers undefiniertes Verhalten.
Stefan U. schrieb:> Mannomann, C hat ja echt ein paar gemeine Fallstricke. Aber man muss> entschuldigend berücksichtigen, wie alt diese Sprache ist.
Das hat nichts mit dem Alter der Sprache zu tun, wie bei jeder anderen
Sprache auch muss man eben wissen, was man tut.
Stefan U. schrieb:> Mannomann, C hat ja echt ein paar gemeine Fallstricke.
So arg fallstrickig ist das gar nicht, da der GCC hier in mindestens
einer der beiden putBitmap-Zeilen eine Warnung ausgibt (die natürlich
nur dann einen Nutzen hat, wenn sie auch gelesen wird ;-)). Ich vermute,
putBitmap ist als
1
voidputBitmap(constuint8_t*bm);
oder so ähnlich deklariert. Wird als Argument &cursorBitmap[10] oder
cursorBitmap+10 übergeben, passt der Datentyp. Bei &cursorBitmap+10
hingegen beschwert sich der Compiler zurecht:
Yalu X. schrieb:> In &cursorBitmap+10 ist cursorBitmap ein Zeiger auf uint8_t[20] (das> Array hat auf Grund der Initialisierung 20 Werte). Die 10 wird nun nicht> mit 1, sondern mit sizeof(uint8_t[20])=20 multipliziert.
Diese Logik ist gewöhnungsbedürftig, aber logisch.
Yalu X. schrieb:> (die natürlich> nur dann einen Nutzen hat, wenn sie auch gelesen wird ;-)).
Es gibt auch eine Warnung, nämlich
"warning: passing argument 1 of 'putBitmap' discards 'const' qualifier
from pointer target type [-Wdiscarded-qualifiers]", (was er allerdings
in allen Zeilen, wo putBitmap() aufgerufen wird, tut, da
1
voidputBitmap(uint8_t*bm);
Ich muß das mal mit dem LLC ausprobieren, vielleicht ist der
geschwätziger.
Uhrenmännchen schrieb:> Ich muß das mal mit dem LLC ausprobieren, vielleicht ist der> geschwätziger.
Oder mal -Wall -Wextra an den GCC übergeben. Sollte man während der
Entwicklung sowieso immer tun.
Dr. Sommer schrieb:> Oder mal -Wall -Wextra an den GCC übergeben. Sollte man während der> Entwicklung sowieso immer tun.
Beim GCC (7.2.0) und Clang (5.0.0) ist die Warnung defaultmäßig
aktiviert und kann ggf. mit -Wno-incompatible-pointer-types unterdrückt
werden. Die const-Warnung wird zusätzlich ausgegeben, wenn das const in
der Deklaration von putBitmap fehlt.
In C++ sind sowohl der falsche Zeigertyp als auch die const-Verletzung
echte Fehler und werden deswegen als Error ausgegeben.
Stefan U. schrieb:> cursorBitmap[] ist das Array von Bytes.> cursorBitmap liefert die Adresse des Arrays.
Nein. Es liefert die Adresse des ersten Array-Elements. Das ist zwar die
selbe Speicherstelle, auf die gezeigt wird, aber der Datentyp ist ein
anderer.
> cursorBitmap+10 addiert 10 zum Zeiger auf das Array.
Nein, das wäre das, was &cursorBitmap+10 macht. Man müsste dann 10
solche Arrays haben, und der Zeiger würde dann auf das elfte Array
zeigen. cursorBitmap+10 addiert 10 zum Zeiger auf das erste Element und
zeigt deshalb auf das elfte Element des Arrays.
> Wenn die Bytes ohne Lücke/Füllbytes ausgerichtet sind (das ist meistens> so, muss aber nicht zwingend der Fall sein, siehe Compiler Optionen),> dann erhälst du so einen Zeiger auf das elfte Element des Arrays.
Das ist nicht meistens so, sondern immer. Füllbytes kann es geben, die
sind aber Teil des Element-Typs. Zwischen den Array-Elementen darf es in
C keine Lücke geben. Deshalb erhält man immer einen Zeiger auf das elfte
Element.
Uhrenmännchen schrieb:> Yalu X. schrieb:>> In &cursorBitmap+10 ist cursorBitmap ein Zeiger auf uint8_t[20] (das>> Array hat auf Grund der Initialisierung 20 Werte). Die 10 wird nun nicht>> mit 1, sondern mit sizeof(uint8_t[20])=20 multipliziert.>> Diese Logik ist gewöhnungsbedürftig, aber logisch.
Ja, logische Logik ist was tolles. ;-)
Daniel H. schrieb:>> Mannomann, C hat ja echt ein paar gemeine Fallstricke. Aber man muss>> entschuldigend berücksichtigen, wie alt diese Sprache ist.>> Das hat nichts mit dem Alter der Sprache zu tun, wie bei jeder anderen> Sprache auch muss man eben wissen, was man tut.