Forum: Compiler & IDEs Leidiges Thema: Pointer/Array/Struct


von Peer (Gast)


Lesenswert?

Leider stoße ich wieder mal an meine Verständnislücke, was die 
Kombination dieser drei Themen angeht.

Ich habe:
1
struct farbe { uint8_t r; uint8_t g; uint8_t b; };
2
3
struct farbe feld[3][4];

Damit habe ich ein zweidimensionales array von structs. Auf die kann ich 
auch zugreifen
1
feld[1][2].r = 1;

Aber: Wie kann ich eindimensional, also linear auf die Elemente 
zugreifen. Die liegen ja im Speicher als rgbrgBrgb....
Ich will jetzt nacheinander auf die Werte zugreifen, Also quasi feld[5] 
um auf das große "B" (also den uint8_t Wert natürlich) zuzugreifen.
Pointer ist die Lösung. Aber wie die Notation?

von Mark B. (markbrandis)


Lesenswert?

Nicht getestet:

1
uint8_t * pointer; // Zeigervariable
2
3
pointer = &feld[0][0].r; // Zeiger auf erstes uint8_t Element richten
4
5
pointer++; // Zum nächsten Element inkrementieren

: Bearbeitet durch User
von Nase (Gast)


Lesenswert?

Peer schrieb:
> Ich will jetzt nacheinander auf die Werte zugreifen, Also quasi feld[5]
> um auf das große "B" (also den uint8_t Wert natürlich) zuzugreifen.
Das geht prinzipiell nicht.

Im ersten Ansatz hast du keine Garantie darüber, dass die drei uint8_t 
in der Struktur tatsächlich direkt hintereinander liegen. Sie könnten 
nämlich z.B. an der Wortgrenze ausgerichtet werden. Dann lägen Füllbytes 
dazwischen.

Daran scheitert auch Mark Brandis' (naiver) Ansatz. Wenn du deinen 
Compiler kennst, könntest du ihm solche Füllbytes aber austreiben, beim 
GCC z.B. mit dem packed-Attribut.

Alternativ ein langes eindimensionales uint8_t-Feld anlegen und die 
Indizes selbst berechnen. So in etwa 
Zeile-mal-Spalte-mal-3-plus-Farboffset.

von Mark B. (markbrandis)


Lesenswert?

Nase schrieb:
> Im ersten Ansatz hast du keine Garantie darüber, dass die drei uint8_t
> in der Struktur tatsächlich direkt hintereinander liegen. Sie könnten
> nämlich z.B. an der Wortgrenze ausgerichtet werden. Dann lägen Füllbytes
> dazwischen.

Das weiß der Compiler doch. Was hindert ihn daran, entsprechenden Code 
zu generieren der dies berücksichtigt?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mark Brandis schrieb:
> Nase schrieb:
>> Im ersten Ansatz hast du keine Garantie darüber, dass die drei uint8_t
>> in der Struktur tatsächlich direkt hintereinander liegen. Sie könnten
>> nämlich z.B. an der Wortgrenze ausgerichtet werden. Dann lägen Füllbytes
>> dazwischen.
>
> Das weiß der Compiler doch. Was hindert ihn daran, entsprechenden Code
> zu generieren der dies berücksichtigt?

Was soll er denn anders generieren?

Typ-Layout wird durch das ABI festgelegt.  Und dein pointer++ geht nach 
jedem Zugriff zu nächsten Byte, unabhängig vom Typ-Layout.

Was geht ist z.B.
1
unsigned gelb (int i)
2
{
3
    struct farbe *f = (struct farbe*) &feld[0][0];
4
    return f[i].r + f[i].g - f[i].b;
5
    // oder
6
    f += i;
7
    return f->r + f->g - f->b;
8
    // usw.
9
}

von Programmierer (Gast)


Lesenswert?

Mark Brandis schrieb:
> Was hindert ihn daran, entsprechenden Code zu generieren der dies
> berücksichtigt?

zB dass er nicht weiß, wann die struct Grenze erreicht ist. Bei den 
ersten zwei Inkrementierungen muss er nur um genau 1 Byte 
inkrementieren, bei der dritten eventuell noch um Padding Bytes, bei der 
4. - 5. wieder nur um eines Füße.. Da er aus "ptr ++" nicht herauslesen 
kann die wievielte Inkrementierung das ist, kann er auch keinen 
entsprechenden Code generieren. Und selbst wenn, dann würde es 
schieflaufen sobald man ptr auf ein normales Array zeigen lässt, weil 
dort keine Padding Bytes sind.

Johann L. schrieb:
> Was geht ist z.B.
Könnten da nicht eventuell noch Extra Padding Bytes zwischen den 
einzelnen Unter Arrays sein die so nicht berücksichtigt werden?

von Mullwark (Gast)


Lesenswert?

Johann L. schrieb:
> Typ-Layout wird durch das ABI festgelegt.  Und dein pointer++ geht nach
> jedem Zugriff zu nächsten Byte, unabhängig vom Typ-Layout.

Also mein C-Buch sagt:
"Ist p ein Zeiger auf eine Struktur, dann wird bei jeder Arithmetik mit 
p die Größe der Struktur berücksichtig; p++ inkrementiert daher p mit 
einem entsprechenden Wert, damit p dann auf das nächste Element im 
Vektor von Strukturen zeigt[...]."
Ist der Kernighan/Ritchie.
Im Anschluss wird auch von den Füllbytes geschrieben, die dabei 
berücksichtigt werden.

Mullwark

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Programmierer schrieb:
> Johann L. schrieb:
>> Was geht ist z.B.
> Könnten da nicht eventuell noch Extra Padding Bytes zwischen den
> einzelnen Unter Arrays sein die so nicht berücksichtigt werden?

Es können Paddings etc vorganden sein, aber diese werden berücksichtigt 
weil über den korrekten Typ zugegriffen wird.

Falls Padding am Ende der Struktur ist, dann gehört dieses zur Struktur 
und ist auch im Array und in sizeof berücksichtigt.

von Rolf M. (rmagnus)


Lesenswert?

Programmierer schrieb:
> Könnten da nicht eventuell noch Extra Padding Bytes zwischen den
> einzelnen Unter Arrays sein die so nicht berücksichtigt werden?

Nein. Die Elemente eines Arrays müssen immer direkt hintereinander 
stehen, und die Größe eines Arrays aus n Elementen ist n * 
sizeof(Element), also kann auch am Ende nichts mehr sein.
Bei der struct dagegen können zwischen den Elementen oder auch am Ende 
noch Padding Bytes liegen.

von Mark B. (markbrandis)


Lesenswert?

Das folgende Beispiel:

