In einem Displaytreiber findet sich folgender C-Code:
1
typedefstruct{
2
uint8_tlength;//length of data minus the command byte
3
uint8_tdata[];//first byte is always the command byte
4
}static_transfer_t;
5
...
6
staticconststatic_transfer_tINIT_04={.length=3,{cmd_BOOSTER_SOFT_START_CONTROL,0xD7,0xD6,0x9D}};//magic values from datasheet
7
staticconststatic_transfer_tINIT_05={.length=1,{cmd_SET_GATE_TIME,0x08}};//magic value: 2us per line
Dass da eine konstante Struktur mit dem Byte length und dem Array data
initialisiert wird, verstehe ich. Aber das Konstrukt .length = ... ?
habe ich noch nie gesehen, obwohl ich seit über 20 Jahren in C
programmiere. Legt das die Länge des folgenden data[]-Feldes fest?
Das legth hat keinerlei einfluss auf die länge des array.
Du kannst lenth=1 und 10 array elemente initialisieren.
Das wird zum vereinfachten auslesen so gemacht sein.
Mike schrieb:> In einem Displaytreiber findet sich folgender C-Code:> typedef struct {> uint8_t length; //length of data minus the command byte> uint8_t data[]; //first byte is always the command byte> } static_transfer_t;> ...> static const static_transfer_t INIT_04 = { .length = 3,> {cmd_BOOSTER_SOFT_START_CONTROL, 0xD7, 0xD6, 0x9D} }; //magic values> from datasheet> static const static_transfer_t INIT_05 = { .length = 1,> {cmd_SET_GATE_TIME, 0x08} }; //magic value: 2us per line>> Dass da eine konstante Struktur mit dem Byte length und dem Array data> initialisiert wird, verstehe ich. Aber das Konstrukt .length = ... ?> habe ich noch nie gesehen, obwohl ich seit über 20 Jahren in C> programmiere. Legt das die Länge des folgenden data[]-Feldes fest?
ich würde es eher von INIT_04 und INIT_05 abhängig machen. .length sagt
nichts aus.
uint8_t data[]; entspricht 0 Bytes, wenn man das struct nun über einen
Speicherbereich castet kann man sehr wohl auf einzelne Elemente
zugreifen.
habe blödsinn geschrieben, da ich was übersehen habe.
Dirk hat recht, das geht schon lange (vor allem bei GCC, bei anderen
Compilern sieht's zum Teil aber anders aus).
> Dirk hat recht, das geht schon lange
Das mag sein. Ich programmiere auch schon laenger in C. Mein
erster C-Compiler war BDS-C unter CP/M im Kinderzimmer,
aber ich sehe das jetzt auch zum erstenmal.
Hat sich wohl nicht so recht durchgesetzt. :)
Olaf
Olaf schrieb:>> Dirk hat recht, das geht schon lange>> Das mag sein. Ich programmiere auch schon laenger in C. Mein> erster C-Compiler war BDS-C unter CP/M im Kinderzimmer,> aber ich sehe das jetzt auch zum erstenmal.>> Hat sich wohl nicht so recht durchgesetzt. :)
Weil viele das nicht kennen. Z.B. im Linuxkernel wird das überall
verwendet und erleichtert dort die wartbarkeit enorm. Auch C++ kennt
mittlerweile eine vereinfachte (und weit weniger nützliche) Form.
Matthias
DPA schrieb:> Nennt sich Designated Initializers.
Danke, mit C99 habe ich mich noch nie richtig beschäftigt. In unserer
Firma ist immer noch C90 aktuell. Die Initialisierung dürfte vor allem
bei großen Strukturen für mehr Übersichtlichkeit sorgen, wirklich nötig
ist sie natürlich nicht.
Mike schrieb:> DPA schrieb:>> Nennt sich Designated Initializers.>> Danke, mit C99 habe ich mich noch nie richtig beschäftigt. In unserer> Firma ist immer noch C90 aktuell. Die Initialisierung dürfte vor allem> bei großen Strukturen für mehr Übersichtlichkeit sorgen, wirklich nötig> ist sie natürlich nicht.
Nicht nur die Übersicht wird besser. Man kann auch problemlos neue
Strukturelement einfügen (auch in der Mitte) oder die Reihenfolge ändern
ohne das sich die Initialisierung ändert bzw. angepasst werden muss. Bei
der Reihenfolge versagt übrigens die C++ Variante
Matthias
Ist zwar nicht für alle Situationen geeignet, und ein bisschen ein Hack,
aber manchmal ist das einfach unendlich praktisch.
(PS: Es ist im Standard geregelt, dass bei Mehrfachinitialisierung die
letzte gewinnt, was für defaults auch praktisch ist. Aber wenn man das
nutzen will, muss man beim Compiler die Warnung deswegen abstellen. Und
das geht leider nicht nur für ein einzelnes Macro...)
Μαtthias W. schrieb:> Nicht nur die Übersicht wird besser. Man kann auch problemlos neue> Strukturelement einfügen (auch in der Mitte) oder die Reihenfolge ändern> ohne das sich die Initialisierung ändert bzw. angepasst werden muss. Bei> der Reihenfolge versagt übrigens die C++ Variante
Versagen würde ich das nicht nennen: es müssen die Regeln zur
(Aggregate)-Initialisierung eingehalten werden, d.h. die Elemente werden
in der Reihenfolge der Deklaration initialisiert. Leider gibt es bei der
Initialisierungsliste eines ctors dazu nur eine Warnung. Bei
designated-initializer ist es dann sinnvollerweise ein Fehler.
Wilhelm M. schrieb:> Versagen würde ich das nicht nennen: es müssen die Regeln zur> (Aggregate)-Initialisierung eingehalten werden, d.h. die Elemente werden> in der Reihenfolge der Deklaration initialisiert. Leider gibt es bei der> Initialisierungsliste eines ctors dazu nur eine Warnung. Bei> designated-initializer ist es dann sinnvollerweise ein Fehler.
Aus Sicht des C++ Experten ist es eine Einhaltung von Regeln wofür es
sicher gute Gründe gibt. Macht das Feature leider weniger praktisch als
in C. Aber immerhin gibt es das jetzt auch in C++ wenn auch mit
Einschränkungen.
Matthias
🐧 DPA 🐧 schrieb:> (PS: Es ist im Standard geregelt, dass bei Mehrfachinitialisierung die> letzte gewinnt, was für defaults auch praktisch ist. Aber wenn man das> nutzen will, muss man beim Compiler die Warnung deswegen abstellen. Und> das geht leider nicht nur für ein einzelnes Macro...)
Kannst aber
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wirgendwas"
...
#pragma GCC diagnostic pop
mit in Dein Macro aufnehmen.
Als besonders praktisch empfinde ich die Designated Initializers für die
Initialisierung dünn besetzter Arrays:
1
intarray[100]={
2
[2]=123,
3
[13]=456,
4
[75]=789
5
};
Dafür gibt es wirklich nicht viele Alternativen außer
- viele Nullen tippen oder
- eine Initialisierungsfunktion schreiben,
was aber beides nicht sehr elegant ist.
Yalu X. schrieb:> Als besonders praktisch empfinde ich die Designated Initializers für die> Initialisierung dünn besetzter Arrays:>>
1
>intarray[100]={
2
>[2]=123,
3
>[13]=456,
4
>[75]=789
5
>};
6
>
>> Dafür gibt es wirklich nicht viele Alternativen außer>> - viele Nullen tippen oder>> - eine Initialisierungsfunktion schreiben,>> was aber beides nicht sehr elegant ist.
Das ist etwas, was ich so auch nutze und ganz praktisch finde.
In C++ (ja, C++, das wurde oben von jemand anderem ja schon
angesprochen, deswegen gehe ich nochmal darauf ein, und ja: bitte die
negative Bewertung nicht vergessen) nimmt man dazu dann eben Lambdas als
IIFE:
Hi
Ich hab das mal in godbolt.org gekippt
C:
https://godbolt.org/z/37s348aae
C++:
https://godbolt.org/z/15n5MhzKK
Da sieht man schön das f in C und C++ den exakt gleichen Code produziert
trotz der Verwendung von std::array. Da macht der Compiler einen guten
Job. Die Initialisierung braucht aber etwas mehr Code. Auf der C Seite
ist das ganze in der .code Initialisierung versteckt. C++ muss es
explizit machen was wohl beim Programmstart etwas Laufzeit und auch
etwas Programmspeicher kostet. Aber garnicht schlecht was der C++
Compiler da draus macht wenn man sich mal die Komplexität des Header
<array> zu Gemüte führt.
Matthias
Μαtthias W. schrieb:> Da sieht man schön das f in C und C++ den exakt gleichen Code produziert> trotz der Verwendung von std::array. Da macht der Compiler einen guten> Job. Die Initialisierung braucht aber etwas mehr Code. Auf der C Seite> ist das ganze in der .code Initialisierung versteckt. C++ muss es> explizit machen was wohl beim Programmstart etwas Laufzeit und auch> etwas Programmspeicher kostet
Hat weniger was mit C++ als mit dem Compiler zu tun: clang ist da für
ARM und x86 besser.
avr-g++ macht es auch besser (<array> schreibt man sich gerade selbst
oder nimmt es von irgendwoher):
Mike schrieb:> Danke, mit C99 habe ich mich noch nie richtig beschäftigt. In unserer> Firma ist immer noch C90 aktuell.
Dabei ist auch C99 schon lange nicht mehr die aktuelle Version.
Wilhelm M. schrieb:> Leider gibt es bei der Initialisierungsliste eines ctors dazu nur eine> Warnung.
Ich würde sagen: Leider gibt es eine Warnung. Die nervt mich nur. Ich
weiß auch so, dass die Elemente in der Reihenfolge der Deklaration
initialisiert werden. Das in der Initialisierungsliste in einer anderen
Reihenfolge zu schreiben, ist ja erstmal kein Fehler. Warum sollte das
also mehr als eine Warnung sein? In der weit überwiegenden Zahl der
Fälle ist mir die Initialisierungsreihenfolge sowieso egal, weil die
Elemente nicht von einander abhängig sind. Da stört es nur, wenn man
dann die Fleißarbeit hat, die Reihenfolge an beiden Stellen immer
unnötigerweise synchron halten zu müssen, um diese blöde Warnung
wegzubekommen.
> Bei designated-initializer ist es dann sinnvollerweise ein Fehler.
Ob das sinnvoll ist, sei dahingestellt. Es kommt leider auch sogar bei
Strukturen, die nicht mal einen explizit definierten Konstruktor haben
oder gar rein POD sind, wo die Reihenfolge nun wirklich keine Rolle
spielt.
Dieser Code z.B. geht in C, führt in C++ aber zu einem Fehler, weil die
Reihenfolge nicht stimmt:
1
structfoo
2
{
3
inta;
4
intb;
5
};
6
7
structfoof=
8
{
9
.b=5,
10
.a=3
11
};
Aber dass die Reihenfolge nicht strikt eingehalten werden muss, ist
einer der wesentlichen Vorteile von designated initializiers.
Genau dieser Fall hat im übrigen dazu geführt, dass ich einigen Code
ändern musste, als ich ihn von C auf C++ umgestellt habe. Ich hab die
designated initializers dort viel genutzt, weil ich es einfacher finde,
mir die Namen der Elemente zu merken als deren Reihenfolge. In C++
brauche ich mit den designated initializers dann beides.
Wilhelm M. schrieb:> In C++ (ja, C++, das wurde oben von jemand anderem ja schon> angesprochen, deswegen gehe ich nochmal darauf ein, und ja: bitte die> negative Bewertung nicht vergessen) nimmt man dazu dann eben Lambdas als> IIFE:> auto a = []{> std::array<int, 100> data;> data[ 2] = 123;> data[13] = 456;> data[75] = 789;> return data;> }();
Das ist dann aber keine Initialisierung mehr, sondern eine Zuweisung
nach Default-Initialisierung.
Μαtthias W. schrieb:> Da sieht man schön das f in C und C++ den exakt gleichen Code produziert> trotz der Verwendung von std::array.
Wieso "trotz"? std::array macht ja zur Laufzeit eigentlich nichts. Ist
alles nur Template-Zeug, dass zur Compilezeit aufgelöst wird.
Rolf M. schrieb:> Μαtthias W. schrieb:>>> Da sieht man schön das f in C und C++ den exakt gleichen Code produziert>> trotz der Verwendung von std::array.>> Wieso "trotz"? std::array macht ja zur Laufzeit eigentlich nichts. Ist> alles nur Template-Zeug, dass zur Compilezeit aufgelöst wird.
Weil das ganze Templatezeug eben reichlich komplex ist und das aus
meiner Sicht für den Compiler bzw. deren Entwickler erheblich komplexer
ist das zum gleichen Code aufzulösen wie die C Variante. Aber von
Compilerbau hab ich auch wirklich keine Ahnung.
Rolf M. schrieb:> Wilhelm M. schrieb:>> In C++ (ja, C++, das wurde oben von jemand anderem ja schon>> angesprochen, deswegen gehe ich nochmal darauf ein, und ja: bitte die>> negative Bewertung nicht vergessen) nimmt man dazu dann eben Lambdas als>> IIFE:>> auto a = []{>> std::array<int, 100> data;>> data[ 2] = 123;>> data[13] = 456;>> data[75] = 789;>> return data;>> }();>> Das ist dann aber keine Initialisierung mehr, sondern eine Zuweisung> nach Default-Initialisierung.
Nein, es ist eine Initialisierung (es geht um a, nicht um data).
Schreib 'const a' und Du stellst fest, dass es eine Initialisierung ist.
Wilhelm M. schrieb:>> Das ist dann aber keine Initialisierung mehr, sondern eine Zuweisung>> nach Default-Initialisierung.>> Nein, es ist eine Initialisierung
Ok, es ist eine Copy-Initialisierung nach Default-Initialisierung und
Zuweisung.
> (es geht um a, nicht um data).
Mir geht es um den Elementtyp. Wenn das kein einfacher int ist, sondern
eine Klasse mit Konstruktor, macht es durchaus einen Unterschied.
Μαtthias W. schrieb:> Rolf M. schrieb:>> Μαtthias W. schrieb:>>>>> Da sieht man schön das f in C und C++ den exakt gleichen Code produziert>>> trotz der Verwendung von std::array.>>>> Wieso "trotz"? std::array macht ja zur Laufzeit eigentlich nichts. Ist>> alles nur Template-Zeug, dass zur Compilezeit aufgelöst wird.>> Weil das ganze Templatezeug eben reichlich komplex ist und das aus> meiner Sicht für den Compiler bzw. deren Entwickler erheblich komplexer> ist das zum gleichen Code aufzulösen wie die C Variante. Aber von> Compilerbau hab ich auch wirklich keine Ahnung.
Es ist eigentlich sogar üblich, dass Templates zu besserem (Peformance)
Code führen, weil Funktionstemplates implizit inline sind
(notwendigerweise sein müssen), der Compiler daher besser optimieren
kann. Daher werden oft keine Funktionsaufrufe generiert und damit kann
besser über Funktionsgrenzen (bei Templatefunktionen oder anderen inline
Funktionen) hinweg optimiert werden.
Natürlich kann - je nach der Anzahl der unterschiedlichen
Instanziierungen des Templates - die Code-Größe steigen. Aber auch das
ist meistens viel geringer als befürchtet (code-bloat).
Rolf M. schrieb:> Ok, es ist eine Copy-Initialisierung nach Default-Initialisierung und> Zuweisung.
In der Praxis haben wir hier copy-elision durch NRVO. Das ist zwar nicht
zwingend (so wie etwa die RVO), findet aber doch gerade bei diesem
Pattern (init with IIFE) (fast) immer statt.
Wilhelm M. schrieb:> In der Praxis haben wir hier copy-elision durch NRVO.
Das ist schon klar. Wie gesagt geht es mir aber nicht darum, wie a
initialisiert wird, sondern darum, dass die Elemente alle nicht direkt
per Initialisierung ihren Wert bekommen, sondern per Zuweisung nach
einer Default-Initialisierung. Wobei nach Eliminierung aller
Kopieraktionen das am Ende quasi auch zur Initialisierung von a wird.
Füge einfach mal bei
1
std::array<int,100>data;
ein const vor dem int ein. Dann siehst du, was ich meine.
Rolf M. schrieb:> Wilhelm M. schrieb:>> In der Praxis haben wir hier copy-elision durch NRVO.>> Das ist schon klar. Wie gesagt geht es mir aber nicht darum, wie a> initialisiert wird,
Mir aber ging es nur darum, und zwar als Ersatz für das vorher genannte:
1
constinta[100]={
2
[2]=123,
3
[13]=456,
4
[75]=789
5
};
Denn diese non-trivial designated-initializer sind nicht in C++ möglich.
Aber: es geht eben mit IIFE und ohne Mehraufwand. Zudem bietet die IIFE
in C++ mehr Möglichkeiten wie etwa non-const Initialisierer, was bei C
wieder nicht funktioniert. Make your choice ...
> Füge einfach mal bei>
1
>std::array<int,100>data;
2
>
> ein const vor dem int ein. Dann siehst du, was ich meine.
Das habe ich schon verstanden: das ist aber sinnlos.
Es geht um die Form:
1
constautoa=[]{
2
std::array<int,100>data;
3
data[2]=123;
4
data[13]=456;
5
data[75]=789;
6
returndata;
7
}();
Dies ist ohne IIFE schlecht machbar (Initialisierung es ro-Objektes)