Forum: Mikrocontroller und Digitale Elektronik Union als Funktionsargument in C


von Simon B. (zmon)


Lesenswert?

Hallo Zusammen,

ich möchte folgende Funktion:
1
unsigned char disk_read(unsigned long int LBA, unsigned char *buf){
2
}

so abändern, dass im weiteren Verlauf der Funktion LBA Teil einer Union 
ist:
1
union d_word {
2
    unsigned long int LBA;
3
    struct {
4
        unsigned char byte0;
5
        unsigned char byte1;
6
        unsigned char byte2;
7
        unsigned char byte3;
8
    };
9
};

Ist das direkt in der Funktionsdefinition möglich oder ist es sinnvoller 
die Definition so zu belassen und innerhalb der Funktion die Union zu 
definieren und dann den Wert hinein zu kopieren?

Danke im Voraus und schöne Grüße!

von A. S. (Gast)


Lesenswert?

Definition so lassen.

Eine Union als Parameter ist meist suboptimal.

von Jobst Q. (joquis)


Lesenswert?

Simon B. schrieb:
> Ist das direkt in der Funktionsdefinition möglich oder ist es sinnvoller
> die Definition so zu belassen und innerhalb der Funktion die Union zu
> definieren und dann den Wert hinein zu kopieren?

Nimm einen void Pointer als Parameter, damit kannst du per Casting 
beliebig auf die Daten zugreifen.

Zu kopieren brauchst du dann nichts.

von Simon B. (zmon)


Lesenswert?

Hallo nochmal,

Ich hab das momentan wie von achs vorgeschlagen am Laufen; es 
funktioniert, scheint mir allerdings vom Rechenaufwand für den µC 
aufwändig. Macht aber momentan nichts, ist ein Hobby-Projekt. @achs: Ich 
bin schon beruhigt, dass nicht nur ich das so sehe.

@joquis: Danke für die Stichworte, das dürfte so ungefähr das sein was 
ich suche ;) Hier habe ich für die Zukunft einen Anhaltspunkt für meine 
Weiterbildung.

Weitere Vorschläge sind natürlich willkommen, ich selbst mach für heute 
Schluss, sonst wird's nix mit dem erholsamen Schlaf.

Schönen Abend euch allen!

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Simon B. schrieb:
> scheint mir allerdings vom Rechenaufwand für den µC aufwändig.
Wie kommst du auf diesen Eindruck? Was ist da so langsam?

von Markus F. (mfro)


Lesenswert?

A. S. schrieb:
> Definition so lassen.
>
> Eine Union als Parameter ist meist suboptimal.

Bezüglich was?

Ob man eine Union mit der Größe und dem Alignment eines long int oder 
ein long int an eine Funktion übergibt, ist einigermaßen Wurscht.

Ich jedenfalls schreibe üblicherweise, was ich meine und finde das 
optimal ;)

von Simon B. (zmon)


Lesenswert?

Lothar M. schrieb:
> Simon B. schrieb:
>> scheint mir allerdings vom Rechenaufwand für den µC aufwändig.
> Wie kommst du auf diesen Eindruck? Was ist da so langsam?

Naja, ich übergebe eine "große" Zahl an eine Funktion, die dann erst 
einen neuen Speicherplatz dafür schafft und noch vier kleine dazugibt 
mit denen ich dann weiterarbeiten kann. Und das jedes Mal wenn ich diese 
Funktion aufrufe, was oft sein wird.

Mein Eindruck von Aufwändig entsteht dadurch, dass ich das Gefühl habe 
einen Umweg zu programmieren. Ich weiß nicht im Detail wie der Compiler 
das dann interpretiert bzw. optimiert, vermutlich macht er eh was 
schlankes draus. Dass ich das aber nicht genau weiß macht mich irgendwie 
nervös. Ist ein Persönlichkeitsding - ich hab die Dinge gern unter 
Kontrolle, von da her wäre Assembler für mich perfekt, ich möchte aber 
etwas mehr C-Praxis bekommen weshalb ich mein Unwohlsein bei der Sache 
wohl akzeptieren muss.