1
#include <stdint.h>
2
#include <stdio.h>
3
4
int main()
5
{
6
    struct farbe { uint8_t r; uint8_t g; uint8_t b; };
7
    struct farbe feld[3][4];
8
    uint8_t * pointer; // Zeigervariable
9
    uint8_t i;
10
    
11
    feld[0][0].r =  10;
12
    feld[0][0].g =  11;
13
    feld[0][0].b =  12;
14
    feld[0][1].r =  20;
15
    feld[0][1].g =  21;
16
    feld[0][1].b =  22;
17
    feld[0][2].r =  30;
18
    feld[0][2].g =  31;
19
    feld[0][2].b =  32;
20
    feld[0][3].r =  40;
21
    feld[0][3].g =  41;
22
    feld[0][3].b =  42;
23
    feld[1][0].r =  50;
24
    feld[1][0].g =  51;
25
    feld[1][0].b =  52;
26
    feld[1][1].r =  60;
27
    feld[1][1].g =  61;
28
    feld[1][1].b =  62;
29
    feld[1][2].r =  70;
30
    feld[1][2].g =  71;
31
    feld[1][2].b =  72;
32
    feld[1][3].r =  80;
33
    feld[1][3].g =  81;
34
    feld[1][3].b =  82;
35
    feld[2][0].r =  90;
36
    feld[2][0].g =  91;
37
    feld[2][0].b =  92;
38
    feld[2][1].r = 100;
39
    feld[2][1].g = 101;
40
    feld[2][1].b = 102;
41
    feld[2][2].r = 110;
42
    feld[2][2].g = 111;
43
    feld[2][2].b = 112;
44
    feld[2][3].r = 120;
45
    feld[2][3].g = 121;
46
    feld[2][3].b = 122;
47
48
    pointer = &feld[0][0].r; // Zeiger auf erstes uint8_t Element richten
49
    
50
    for (i=0; i < 36; i++)
51
    {
52
        printf("Inhalt von Element %2d: %3d\n", i, *pointer);
53
        pointer++; // Zum nächsten Element inkrementieren
54
    }
55
    
56
    return 0;
57
}

ergibt bei mir den folgenden Output:

1
Inhalt von Element  0:  10
2
Inhalt von Element  1:  11
3
Inhalt von Element  2:  12
4
Inhalt von Element  3:  20
5
Inhalt von Element  4:  21
6
Inhalt von Element  5:  22
7
Inhalt von Element  6:  30
8
Inhalt von Element  7:  31
9
Inhalt von Element  8:  32
10
Inhalt von Element  9:  40
11
Inhalt von Element 10:  41
12
Inhalt von Element 11:  42
13
Inhalt von Element 12:  50
14
Inhalt von Element 13:  51
15
Inhalt von Element 14:  52
16
Inhalt von Element 15:  60
17
Inhalt von Element 16:  61
18
Inhalt von Element 17:  62
19
Inhalt von Element 18:  70
20
Inhalt von Element 19:  71
21
Inhalt von Element 20:  72
22
Inhalt von Element 21:  80
23
Inhalt von Element 22:  81
24
Inhalt von Element 23:  82
25
Inhalt von Element 24:  90
26
Inhalt von Element 25:  91
27
Inhalt von Element 26:  92
28
Inhalt von Element 27: 100
29
Inhalt von Element 28: 101
30
Inhalt von Element 29: 102
31
Inhalt von Element 30: 110
32
Inhalt von Element 31: 111
33
Inhalt von Element 32: 112
34
Inhalt von Element 33: 120
35
Inhalt von Element 34: 121
36
Inhalt von Element 35: 122

Das Ganze auf einem PC mit Windows 7 Home Premium 64-Bit und mit gcc 
Version: 4.8.1

Dazu zwei Fragen:
1.) Ist es Zufall, dass dies funktioniert?
2.) Wenn man es nicht so machen soll, was ist dann der richtige Weg um 
über solche Elemente möglichst elegant und einfach zu iterieren? Oder 
gibt es in C hier keinen eleganten und einfachen Weg?

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Mark Brandis schrieb:
> 1.) Ist es Zufall, dass dies funktioniert?

Sagen wir mal so: Es ist von der C-Norm nicht garantiert. Ich würde 
erwarten, daß es gerade für den Fall uint8_t auf der überwiegenden 
Mehrzahl der Plattformen/Compiler funktioniert. Trotzdem ist es 
eigentlich ein Fehler.

> 2.) Wenn man es nicht so machen soll, was ist dann der richtige Weg um
> über solche Elemente möglichst elegant und einfach zu iterieren? Oder
> gibt es in C hier keinen eleganten und einfachen Weg?

Es gibt keinen. Wenn du iterieren willst, mußt du ein Array nehmen. Der 
Standard-Anwendungsfall von structs ist ja auch nicht unbdingt, daß alle 
Element den selben Typ haben. Sobald die Typen verschieden sind, ginge 
das so oder so nicht mehr.

von Tom (Gast)


Lesenswert?

Interessant.

gcc packt das struct auch hier (32bit Linux, gcc)  und muss erst mit
1
 struct farbe { uint8_t r; uint8_t g; uint8_t b; } __attribute__((aligned(4)));
überredet werden, zu padden und etwas nichtfunktionierendes zu liefern.

> elegant und einfach
Was für den einen elegant und einfach ist, ist für den anderen ein 
dreckiger Hack unter Ausnutzung von Annahmen über das Speicherlayout. 
Ich würde wahrscheinlich 2 Schleifen schachteln und in der inneren 
explizit auf die einzelnen Struct-Member zugreifen.

von Rolf M. (rmagnus)


Lesenswert?

Tom schrieb:
>> elegant und einfach
> Was für den einen elegant und einfach ist, ist für den anderen ein
> dreckiger Hack unter Ausnutzung von Annahmen über das Speicherlayout.

Ist halt so wie wenn du mit dem Auto an einer Kreuzung stehst, die Ampel 
ist rot, und gut erkennbar kein anderes Auto in der Nähe. Fährst du 
einfach (und elegant ;-) drüber, weil wahrscheinlich eh nix passieren 
wird, oder wartest du doch?

von 3D 3D (Gast)


Lesenswert?

Mal ein andere Idee. Warum nicht ein 3D array? Eine Dimension für die 
Farben, eine für die Zeilen und eine für die Spalten.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mark Brandis schrieb:
> 1.) Ist es Zufall, dass dies funktioniert?

Es ist kein Zufall. Gleichwohl bedeutet nicht, dass der Code in jedem 
Kontext korrekt ist.

von Markus (Gast)


Lesenswert?

Hi,

ich sehe keine Grund, warum ein Compiler bei
1
struct farbe { uint8_t r; uint8_t g; uint8_t b; };
Füllbytes einfügen sollte. Das würde keinen Sinn ergeben, da es auf 
keiner Plattform notwendig wäre. Oder gibt es welche mit 4-bit-Bytes?
Bei z.B.
1
struct farbe { uint8_t r; double g; uint8_t b; };
dagegen sähe ich es je ein.

Ein x-dimensionales Struct-Byte-Array habe ich auf allen möglichen 
Plattformen mit einem Byte-Pointer durchgeackert. Hat immer 
funktioniert.

Grüße, Markus

von Andreas R. (daybyter)


Lesenswert?

Meine erste Idee war eine Union.

Dann dachte ich aber, dass eine Klasse mehr Sinn macht. Schreib da halt 
nen Pointer zum physikalischen Speicher rein und füge Methoden wie plot, 
fill usw hinzu. Dann ist die Datenstruktur außerhalb völlig wurscht.

von Nase (Gast)


Lesenswert?

