Merkwürdiges C-Konstrukt

Gast #7210939
Lesenswert?

In einem Displaytreiber findet sich folgender C-Code:
1
typedef struct {
2
    uint8_t length; //length of data minus the command byte
3
    uint8_t data[]; //first byte is always the command byte
4
} static_transfer_t;
5
...
6
static const static_transfer_t INIT_04 = { .length = 3, {cmd_BOOSTER_SOFT_START_CONTROL, 0xD7, 0xD6, 0x9D} }; //magic values from datasheet
7
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?
#7210957
Lesenswert?

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.
Gast #7210978
Lesenswert?

> 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
Persönliche Seite #7211028
Lesenswert?

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
Gast #7211876
Lesenswert?

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.
Persönliche Seite #7212053
Lesenswert?

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
Gast #7212106
Lesenswert?

Ich nutze es auch manchmal noch, um Named Parameter & optionale 
Parameter bei Funktionen zu simulieren:
1
struct myfunction_args {
2
  int x;
3
  int bla;
4
  const char* blup;
5
};
6
#define myfunction(...) myfunction_p((struct myfunction_args){__VA_ARGS__})
7

8
int main(){
9
  myfunction(1, .blup="Hello World!")
10
}

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...)
#7212108
Lesenswert?

Μα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.
Persönliche Seite #7212211
Lesenswert?

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
#7212220
Lesenswert?

🐧 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.
Moderator #7212244
Lesenswert?

Als besonders praktisch empfinde ich die Designated Initializers für die
Initialisierung dünn besetzter Arrays:
1
int array[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.
#7212314
Lesenswert?

Yalu X. schrieb:
> Als besonders praktisch empfinde ich die Designated Initializers für die
> Initialisierung dünn besetzter Arrays:
>
>
1
> int array[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:
1
auto a = []{
2
    std::array<int, 100> data;
3
    data[ 2] = 123;
4
    data[13] = 456;
5
    data[75] = 789;
6
    return data;
7
}();
Persönliche Seite #7212334
Lesenswert?

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
#7212358
Lesenswert?

Μα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):
1
main:
2
ldi r24,0                ;
3
ldi r25,0                ;
4
ret
5
.size   main, .-main
6
.data
7
.type   a, @object
8
.size   a, 200
9
a:
10
.zero   4
11
.word   123
12
.zero   20
13
.word   456
14
.zero   122
15
.word   789
16
.zero   48
#7215684
Lesenswert?

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
struct foo
2
{     
3
    int a;
4
    int b;
5
};
6
  
7
struct foo f =
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.
Persönliche Seite #7215702
Lesenswert?

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.
#7215710
Lesenswert?

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.
#7215720
Lesenswert?

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.
#7215750
Lesenswert?

Μα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).
#7215860
Lesenswert?

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.
#7216934
Lesenswert?

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
const int a[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
const auto a = []{
2
    std::array<int, 100> data;
3
    data[ 2] = 123;
4
    data[13] = 456;
5
    data[75] = 789;
6
    return data;
7
}();

Dies ist ohne IIFE schlecht machbar (Initialisierung es ro-Objektes)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren