Zeiger auf array-Element: Inkrement WTF?

Gast #5197433
Moin,

ich probiere mich gerade in der ARM-Programmierung und ich habe den 
folgenden Code geschrieben:

1
const uint8_t cursorBitmap[] = {
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
int main(void)
8
{
9
    putBitmap(&(cursorBitmap)+ 10);
10
    putBitmap(&cursorBitmap[10]);
11

12
    while(1);
13
    return 0;
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???
Gast #5197760
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.
Gast #5197766
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.
Gast #5197921
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.
Moderator #5197967
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.
Moderator #5198053
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
void putBitmap(const uint8_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:

1
expected ‘const uint8_t * {aka const unsigned char *}’ but argument
2
is of type ‘const uint8_t (*)[20] {aka const unsigned char (*)[20]}’
Gast #5198241
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
void putBitmap(uint8_t *bm);
Ich muß das mal mit dem LLC ausprobieren, vielleicht ist der 
geschwätziger.
Moderator #5198345
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.
#5198514
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.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 8 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.