Markus schrieb:
> ch sehe keine Grund, warum ein Compiler bei [...] Füllbytes einfügen sollte.
Vielleicht, weil Zugriffe auf der Plattform langsamer sind, wenn sie 
nicht Wort-weise ausgerichtet sind?

Mullwark schrieb:
> "Ist p ein Zeiger auf eine Struktur, dann wird bei jeder Arithmetik mit
> p die Größe der Struktur berücksichtig;
Ja, aber p ist hier kein Zeiger auf eine Struktur, sondern ein Zeiger 
auf ein uint8_t. Darum auch der (uint8_t *)-Cast.

Andreas Rückert schrieb:
> Meine erste Idee war eine Union.
Auch keine gute Idee, derselben Problematik halber.

von Peer (Gast)


Lesenswert?

Wow. Hätte ich gewußt, daß ich so eine Diskussion lostrete... Dann muß 
ich ja gar nicht an meinen C Pointer Verständnislücken knabbern.
Aber ihr habt mir sehr geholfen!
Ich nutze das auf einem AVR und erwarte jetzt einfach, daß er mit
1
struct __attribute__((packed)) farbe { uint8_t r; uint8_t g; uint8_t b; };

Das Ding linear ablegt. Wenn nicht, habe ich Pech, aber es klappt erst 
einmal. Ist auch nicht gravierend, wenn  nicht, dann sehe ich es sofort 
und mit
1
pointer[i] = pointer[i-3];
iteriere ich durchs Feld.

Schönen Feiertag noch.

von meckerziege (Gast)


Lesenswert?

Mullwark schrieb:
> Johann L. schrieb:
>> Typ-Layout wird durch das ABI festgelegt.  Und dein pointer++ geht nach
>> jedem Zugriff zu nächsten Byte, unabhängig vom Typ-Layout.
>
> Also mein C-Buch sagt:
> "Ist p ein Zeiger auf eine Struktur, dann wird bei jeder Arithmetik mit
> p die Größe der Struktur berücksichtig; p++ inkrementiert daher p mit
> einem entsprechenden Wert, damit p dann auf das nächste Element im
> Vektor von Strukturen zeigt[...]."
> Ist der Kernighan/Ritchie.
> Im Anschluss wird auch von den Füllbytes geschrieben, die dabei
> berücksichtigt werden.
>
> Mullwark

Natürlich hat der Kernighan/Ritchie recht. Aber wenn du schon aus der "C 
Bibel" zitierst, dann mach es wenigstens richtig. Sie schreiben nämlich 
explizit von einem "Zeiger auf eine Struktur". Was zuvor verwendet 
wurde, ist allerdings ein Zeiger auf ein uint8_t, also NICHT auf eine 
Struktur. Deshalb kannst du eben doch Probleme mit den Füllbytes 
bekommen.

Und genau aus diesem Grund ist es sehr sehr schlechter Stil, einen 
Pointer auf ein Struct auf einen uint8_t* zu casten.
Solche Akrobatik ist normalerweise ein Zeichen dafür, dass schon viel 
früher etwas schiefgelaufen ist: Beim Design und der Planung der 
Software.

Wenn du mehr über die Feinheiten von C erfahren willst, dann empfehle 
ich dir beispielsweise MISRA-C. Dort sind viele Beispiele enthalten, die 
aufzeigen, wie schnell etwas schief gehen kann.

von 3D 3D (Gast)


Lesenswert?

meckerziege schrieb:
> ist es sehr sehr schlechter Stil, einen
> Pointer auf ein Struct auf einen uint8_t* zu casten

Genau, das ist reines Glücksspiel. Unterschiedliche 
Optimierungseinstellungen werden auf unterschiedlichen Plattformen 
unterschiedliche Ergebnisse bringen. Wenns um Geschwindigkeit geht, sind 
Füllbytes ein Standardmittel.

Beitrag "Re: Leidiges Thema: Pointer/Array/Struct"
1
#define RT  0
2
#define GR  1
3
#define BL  2
4
#define RGB 3
5
6
#define COLS  4
7
#define LINES 3
8
9
uint8_t farbe[LINES][COLS][RGB];
10
11
uint8_t* colorptr = farbe;
12
// uint8_t* colorptr = &(farbe[0][0][RT]);  // Ptr ausf erste Element
13
14
farbe[3][4][RT] = 1;
15
16
for (uint16_t i = 0;i < sizeof (farbe);i++) {
17
  foo (coloptr++);        // jedes Element: Farbe pro Spalte pro Linie
18
  ...
19
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

3D 3D schrieb:
> meckerziege schrieb:
>> ist es sehr sehr schlechter Stil, einen
>> Pointer auf ein Struct auf einen uint8_t* zu casten
>
> Genau, das ist reines Glücksspiel. Unterschiedliche
> Optimierungseinstellungen werden auf unterschiedlichen
> Plattformen unterschiedliche Ergebnisse bringen.

Unterschiedliche Optimierungseinstellungen können immer ui anderem 
Binärcode führen, auch auf der gleichen Plattform.

Was sich durch Optimierungseinstellungen nicht ändert ist die Semantik 
des Codes oder das implementierte ABI.

GCC-Optionen wie -f[no-]short-enums, -f[no-]unsigned-char, -fpack-struct 
sind übrigens keine Optimierungsoptionen, auch wenn manche IDEs 
wie Atmel-Studio suggerieren es seien welche und diese ungefragt 
aktivieren.

Solche Optionen ändern das ABI!

von Bernd K. (prof7bit)


Lesenswert?

Für uint8_t gibt es keine Alignment-Anforderung.

Alle breiteren Typen sind auf den meisten (allen neueren) Architekturen 
"self-aligned", also die Adresse muss durch ihre eigene Länge teilbar 
sein:

Beispiel A, falsch (erzwingt Füllbytes):
1
struct {
2
    uint8_t a;    // addr 0
3
    uint8_t b;    // addr 1 (ok weil 1-aligned)
4
                  // addr 2 (füllbyte)
5
                  // addr 3 (füllbyte)
6
    uint32_t c;   // addr 4 (braucht 4-aligned)
7
    uint16_t d;   // addr 8 (ok weil 2-aligned)
8
}

Beispiel B, richtig:
1
struct {
2
    uint8_t a;    // addr 0
3
    uint8_t b;    // addr 1
4
    uint16_t d;   // addr 2 (ok weil 2-aligned)
5
    uint32_t c;   // addr 4 (ok weil 4-aligned)
6
}

Ich würde auf jeden Fall meine Datenstrukturen so auslegen daß es 
schonmal nach der Methode in Beispiel B passt, das ist schonmal die 
halbe Miete, selbst wenn man dann noch zusätzlich ein packed erzwingt um 
auch auf exotischen CPUs das selbe Layout zu erzielen dann hat man sein 
möglichstes getan.

Wenn man jedoch wie im Beispiel A schon mit einer krummen Anordnung 
anfängt dann ist an diesem Punkt das Kind schon in den Brunnen gefallen, 
wenn man dann packed erzwingt wird es auf keiner Platform (mit mehr 
als 8 bit) performanten Code erzeugen, der Compiler muss dann immer 
übles Byte-geschubse erzeugen.

Oder mit anderen Worten: Wenn ihr packed erzwingen wollt dann ordnet die 
Daten schonmal so an (notfalls eigene Füllbytes einfügen) daß sie sich 
hinterher auch schmerzfrei packen lassen.

: Bearbeitet durch User
von Markus (Gast)


Lesenswert?

Nase schrieb:
> Vielleicht, weil Zugriffe auf der Plattform langsamer sind, wenn sie
> nicht Wort-weise ausgerichtet sind?
Welche Plattform kann auf ein Byte nicht effizient Byte-Boundary-weise 
zugreifen sondern muß dazu das Byte auf Wortgrenze abgelegt haben und 
der Compiler unterstützt dies per default? Bei 64-bit Plattformen würde 
Software von einem derart optimierenden Compiler erzeugt sieben von acht 
Bytes verschwenden und könnte kein einziges BMP, GIF oder JPG 
verarbeiten.
Das ist doch Schwachsinn.

von 3D 3D (Gast)


Lesenswert?

@Markus
Es gibt einen Unterschied zwischen struct und array. ;-)