Wenn das Programm dann mal läuft und ich Timing-Messungen daran machen 
kann, werden sich vermutlich meine Bedenken in Luft auflösen. Bis dahin 
werde ich betreffend dieses Themas etwas Unruhe verspüren ;)

So, nun aber gute Nacht - Danke euch allen!

von Markus F. (mfro)


Lesenswert?

Simon B. schrieb:
> Naja, ich übergebe eine "große" Zahl an eine Funktion, die dann erst
> einen neuen Speicherplatz dafür schafft und noch vier kleine dazugibt
> mit denen ich dann weiterarbeiten kann.

Du weißt schon, was eine Union von einem Struct unterscheidet?

von A. S. (Gast)


Lesenswert?

Markus F. schrieb:
> Bezüglich was?

Eine Union ist in C relativ eingeschränkt. Ich kann ein Datum nur in der 
Form rauslesen, wie ich's reingeschrieben habe.

Entweder der Up möchte immer ein Long übergeben, dann macht die Union 
das ganze komplizierter als nötig.

Oder der Up möchte Mal ein Long, Mal ein struckt übergeben, dann lieber 
ein void-ptr. Oder 2 Funktionen.

Eine Union nur dann, wenn diese Selbstzweck ist und nicht ihre Elemente.

Es ist wie ein goto, manchmal sinnvoll, aber meist schlechtes Design.


Eine Union als Parameter eigentlich nur als Teil eines structs, dessen 
Inhalt u.a. beschreibt, welches Element gemeint ist (z.b. Telegramme)

von A. S. (Gast)


Lesenswert?

Kümmer dich um Performance erst, wenn es (zu langsam) läuft.und dann mit 
realem Cod hier. So wie es ausschaut, willst  Du vielleicht 
serialisieren und brauchst die Union vielleicht gar nicht. Zum lernen 
ist es aber hinderlich vor jedem Schritt zu schauen, ob er auch perfekt 
ist. Geh mit den Konstrukte, die Du kennst, bist Du sie so gut kennst, 
dass Du (oder wir) sie verbessern können.

von Carl D. (jcw2)


Lesenswert?

Oder vielleicht einfach so, wenn man nur innerhalb der Funktion die 
einzelnen Bytes verarbeiten will.
1
unsigned char disk_read(unsigned long int LBA, unsigned char *buf){
2
  unsigned char* lba_bytes = (unsigned char*)&LBA;
3
4
  lba_bytes[0] = 0;
5
6
}
PS: ein ordentlicher Compiler verlangt dafür keinen Aufpreis.

von Dr. Sommer (Gast)


Lesenswert?

Eine union zu Verwenden um ein long in Bytes zu konvertieren o.ä. ist in 
C Implementation defined (d.h. unportabel) und in C++ ganz verboten. 
unions sind zum Speicher sparen gemacht, nicht zur Daten Konvertierung. 
Es ist nicht ganz klar was jetzt bei dir der Fall ist. Mehr im Artikel 
Serialisierung. Die genannte Möglichkeit Pointer zu casten ist 
ebenfalls problematisch - nur in bestimmten Sonderfällen erlaubt, aber 
auch dann unportabel. Möglicherweise ist es besser, mit bitweisen 
Operationen zu arbeiten. Diese können auch gut vom Compiler optimiert 
werden. Gerade wenn man C lernen will, sollte man sich direkt angewöhnen 
wohldefinierten portablen Code zu schreiben. Optimierungen für 
Sonderfälle kann man immer noch machen wenn es erwiesenermaßen (!) zu 
langsam ist.

von jemand (Gast)


Lesenswert?

A. S. schrieb:
> Definition so lassen.
>
> Eine Union als Parameter ist meist suboptimal.

