Leider stoße ich wieder mal an meine Verständnislücke, was die
Kombination dieser drei Themen angeht.
Ich habe:
1
structfarbe{uint8_tr;uint8_tg;uint8_tb;};
2
3
structfarbefeld[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?
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.
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?
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.
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?
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
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.
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.
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
return0;
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?
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.
ü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.
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?
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.
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
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.
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.
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
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.
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_tfarbe[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_ti=0;i<sizeof(farbe);i++){
17
foo(coloptr++);// jedes Element: Farbe pro Spalte pro Linie
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!
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_ta;// addr 0
3
uint8_tb;// addr 1 (ok weil 1-aligned)
4
// addr 2 (füllbyte)
5
// addr 3 (füllbyte)
6
uint32_tc;// addr 4 (braucht 4-aligned)
7
uint16_td;// addr 8 (ok weil 2-aligned)
8
}
Beispiel B, richtig:
1
struct{
2
uint8_ta;// addr 0
3
uint8_tb;// addr 1
4
uint16_td;// addr 2 (ok weil 2-aligned)
5
uint32_tc;// 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.
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.
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
structfoo{
2
uint16_tbar;
3
uint8_tbaz;
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.
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.
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 undARM?
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.
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.
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.
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.
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.
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!
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.
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.
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.
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.
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.
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.
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.
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.
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_ty=0;y<HEIGHT;++y)
2
{
3
for(uint8_tx=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_ti=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
* 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.
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.
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? :-/
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".
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.
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.
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.
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.
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.
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.
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
typedefstruct{
2
charr;
3
charg;
4
charb;
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]
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.
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.
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.
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.