von Bernd K. (prof7bit)


Lesenswert?

Das Stride eines Array of struct ergibt sich aus der 
alignment-Anforderung seines grössten Elements. Also im vorliegenden 
Falle 3 Byte weil das grösste Element nur 1byte-aligned sein muss. Also 
gibt es es diesem Array keine Füllbytes.

Hätte er jedoch sowas (nur mal als Beispiel):
1
struct foo {
2
    uint16_t bar;
3
    uint8_t baz;
4
}

Dann wäre bei einem Array zwar im nullten Element noch alles ordentlich 
aligned, beim nächsten jedoch muss sichergestellt sein dass auch dort 
das uint16_t bar ebenfalls wieder an einer 2er-Adresse anfängt. Daher 
würde obwohl das struct eigentlich nur 3 Byte benötigt dennoch die 
Anforderung erwachsen daß die Stride-Länge zwingend durch 2 teilbar sein 
muss, ansonsten würde die Hälfte aller uint16_t bar auf krumme Adressen 
zu liegen kommen. In diesem Falle würde also nach jedem Arrayelement so 
viele Füllbytes angefügt bis man auf ein Vielfaches von 2 kommt. In 
diesem Falle also 1 Füllbyte zwischen jedem Array-Element, also eine 
Stride-Länge von 4.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Das Stride eines Array of struct ergibt sich aus der
> alignment-Anforderung seines grössten Elements. Also im vorliegenden
> Falle 3 Byte weil das grösste Element nur 1byte-aligned sein muss. Also
> gibt es es diesem Array keine Füllbytes.

Das ist wie gesagt der üblich anzutreffende Fall, aber von C nirgends so 
vorgegeben.

> In diesem Falle würde also nach jedem Arrayelement so viele Füllbytes
> angefügt bis man auf ein Vielfaches von 2 kommt.

Nicht nach jedem Array-Element, sondern als Teil jedes Array-Elements. 
struct foo ist dann eben 4 Bytes groß. Zwischen den Elementen eines 
Arrays darf kein Platz sein.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Markus schrieb:
> Nase schrieb:
>> Vielleicht, weil Zugriffe auf der Plattform langsamer sind, wenn sie
>> nicht Wort-weise ausgerichtet sind?
> Welche Plattform kann auf ein Byte nicht effizient Byte-Boundary-weise
> zugreifen sondern muß dazu das Byte auf Wortgrenze abgelegt haben und

ARM?

Ist nicht die einzige Plattform die GCC als "strict alignment" führt, 
d.h. es kann nicht einfach so per 32-Bit Zugriff von einer Adresse 
gelesen werden, die nicht 0 mod 4 ist.  Dies wiederum wird angenommen, 
wenn ein packed zugegriffen werden soll, und wenn von diesem nur die 
Adresse bekannt ist (und kein assume_aligned angegeben ist) dann muss 
der Wert eben als Hackfleisch geladen werden.

Bernd K. schrieb:
> Das Stride eines Array of struct ergibt sich aus der
> alignment-Anforderung seines grössten Elements. Also im vorliegenden
> Falle 3 Byte weil das grösste Element nur 1byte-aligned sein muss.
> Also gibt es es diesem Array keine Füllbytes.

Ich find's grad nicht im Standard, gib mal nen Tipp wo es nachzulesen 
ist.

von 3D 3D (Gast)


Lesenswert?

Danke Rolf, endlich jemand mit Hirn. :-D

von Bernd K. (prof7bit)


Lesenswert?

Johann L. schrieb:
> Ich find's grad nicht im Standard, gib mal nen Tipp wo es nachzulesen
> ist.

Das steht wahrscheinlich nicht im Standard weils aus Sicht des Standards 
wahrscheinlich entweder nicht definiert oder irrelevant ist.

Aber ich schrieb ja: Er soll sich anstatt blind ein packed dranzuhängen 
und schräges Alignment mit der Brechstannge zu erzwingen zunächst die 
Daten geschickterweise so anordnen daß sie sich auf den üblichen oder 
angestrebten Plattformen mit den üblichen Compilern von selbst nahtlos 
zusammenfügen und dann erst das packed dranschreiben um diesen Zustand 
zu zementieren für den Fall dass jemals wieder eine Architektur in Mode 
kommt die sich diesbezüglich nicht verhält wie ARM oder x86.

von 3D 3D (Gast)


Lesenswert?

@Bernd
So weiß du es willst, steht es doch schon da
Beitrag "Re: Leidiges Thema: Pointer/Array/Struct"

von Bernd K. (prof7bit)


Lesenswert?

Rolf Magnus schrieb:
> Das ist wie gesagt der üblich anzutreffende Fall,

Ja

> aber von C nirgends so
> vorgegeben.

Das spielt doch keine Rolle mehr wenn er es geschickterweise so 
einfädelt daß auf der üblich anzutreffenden Plattform das attribute 
packed keine Änderung mehr bewirken würde. Dann kann er hinterher guten 
Gewissens packed hinschreiben ohne dadurch das von der üblichen CPU 
benötigte Alignment zu opfern.

von Bernd K. (prof7bit)


Lesenswert?

Johann L. schrieb:
>> Welche Plattform kann auf ein Byte nicht effizient Byte-Boundary-weise
>> zugreifen sondern muß dazu das Byte auf Wortgrenze abgelegt haben und
>
> ARM?

Nein, auch auf ARM braucht ein Byte ein Alignment von nur 1 und der gcc 
plaziert sie daher alle lückenlos.

von Nase (Gast)


Lesenswert?

Bernd K. schrieb:
> Johann L. schrieb:
>>> Welche Plattform kann auf ein Byte nicht effizient Byte-Boundary-weise
>>> zugreifen sondern muß dazu das Byte auf Wortgrenze abgelegt haben und
>>
>> ARM?
>
> Nein, auch auf ARM braucht ein Byte ein Alignment von nur 1 und der gcc
> plaziert sie daher alle lückenlos.
Jetzt bin ich aber gespannt, zumal ich weiß, dass Johann sich recht gut 
mit der Materie auskennt.

von 3D 3D (Gast)


Lesenswert?


von Bernd K. (prof7bit)


Lesenswert?