Und am dritten Tag erschuf Gott den Pointer, um Funktionen beliebigen 
Kram übergeben zu können.

von Rolf M. (rmagnus)


Lesenswert?

Simon B. schrieb:
> Ist das direkt in der Funktionsdefinition möglich oder ist es sinnvoller
> die Definition so zu belassen und innerhalb der Funktion die Union zu
> definieren und dann den Wert hinein zu kopieren?

Es ist sinnvoller, das mit der Union gleich ganz bleiben zu lassen.

Simon B. schrieb:
> Naja, ich übergebe eine "große" Zahl an eine Funktion, die dann erst
> einen neuen Speicherplatz dafür schafft und noch vier kleine dazugibt
> mit denen ich dann weiterarbeiten kann. Und das jedes Mal wenn ich diese
> Funktion aufrufe, was oft sein wird.

Das ist das, was Donald E. Knuth mit der Aussage "premature optimization 
is the root of all evil" meinte. Es ist sehr wahrscheinlich, dass der 
Compiler die Daten in Register kopiert, was er sowieso machen müsste, um 
sie zu verarbeiten. Also Overhead: 0.
Wenn man mit der Programmierung anfängt, weiß man solche Details 
natürlich nicht, sollte sich aber auch nicht darauf versteifen. Schreib 
erstmal einfach dein Programm. Wenn es dann zu langsam sein sollte, 
finde heraus, wo der Flaschenhals ist, dann optimiere gezielt diese 
Stelle.

> Dass ich das aber nicht genau weiß macht mich irgendwie nervös. Ist ein
> Persönlichkeitsding

Das geht vielen so, ist also ganz normal.

> Wenn das Programm dann mal läuft und ich Timing-Messungen daran machen
> kann, werden sich vermutlich meine Bedenken in Luft auflösen. Bis dahin
> werde ich betreffend dieses Themas etwas Unruhe verspüren ;)

So wird es sein.

jemand schrieb:
> A. S. schrieb:
>> Definition so lassen.
>>
>> Eine Union als Parameter ist meist suboptimal.
>
> Und am dritten Tag erschuf Gott den Pointer, um Funktionen beliebigen
> Kram übergeben zu können.

Den Pointer hat er gleich am Anfang erschaffen. Aber void gab's erst am 
dritten Tag. ;-)

von Simon B. (zmon)


Lesenswert?

Guten Morgen alle zusammen und danke für eure Rückmeldungen

A. S. schrieb:
> So wie es ausschaut, willst  Du vielleicht
> serialisieren
Dr. Sommer schrieb:
> Serialisierung
Genau das ist es was ich will, auf diesen Begriff wäre ich nie gekommen. 
Danke für den Link, er wird mir weiterhelfen.

Markus F. schrieb:
> Du weißt schon, was eine Union von einem Struct unterscheidet?
Ja. Definitiv.

A. S. schrieb:
> Entweder der Up möchte immer ein Long übergeben [...]
Ja, so ist es. ich möchte einen Wert mit 32 Bit übergeben (LBA-Adresse) 
und möchte diese in der Funktion per SPI ausgeben.


SPI-mäßig sehe ich zwei Möglichkeiten:

1: Startbyte ausgeben, Schnittstelle umstellen auf Übertragung von 4 
bytes, LBA-Adresse als Long ausgeben, wieder rückstellen auf Byteweise 
übertragung, Vorgang mittels Abschluss-Byte beenden.

2: Die LBA in einzelne Bytes aufteilen und einzeln schicken.

Ich habe mich für Variante 2 entschieden, weshalb ich also 
"serialisieren" muss. Ich werde mich heute Abend mit dem Artikel 
beschäftigen, aber jetzt ran an die Arbeit und schöne Grüße!

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Rolf M. schrieb:
> Den Pointer hat er gleich am Anfang erschaffen. Aber void gab's erst am
> dritten Tag. ;-)

Ich bin bestimmt nicht bibelfest, aber ziemlich sicher, daß das Nichts 
ganz am Anfang war ;)

von Rolf M. (rmagnus)


Lesenswert?

Markus F. schrieb:
> Ich bin bestimmt nicht bibelfest, aber ziemlich sicher, daß das Nichts
> ganz am Anfang war ;)

Du meinst, er hat damit angefangen, nichts zu schaffen? Ich dachte, der 
letzte Tag sei der Ruhetag gewesen, nicht der erste. ;-)

von Jobst Q. (joquis)


Lesenswert?

Am Anfang war das Wort.

Ob das aber als String (char*) oder als 16-Bit Wert (unsigned short) 
gemeint war, ist unklar.

von A. S. (Gast)


Lesenswert?

jemand schrieb:
> Und am dritten Tag erschuf Gott den Pointer, um Funktionen beliebigen
> Kram übergeben zu können.

Der T.O. hat es unten noch mal bestätigt, aber hat schon zu Anfang 
geschrieben, dass er genau NICHT beliebige Sachen übergeben will. Ein 
Pointer wäre damit genauso unsinnig wie die Union oder sonst eine 
Verschleierungstaktik.

Wenn jemand immer ein Long übergeben will, dann ist ein Long in der 
Schnittstelle auch das einzig sinnvolle.

von jemand (Gast)


Lesenswert?

A. S. schrieb:
> geschrieben, dass er genau NICHT beliebige Sachen übergeben will. Ein
> Pointer wäre damit genauso unsinnig wie die Union oder sonst eine
> Verschleierungstaktik.

Pointer sind Verschleierungstaktik?

Beispiel:
1
typedef union{
2
  //whatnot
3
}zeigstl;
4
5
void machwas(zeigstl *ptr){
6
//blabla
7
}

Da wird nichts verschleiert. Da ist genauestens definiert, was die 
Funktion zu erwarten hat. Von dem Umstand mal abgesehen, dass Unions 
ihre eigenen Probleme haben.
Nur kann man mit dem Prinzip Pointer beliebige Dinge übergeben.

von Bernd K. (prof7bit)


Lesenswert?

A. S. schrieb:
> Markus F. schrieb:
>> Bezüglich was?
>
> Eine Union ist in C relativ eingeschränkt. Ich kann ein Datum nur in der
> Form rauslesen, wie ich's reingeschrieben habe.

Da hab ich auch schon anderweitiges gelesen, zum Beispiel daß es seit 
C99 offiziell erlaubt ist.

Beitrag #5630988 wurde von einem Moderator gelöscht.
von Simon B. (zmon)


Lesenswert?

Mahzeit nochmal

A. S. schrieb im Beitrag #5630988:
> Wenn die Aufgabenstellung eine andere ist, z.B. dass die Union eh schon
> beim Aufrufer vorliegt,

Darauf möchte ich noch kurz eingehen trotz der Befürchtung dass ich 
damit vielleicht einigen Wirbel in die Diskussion bringe: Das Long das 
ich an die Funktion übergeben möchte ist im Main bereits Teil einer 
Union. Trotzdem möchte ich die Funktion auch z.B. mit
1
res=disk_read(0x11200, &sector)

aufrufen können, was, wie ich denke, mit einer Pointervariante nicht 
funktionieren wird.

Ich werde mir demnächst den Artikel zum serialisieren zu Gemüte führen 
und die Übergabe eines Pointers auf das Union in der Main auch 
ausprobieren. Dazu finde ich sicher Info im weltweiten Gewebe.

von A. S. (Gast)


Lesenswert?

jemand schrieb:
> Da wird nichts verschleiert. Da ist genauestens definiert, was die
> Funktion zu erwarten hat. Von dem Umstand mal abgesehen, dass Unions
> ihre eigenen Probleme haben.
> Nur kann man mit dem Prinzip Pointer beliebige Dinge übergeben.