3D 3D schrieb:
> guckst du ganz unten
> 
http://www.mikroe.com/download/eng/documents/compilers/mikroc/pro/arm/help/arm_specifics.htm

Nochmal: Ein Byte-Zugriff ist immer aligned weil für Byte ein Alignment 
von 1 gilt.

von 3D 3D (Gast)


Lesenswert?

Bernd K. schrieb:
> Nochmal:

Aus Performance kann (wird) der Compiler ein struct auffüllen. Er darf 
es, er kann es, er macht es.

Nimm das verf... 3 dimensionale array und gut. Das deckt auch genau die 
Aufgabenstellung ab. Mit struct ist und bleibt es Bullshit!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Nase schrieb:
> Bernd K. schrieb:
>> Johann L. schrieb:
>>>> Welche Plattform kann auf ein Byte nicht effizient Byte-Boundary-weise
>>>> zugreifen sondern muß dazu das Byte auf Wortgrenze abgelegt haben und
>>>
>>> ARM?
>>
>> Nein, auch auf ARM braucht ein Byte ein Alignment von nur 1 und der gcc
>> plaziert sie daher alle lückenlos.
> Jetzt bin ich aber gespannt, zumal ich weiß, dass Johann sich recht gut
> mit der Materie auskennt.

Ich hab beim Lesen gepatzt :-/ nämlich dass ein Byte gelesen wird.

von Bernd K. (prof7bit)


Lesenswert?

3D 3D schrieb:
> Bernd K. schrieb:
>> Nochmal:
>
> Aus Performance kann (wird) der Compiler ein struct auffüllen. Er darf
> es, er kann es, er macht es.
>
> Nimm das verf... 3 dimensionale array und gut

Nein, struct ist OK und besser lesbar. Nichts wird aufgefüllt weil alles 
bereits perfekt aligned ist. Gründe habe ich dargelegt.

von 3D 3D (Gast)


Lesenswert?

Bernd K. schrieb:
> und besser lesbar

Wieso das denn? Nach deiner Logik fehlen dann noch Verschachtelungen:
1
farbe.line[3].col[4].rgb = 1;
Und das ist doch in C absoluter Blödsinn! Es handelt sich um ein 
stinknormales array mit drei Dimensionen.
Beitrag "Re: Leidiges Thema: Pointer/Array/Struct"
Und da kann man ordentlich mit einem typisierten Zeiger durchlaufen. 
Alles ist definiert.

Bernd K. schrieb:
> Gründe habe ich dargelegt

Du hast über deine persönliche Einschätzung gegenüber den 
Compilerbauern berichtet. Gründe gibt es nicht, da es nicht definiert 
ist, wie die struct im Speicher abgelegt wird. Die gewünschte Zeigerei 
ist so daher nicht definiert und reine Glückssache.

von Bernd K. (prof7bit)


Lesenswert?

3D 3D schrieb:
> da es nicht definiert ist, wie die struct im Speicher abgelegt wird.

Doch,  im jeweiligen ABI ist das spezifiziert. Versuch doch mal eins zu 
finden bei dem char anders aligned ist als 1 oder ein struct unnötiges 
Padding enthalten darf.

von 3D 3D (Gast)


Lesenswert?

Bernd K. schrieb:
> im jeweiligen ABI

Es interessiert der C Standard und nicht die persönliche Auslegung 
einzelner Ausführungen. Ist es denn so schwer?

von Mark B. (markbrandis)


Lesenswert?

3D 3D schrieb:
> Es interessiert der C Standard und nicht die persönliche Auslegung
> einzelner Ausführungen.

Das stimmt so pauschal nicht. In realen Software-Projekten wechselt man 
eben nicht ständig den Compiler, das Betriebssystem, die SW-Bibliotheken 
und so weiter. Viele Industrieprojekte haben gar nicht den Anspruch, 
etwas zu erschaffen was auch in zehn Jahren und unter anderen 
Randbedingungen genau so gut funktioniert und sich hundertprozentig an 
sämtliche Standards hält. Ob das gut oder schlecht ist sei dahingestellt 
- es ist aber nun mal die Realität.

: Bearbeitet durch User
von 3D 3D (Gast)


Lesenswert?

Mark Brandis schrieb:
> Viele Industrieprojekte haben gar nicht den Anspruch,
> etwas zu erschaffen was auch in zehn Jahren und unter anderen
> Randbedingungen genau so gut funktioniert und sich hundertprozentig an
> sämtliche Standards hält.