Der UP will immer ein Long übergeben. Alles andere als ein Long ist 
daher suboptimal sprich: Verschleierung. Egal, ob es möglich, definiert 
oder sonstwofür gut ist.

von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> A. S. schrieb:
>> Markus F. schrieb:
>>> Bezüglich was?
>>
>> Eine Union ist in C relativ eingeschränkt. Ich kann ein Datum nur in der
>> Form rauslesen, wie ich's reingeschrieben habe.
>
> Da hab ich auch schon anderweitiges gelesen, zum Beispiel daß es seit
> C99 offiziell erlaubt ist.

Das ist bis auf eine Ausnahme falsch. C99 sagt:

"The value of at most one of the members can be stored in a union object 
at any time."

Die Union kann immer nur eines seiner Members enthalten. Daraus folgt, 
dass ich auch das lesen darf, das gerade enthalten ist.
Die Ausnahme:

"One special guarantee is made in order to simplify the use of unions: 
if a union contains several structures that share a common initial 
sequence (see below), and if the union object currently contains one of 
these structures, it is permitted to inspect the common initial part of 
any of them anywhere that a declaration of the complete type of the 
union is visible."

Wenn ich also in einer Union zwei Strukturen habe, die gleich anfangen, 
dann darf ich über eine der Strukturen den gemeinsamen Teil (und nur 
den) lesen, auch wenn eigentlich die andere enthalten ist. Das ist die 
einzige Situation, in der man ein anderes Element lesen darf als das, 
das zuletzt reingeschrieben wurde.

von x^2 (Gast)


Lesenswert?

Carl D. schrieb:
> Oder vielleicht einfach so, wenn man nur innerhalb der Funktion
> die
> einzelnen Bytes verarbeiten will.unsigned char disk_read(unsigned long
> int LBA, unsigned char *buf){
>   unsigned char* lba_bytes = (unsigned char*)&LBA;
>
>   lba_bytes[0] = 0;
>
> }PS: ein ordentlicher Compiler verlangt dafür keinen Aufpreis.

Ein derartiger Zugriff per Pointer ist nicht zu empfehlen. Aus mehreren 
Gründen:

a) Der Code ist abhängig von der Endianness
b) Man zwingt den Parameter oft auf den Stack
c) Auf einigen exotischen Plattformen mit Wortadressierung führt es 
sogar zu undefiniertem Verhalten. Die implizierte Annahme, dass je 
Adresse ein Byte vorliegt ist dort nämlich falsch. Ein 32-bit Wort 
spannt meist über nur 2 Adressen, ein Array aus 4 Bytes aber über 4 
Addressen. Folgendes wird dabei zum Knallfrosch:
1
void f(uint32_t x)
2
{
3
   volatile uint8_t* y = (volatile uint8_t*)&x;
4
   y[0] = 1;
5
   y[1] = 2;
6
   y[3] = 3;
7
   y[4] = 4;
8
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

x^2 schrieb:
> y[0] = 1;
>    y[1] = 2;
>    y[3] = 3;
>    y[4] = 4;

[2]?

von x^2 (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> x^2 schrieb:
>> y[0] = 1;
>>    y[1] = 2;
>>    y[3] = 3;
>>    y[4] = 4;
>
> [2]?

Ja, Tippfehler, Danke! 0,1,2,3

von Rolf M. (rmagnus)


Lesenswert?

x^2 schrieb:
> c) Auf einigen exotischen Plattformen mit Wortadressierung führt es
> sogar zu undefiniertem Verhalten.

Dann ist aber der Compiler kaputt, denn C schreibt vor, dass man jedes 
beliebige Objekt als Array aus char interpretieren können muss.

> Die implizierte Annahme, dass je Adresse ein Byte vorliegt ist dort
> nämlich falsch.

Das ist aber die Definition eines Bytes - die kleinste adressierbare 
Einheit. Übrigens: Ein Byte muss in C nicht 8 Bit breit sein.

von Markus F. (mfro)


Lesenswert?

Rolf M. schrieb:
> Das ist bis auf eine Ausnahme falsch. C99 sagt:
>
> "The value of at most one of the members can be stored in a union object
> at any time."

In C11 ist das Lesen eines "anderen" union members plötzlich wieder 
erlaubt - type punning über Unions ist explizit erwähnt. Wahrscheinlich 
hat ein Commitee Member festgestellt, daß sie das selber benutzen.

Im gcc war es (als Erweiterung) meiner Kenntnis nach immer erlaubt.

von Bernd K. (prof7bit)


Lesenswert?

Rolf M. schrieb:
> Bernd K. schrieb:
>> A. S. schrieb:
>>> Markus F. schrieb:
>>>> Bezüglich was?
>>>
>>> Eine Union ist in C relativ eingeschränkt. Ich kann ein Datum nur in der
>>> Form rauslesen, wie ich's reingeschrieben habe.
>>
>> Da hab ich auch schon anderweitiges gelesen, zum Beispiel daß es seit
>> C99 offiziell erlaubt ist.
>
> Das ist bis auf eine Ausnahme falsch. C99 sagt:
>
> "The value of at most one of the members can be stored in a union object
> at any time."

Das ist ja wohl auch logisch da sie rein physikalisch nur Platz hat für 
einen (den größten) member, das sagt aber absolut null aus zu obiger 
Fragestellung. Hast also die falsche Stelle zitiert bzw. falsch 
verstanden.

Ich zitiere jetzt auch mal aus dem Standard:

"If the member used to access the contents of a union object is not the 
same as the member last used to store a value in the object, the 
appropriate part of the object representation of the value is 
reinterpreted as an object representation in the new type as described 
in 6.2.6 (a process sometimes called "type punning"). This might be a 
trap representation." - 6.5.2.3, fn 82 (Seite 73 unten)

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
>> Das ist bis auf eine Ausnahme falsch. C99 sagt:
>>
>> "The value of at most one of the members can be stored in a union object
>> at any time."
>
> Das ist ja wohl auch logisch da sie rein physikalisch nur Platz hat für
> einen (den größten) member, das sagt aber absolut null aus zu obiger
> Fragestellung.

Finde ich schon. Eine union ist wie eine Struct, nur dass eben zu jedem 
Zeitpunkt nur eines der Elemente darin gespeichert sein kann. Es ist 
dann eigentlich selbstverständlich, dass man dann auch nur das Element 
lesen darf, das gerade gespeichert ist und kein anderes.

> Hast also die falsche Stelle zitiert bzw. falsch verstanden.
>
> Ich zitiere jetzt auch mal aus dem Standard:

Sicher, dass das C99 ist? In meinem C99 finde ich einen solchen Satz 
nämlich nicht.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

Rolf M. schrieb:
> Eine union ist wie eine Struct

Nein.

— A structure type describes a sequentially allocated nonempty set of 
member objects (and, in certain circumstances, an incomplete array), 
each of which has an optionally specified name and possibly distinct 
type.

— A union type describes an overlapping nonempty set of member 
objects, each of which has an optionally specified name and possibly 
distinct type.

von Bernd K. (prof7bit)


Lesenswert?

Rolf M. schrieb:
> meinem C99 finde ich einen solchen Satz
> nämlich nicht.

Deiner ist zu alt.


Defect Report #283
http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm

ISO/IEC 9899:1999
Technical Corrigendum 3
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1235.pdf

von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Rolf M. schrieb:
>> Eine union ist wie eine Struct
>
> Nein.

Toll abgeschnitten…

Bernd K. schrieb:
> Rolf M. schrieb:
>> meinem C99 finde ich einen solchen Satz
>> nämlich nicht.
>
> Deiner ist zu alt.

Ok, dann wurde das nachträglich noch angepasst. Ich hatte in der 
ursprünglichen Version ohne Korrekturen nachgeschaut.

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.