Puuuuhhhh, das ist mal ne Ansage. :-(((

Die Realität sieht anders aus. Die Softwarequalität in der Industrie 
steigt ständig. Der Ruf nach Tools zur Softwareanalyse und zum 
Fehlerausschluss wird immer lauter. Das geht einher mit wieder zu 
verwendenen Bibliotheken und Modulen, da es sonst zu teuer wird. So ein 
Sch.. mit falsch verstandenen struct wie hier darf es dabei nicht geben.

von Bernd K. (prof7bit)


Lesenswert?

3D 3D schrieb:
> mit falsch verstandenen struct wie hier darf es dabei nicht geben.

Ich glaube DU bist es der structs nicht versteht. Es ist haarklein 
definiert wie ein struct auszusehen hat, wie große es ist, wann und wo 
das Padding eingefügt wird und wieviel davon.

Was glaubst Du warum man jederzeit in der Lage ist zum Beispiel eine 
vorkompilierte Library-Funktion, sagen wir mal zum Beispiel eine 
Funktion im Windows-API aufzurufen und ihr Pointer auf Arrays von 
Structs übergeben kann ohne sich die Bohne darum zu kümmern welchen 
Compiler man in Redmond an diesem regnerischen Nachmittag benutzt hat? 
Weils im ABI für x86, im ABI von x86-64, im ABI von ARM genau definiert 
ist. Da steht auch drin in welcher Reihenfolge die Argumente für 
Funktionen übergeben werden, wie der Stack zu behandeln ist, etc. Alles 
was Du hier als "Glückssache" hinstellst ist dort haarklein definiert.

Auch dass sizeof(char) == 1 steht da drin.

Und jetzt erklär mir wie wahrscheinlich es ist daß wir in diesem Leben 
noch mit einer neuen CPU-Generation konfrontiert werden bei dem ein Byte 
mehr als eine Speicherstelle braucht und/oder nicht mehr 1-aligned ist? 
Sollte das jemals geschehen garantiere ich Dir daß mehr als nur dieser 
Code umgeschrieben werden muss.

: Bearbeitet durch User
von Nase (Gast)


Lesenswert?

Bernd K. schrieb:
> Was glaubst Du warum man jederzeit in der Lage ist zum Beispiel eine
> vorkompilierte Library-Funktion, sagen wir mal zum Beispiel eine
> Funktion im Windows-API aufzurufen und ihr Pointer auf Arrays von
> Structs übergeben kann ohne sich die Bohne darum zu kümmern welchen
> Compiler man in Redmond an diesem regnerischen Nachmittag benutzt hat?
> Weils im ABI für x86, im ABI von x86-64, im ABI von ARM genau definiert
> ist.
Nein, weil genau das eben nicht funktioniert.

> Da steht auch drin in welcher Reihenfolge die Argumente für
> Funktionen übergeben werden, wie der Stack zu behandeln ist, etc. Alles
> was Du hier als "Glückssache" hinstellst ist dort haarklein definiert.
Das ist falsch.
Gerade das ist problematisch bis falsch.
Windows-API mit einem nicht-Microsoft-Compiler zu verwenden führt sehr 
schnell zu Problemen. Das WinAPI verwendet nämlich stdcall, und das 
ist inhärent inkompatibel zum üblichen cdecl, wie es etwa der GCC 
standardmäßig verwendet. Darum gibt es von vielen Bibliotheken, etwa Qt, 
auch eigene Kompilate für MSVC und GCC.

> Auch dass sizeof(char) == 1 steht da drin.
Diese Aussage ist in dieser Form eindeutig falsch. Das steht nicht im 
ABI, sondern das ist im C-Standard definiert. Mehr oder weniger 
Wortwörtlich, sizeof(char) = 1. Das hat mit dem ABI nichts zu tun.

von Tom (Gast)


Lesenswert?

Abgesehen davon, ob das Gepointere funktioniert: Programmierer sollen 
gefälligst das hinschreiben, was sie meinen, und nicht eine 
Konstruktion, die im Speicher den gleichen Effekt hat wie das, was sie 
eigentlich meinen.
1
for (uint8_t y = 0; y < HEIGHT; ++y)
2
{
3
    for (uint8_t x = 0; x < WIDTH; ++x)
4
    {   // dunkles grau ist schoen
5
        img[y][x].r = 10;
6
        img[y][x].g = 10;
7
        img[y][x].b = 10;
8
    }
9
}
ist von jedem innerhalb von 3 Sekunden vollständig zu verstehen.

1
uint8_t* p = &img[0][0].r;
2
for (uint16_t i = 0; i < WIDTH * HEIGHT * sizeof(farbe); ++i)
3
{
4
    *p = 10;
5
}
ist die Art Code, die ich am Freitagnachmittag nach 4h Suche nach dem 
Bug, der manchmal am unteren Bildrand eine grüne Linie einblendet, nicht 
lesen will. Hier sind nämlich einige implizite Annahmen eingebaut, die 
ich dann alle überprüfen muss. Jemand hat im struct einen Alpha-Kanal 
nachgerüstet und nicht in der obigen Funktion fill_dark_gray() 
nachgesehen, ob diese low level im Speicher rummacht => Bug. Jemand hat 
auf 16-Bit-Farben (5,6,5Bit) umgebaut => Unfug. Jemand hat die 
struct-Member umsortiert und r ist nicht mehr das erste => In den 
Speicher hinter dem Bild wird 10 geschrieben=fieser Bug. usw. usw.
Ohne einen Kommentar a la
1
/*
2
 * HACK: das spart ggue. geschachtelter Schleifen mit 
3
 * certified_safe_c.exe (ver. 1997-4) mindestens 1827 
4
 * Takte und ist noetig fuer die erwuenschte Framerate. 
5
 */
wäre ich geneigt, den Urheber für einen rücksichtslosen Frickler zu 
halten, der mein Leben unnötig schwer macht und der seinen Spaß an 
unnötiger Komplexität in seiner Freizeit ausleben sollte.

von Bernd K. (prof7bit)


Lesenswert?

Nase schrieb:

> stdcall

ja, Punkt für Dich, aber die Datentypen und zusammengesetzte Datentypen 
sind identisch.

> Das steht nicht im
> ABI, sondern das ist im C-Standard definiert.

Na also, noch besser (im ABI stehts übrigens meist der Vollständigkeit 
halber nochmal drin, also doppelt gemoppelt). Das impliziert auch 
automatisch daß es immer 1-aligned ist sonst könnte man keine Arrays 
davon machen. Und 1-aligned wiederum (zusammen mit der Forderung in 
allen relevanten ABIs daß nur soviel Padding wie nötig verwendet wird) 
impliziert daß das struct aus post #1 eine Größe von 3 hat.

Ich zitiere mal beispielhaft aus dem ARM-32 ABI (bei ARM 64 stehts 
wortwörtlich genauso drin):

[quote]
1
                         Byte    Byte
2
Type Class Machine Type                 Note
3
                         size alignment
4
Integral   Unsigned byte  1        1    Character
1
4.3     Composite Types
2
A Composite Type is a collection of one or more Fundamental Data Types that are handled as a single entity at
3
the procedure call level. A Composite Type can be any of:
4
   An aggregate, where the members are laid out sequentially in memory
5
   A union, where each of the members has the same address
6
   An array, which is a repeated sequence of some other type (its base type).
7
The definitions are recursive; that is, each of the types may contain a Composite Type as a member.
8
9
4.3.1 Aggregates
10
   The alignment of an aggregate shall be the alignment of its most-aligned component.
11
   The size of an aggregate shall be the smallest multiple of its alignment that is sufficient to hold all of its
12
    members when they are laid out according to these rules.
13
14
4.3.2 Unions
15
  The alignment of a union shall be the alignment of its most-aligned component.
16
  The size of a union shall be the smallest multiple of its alignment that is sufficient to hold its largest member.
17
18
4.3.3 Arrays
19
  The alignment of an array shall be the alignment of its base type.
20
  The size of an array shall be the size of the base type multiplied by the number of elements in the array.
[/quote]

Soviel also zur "Glückssache". Das ist glasklar und unmißverständlich 
geregelt, da wird nichts dem Zufall überlassen.

von Le X. (lex_91)


Lesenswert?

Mal bisl abseits von den Wortklaubereien hier, nachdem ich kurz drüber 
nachgedacht hab frag ich mich jetzt ernsthaft:

Wie schaffts ein gcc-Kompilat denn nun, eine Win32-Api zu benutzen? Die 
API ist höchstwahrscheinlich nicht mit dem gcc gebaut worden, und laut 
Wiki benutzt der MS-Compiler andere Aufrufkonventionen...

Ich selbst musste mal Boost kompilieren, weil die Binaryies die ich 
gefunden habe nicht mit dem gcc wollten. Ist doch eigentlich das selbe 
Problem? :-/

von Bernd K. (prof7bit)


Lesenswert?

le x. schrieb:
> Wie schaffts ein gcc-Kompilat denn nun, eine Win32-Api zu benutzen?

Du deklarierst die externen API-Aufrufe als __stdcall. Der gcc kann das, 
auch andere Compiler die Windows-Binaries bauen können sollen haben so 
eine Option, z.B. Delphi oder FPC, da deklarierst Du die externen 
Prozeduren und Funktionen als "stdcall".

von Mark B. (markbrandis)


Lesenswert?

3D 3D schrieb:
> Die Realität sieht anders aus. Die Softwarequalität in der Industrie
> steigt ständig.

Davon merk ich aber nichts. Es gibt Displays von Getränkeautomaten die 
abstürzen, es gibt Züge die komplett aus- und wieder eingeschaltet 
werden müssen damit sie wieder fahren, mein Autoradio brauchte ein 
Software-Update weil es ne Macke hatte. Alles innerhalb der letzten 
zwölf Monate erlebt.

Wenn Du gesagt hättest "die Komplexität steigt ständig", dem würde ich 
zustimmen.

von Mark B. (markbrandis)


Lesenswert?

Das hier passt grad wie die Faust aufs Auge:

"Boeing 787: US-Luftfahrtbehörde warnt vor Stromausfall im Dreamliner

Die US-Luftfahrtbehörde FAA (Federal Aviation Administration) hat eine 
Warnung für den Boeing-Langstreckenjet 787 Dreamliner herausgegeben. 
Demnach kann ein Softwareproblem dazu führen, dass in dem Flugzeug der 
Strom ausfällt, sodass die Piloten die Kontrolle über den Flieger 
verlieren würden."

Link: 
http://www.spiegel.de/wissenschaft/technik/boeing-787-faa-warnt-vor-stromausfall-im-dreamliner-a-1031728.html

Machen wir uns doch nichts vor: So lange im Management hauptsächlich 
Leute sitzen, die von Softwareentwicklung nichts verstehen, wird das 
Thema Softwarequalität immer stiefmütterlich behandelt werden. Man macht 
dann halt gerade so viel wie man muss um noch eine Zulassung zu bekommen 
- mehr aber nicht.

von W.S. (Gast)


Lesenswert?

Peer schrieb:
> Damit habe ich ein zweidimensionales array von structs. Auf die kann ich
> auch zugreifenfeld[1][2].r = 1;
>
> Aber: Wie kann ich eindimensional, also linear auf die Elemente
> zugreifen. Die liegen ja im Speicher als rgbrgBrgb....
> Ich will jetzt nacheinander auf die Werte zugreifen, Also quasi feld[5]
> um auf das große "B" (also den uint8_t Wert natürlich) zuzugreifen.
> Pointer ist die Lösung. Aber wie die Notation?


Viel Lärm um nichts und nur Unverständnis!

Begreife mal, daß es in C keine mehrdimensionalen Felder gibt. Basta.

Wenn du sowas wie char blabla[3][4][5] schreibst, was glaubst du 
eigentlich, was du damit tatsächlich angerichtet hast???

Du hast zunächst erstmal einen namenlosen Typ char[5] geschaffen, dann 
einen ebenso namenlosen Typ von Zeigern auf den ersten namenlosen Typ 
und von diesem ein Feld von 4 Elementen, dann noch einen namenlosen Typ 
von Zeigern auf den zweiten Typ - und von allen Typen hast du arrays 
geschaffen. Da kannst du nur hoffen, daß dein Compiler damit beim 
Optimieren kräftig aufräumt und intern was ganz anderes mit 
vergleichbarer Funktionalität macht. Aber garantieren kann dir das 
keiner. Nochmal: blabla ist ein Feld von 3 Zeigern, von denen jeder auf 
ein Feld von je 4 weiteren Zeigern zeigt, von denen jeder dann auf ein 
Feld von 4 char's zeigt. Jedenfalls dann, wenn der Compiler angesichts 
dieses Urwaldes von Zeigern nicht zur Machete greift und sich heimlich 
einen sinnvolleren Ersatz schafft.

Wird dir jetzt was klar? Dein Glaube "Die liegen ja im Speicher als 
rgbrgBrgb...." ist ein Denkfehler der ganz groben Art. Das hat GARNICHTS 
mit Alignments zu tun, sondern mit deinem falschen Verständnis von 
Feldern in C. Physisch kann es durchaus vorkommen, daß all diese 
Felderchen tatsächlich hintereinander im Speicher liegen. Aber ohne 
jegliche Garantie!

Mir ist klar, daß diese Thematik gerade für mathematische Zwecke eine 
echte Problemecke ist, weswegen es eben für Mathematikzwecke weitaus 
besser ist, eine Sprache zu verwenden, die tatsächlich mehrdimensionale 
Felder beherrscht.

Aber hier geht es doch garnicht um echte mathematische Themen, sondern 
dein Ansinnen wäre es ja bloß, die Pixel eines Displays oder eines 
Bildes adressieren zu können - und da solltest du dir solche Konstrukte 
ganz einfach verkneifen und mit einem ganz simplen eindimensionalen Feld 
von einfachen Daten (also word oder dword oder so) arbeiten. Wo dann 
innerhalb eines solchen Elementes die Bits für Rot, Grün, Blau 
tatsächlich liegen, ist anwendungsspezifisch. Genau aus diesem Grunde 
gibt es ja bei Windows DIB, also geräteunabhängige Bitmaps, die man zum 
Konvertieren von Grafiken einer Anwendung auf eine konkrete Hardware 
dringend benötigt.

W.S.

von Bernd K. (prof7bit)


Lesenswert?

W.S. schrieb:
> Du hast zunächst erstmal einen namenlosen Typ char[5] geschaffen, dann
> einen ebenso namenlosen Typ von Zeigern

Bullshit.

> hoffen, daß dein Compiler damit beim
> Optimieren kräftig aufräumt und intern was ganz anderes mit
> vergleichbarer Funktionalität macht.

Das darf er nicht, es ist auf jeder Plattform glasklar und 
unmißverständlich definiert wie diese Datenstruktur physikalisch 
auszulegen ist. Der Compiler hat hier genau null Spielraum. Siehe oben.

: Bearbeitet durch User
von Nase (Gast)


Lesenswert?

W.S. schrieb:
> Nochmal: blabla ist ein Feld von 3 Zeigern, von denen jeder auf
> ein Feld von je 4 weiteren Zeigern zeigt, von denen jeder dann auf ein
> Feld von 4 char's zeigt.

Das ist völliger Schwachsinn, den du da verzapfst. Beschäftige dich doch 
erstmal selbst mit Grundlagen, bevor du anderen falsche Ratschläge 
gibst.

Selbst der C-Standard spricht von multidimensionalen Arrays.
Außerdem sind Arrays in C nicht dasselbe wie Zeiger. Und genau das hilft 
hier.

von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Weils im ABI für x86, im ABI von x86-64, im ABI von ARM genau definiert
> ist.

Was man an dieser Aussage sehr schön sehen kann, ist daß es eben nicht 
allgemein standardisiert ist, sondern bei jedem ABI extra definiert ist 
- und bei jedem anders sein kann.

Bernd K. schrieb:
> (zusammen mit der Forderung in allen relevanten ABIs daß nur soviel
> Padding wie nötig verwendet wird)

Hmm, ist das ABI von x86 nicht relevant? Dort ist Padding nicht nötig, 
wird aber trotzdem gemacht.

Mark Brandis schrieb:
> 3D 3D schrieb:
>> Die Realität sieht anders aus. Die Softwarequalität in der Industrie
>> steigt ständig.
>
> Davon merk ich aber nichts.

Vielleicht sollte man sagen: "Die Anforderungen an die 
Softwarequalität in der Industrie steigen ständig".

W.S. schrieb:
> Wenn du sowas wie char blabla[3][4][5] schreibst, was glaubst du
> eigentlich, was du damit tatsächlich angerichtet hast???

> Nochmal: blabla ist ein Feld von 3 Zeigern, von denen jeder auf
> ein Feld von je 4 weiteren Zeigern zeigt, von denen jeder dann auf ein
> Feld von 4 char's zeigt.

Das ist sowas von falsch! Zeiger kommen da überhaupt nicht vor! Was du 
da schreibst, sind typische Aussagen von jemandem, der Zeiger und Arrays 
(und vor allem den Unterschied zwischen den beiden) nicht verstanden 
hat.
'blabla' besteht aus 60 chars, die nach ISO C alle direkt hintereinander 
im Speicher liegen müssen.

> Wird dir jetzt was klar? Dein Glaube "Die liegen ja im Speicher als
> rgbrgBrgb...." ist ein Denkfehler der ganz groben Art.

Nein! Es ist korrekt.

Nase schrieb:
> Selbst der C-Standard spricht von multidimensionalen Arrays.

Technisch ist es aber tatsächlich nichts weiter als ein Array aus 
Arrays.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

Rolf Magnus schrieb:
> Hmm, ist das ABI von x86 nicht relevant? Dort ist Padding nicht nötig,
> wird aber trotzdem gemacht.

Wo hast Du das her?

http://refspecs.linuxfoundation.org/elf/abi386-4.pdf

Seite 28 und Seite 29, dort ist das Padding genau definiert:

386-ABI
1
Seite 28
2
3
                                              Alignment           Intel386
4
     Type              C              sizeof  (bytes)           Architecture
5
____________________________________________________________________________
6
                   char                             
7
                   signed char          1      1     signed byte
8
                 ______________________________________________________________
9
                   unsigned char        1      1     unsigned byte
10
                   short                            
11
                   signed short         2      2     signed halfword
12
                 _____________________________________________________________
13
                   unsigned short       2      2     unsigned halfword
14
Integral           int                              
15
                   signed int                       
16
                                                    
17
                   long                 4      4     signed word
18
                   signed  long                     
19
                 ______________________________________________________________
20
                   enum                             
21
                   unsigned int                     
22
                                        4      4     unsigned word
23
_______________________________________________________________________________
24
                   unsigned long                    
25
                   any-type *                       
26
Pointer            any-type ( *)( )     4      4     unsigned word
27
_______________________________________________________________________________
28
                   float                4      4     single-precision (IEEE)
29
Floating-point   ______________________________________________________________
30
                   double               8      4     double-precision (IEEE)
31
_______________________________________________________________________________
32
                   long double         12      4     extended-precision (IEEE)
1
Seite 29
2
3
[...]
4
5
Aggregates and Unions
6
7
Aggregates (structures and arrays) and unions assume 
8
the alignment of their most strictly aligned component. 
9
The size of any object, including aggregates and unions, 
10
is always a multiple of the object’s alignment. An array 
11
uses the same alignment as its elements. Structure and 
12
union objects can require padding to meet size and 
13
alignment constraints. The contents of any padding is 
14
undefined.     
15
16
       An entire structure or union object is aligned on 
17
       the same boundary as its most strictly aligned member.
18
19
       Each member is assigned to the lowest available offset 
20
       with the appropriate alignment. This may require 
21
       internal padding, depending on the previous member.
22
23
       A structure’s size is increased, if necessary, to make 
24
       it a multiple of the alignment. This may require tail 
25
       padding, depending on the last member.

-> "Each member is assigned to the lowest available offset with the 
appropriate alignment"

Oder kennst Du ein anderes x86 ABI als das obenzitierte oder wie kommst 
Du zu der abenteuerlichen Aussage "Dort ist Padding nicht nötig"? Wir 
haben jetzt im Laufe dieses Threads ARM32 und ARM64 sowie AMD64 und x86 
ABI gesehen und in jedem steht fast wortgleich das selbe drin: "Each 
member is assigned to the lowest available offset with the appropriate 
alignment".

Diese ganze Diskussion wurde losgetreten weil irgendjemand vorgeschlagen 
hat einfach blind ein packed reinzuknallen als Allheilmittel. Ich habe 
daraufhin erwiedert daß man erstens im vorliegenden Falle kein Packed 
braucht weil nur chars im Spiel sind und sonst nichts, man aber wenn man 
packed nutzen will dann soll man bitte vorher schonmal von Hand die 
Daten so anordnen daß sie auch von selbst schon aligned wären und *dann 
erst* kann man packed hinschreiben weil es dann keinen Schaden¹ mehr 
anrichten wird auf 99% aller jetzigen und 100% aller zukünftigen CPUs.

__________
¹ Schaden: Performanceeinbuße.

Ich würde jedoch statt packed lieber sowas hinschreiben, gleich am 
Anfang von main() bevor noch irgendwas schlimmeres passiert:
1
typedef struct {
2
  char r;
3
  char g;
4
  char b;
5
} foo_t;
6
7
if (sizeof(foo_t) != 3) {
8
  printf("Bitte rufen Sie +49 xxx xyy yyz an und teilen Sie uns mit wo (und wann) Sie sich befinden.");
9
}

[edit] Ja ok, im configure script wärs wahrscheinlich besser aufgehoben 
aber interessieren würds mich schon wann das passieren soll [/edit]

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Oder kennst Du ein anderes x86 ABI als das obenzitierte oder wie kommst
> Du zu der abenteuerlichen Aussage "Dort ist Padding nicht nötig"?

Nicht das ABI definiert, ob Padding nötig ist, sondern die 
Prozessorarchitektur. Das ABI definiert nur, ob es gemacht wird.

von Bernd K. (prof7bit)


Lesenswert?

Rolf Magnus schrieb:
> Das ABI definiert nur, ob es gemacht wird.

Also Komm! Jetzt ist aber mal gut. Genau darum gehts doch hier. Aus der 
Sicht des Compilers ist es genau dann nötig wenns im ABI so 
vorgeschrieben ist.

von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Rolf Magnus schrieb:
>> Das ABI definiert nur, ob es gemacht wird.
>
> Also Komm! Jetzt ist aber mal gut. Genau darum gehts doch hier. Aus der
> Sicht des Compilers ist es genau dann nötig wenns im ABI so
> vorgeschrieben ist.

Na damit drehst du dich aber jetzt im Kreis. Oben schreibst du noch, daß 
die ABIs in der Regel nur soviel Padding vorschreiben wie nötig, jetzt 
sagst du, daß es erst dadurch nötig wird, daß es im ABI vorgeschrieben 
ist.
Damit sagst du quasi aus, daß die Notwendigkeit für Padding aus der 
Notwendigkeit für Padding resultiert, und das bringt doch nix.
Die Notwendigkeit für Padding entsteht aus Einschränkungen des 
Prozessors bei Zugriffen auf den Speicher.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

Rolf Magnus schrieb:
> Oben schreibst du noch, daß die ABIs in der Regel nur soviel Padding
> vorschreiben wie nötig, jetzt sagst du, daß es erst dadurch nötig wird,
> daß es im ABI vorgeschrieben ist.

Ich schreib seit 2 Tagen das selbe. Ich weiß nicht was du da jetzt in 
meine Worte reininterpretieren bzw verdrehen willst. Wahrscheinlich hast 
du es falsch gelesen. Lies es einfach nochmal, auch die Auszüge aus den 
ABIs, aber diesmal etwas langsamer lesen, vielleicht klappts im zweiten 
Anlauf.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.