Forum: Compiler & IDEs Nullelement eines struct


von Walter Tarpan (Gast)


Lesenswert?

Hallo zusammen,
ich frage mich gerade, wie unter C das Nullelement eines struct 
aussieht. Ich habe eine Funktion, die in etwa so aufgebaut ist:
1
typedef struct{ ...   } Objdata_t;
2
3
4
Objdata_t Objinit(Objinittype_t Init)
5
{
6
    Objdata_t obj;
7
8
    [...]
9
10
    return obj;
11
   
12
    error:
13
    return NULL; // geht das?
14
}

und ich würde in der aufrufenden Funktion gern prüfen, ob die 
Initialisierung fehlgeschlagen ist, etwa so
1
[...]
2
Objdat = Objinit(Init);
3
if(Objdat==NULL) goto error;
4
[...]

Geht das so? Laut C-Standard kann man structs ja nicht vergleichen.

Viele Grüße
W.T.

von Fred (Gast)


Lesenswert?

Probier es doch einfach mal aus.

Antwort: Nein. NULL ist kein Objdata_t. Du kannst aber auch einen Zeiger 
auf Objdata_t zurückgeben...

Und dein return obj; "funktioniert" zwar insofern, als der Compiler es 
übersetzt, es macht aber nicht das, was du sillst und ist ein grober 
Schnitzer.

von Fred (Gast)


Lesenswert?

Und man sieht dort sehr schön einen großen Vorteil von Rust 
(www.rust-lang.org): Der läßt das return obj; nämlich nicht zu, weil er 
die Lebensdauer von Variablen überprüft (und aufgrund des 
Rust-Typsystems im gegensatz zu C/C++ das auch eindeutig überprüfen 
kann).

von Peter II (Gast)


Lesenswert?

Fred schrieb:
> Und dein return obj; "funktioniert" zwar insofern, als der Compiler es
> übersetzt, es macht aber nicht das, was du sillst und ist ein grober
> Schnitzer.

warum denn nicht? Es wird dabei eine Kopie vom objekt erzeugt und diese 
zurückgegeben.


Es wird allgemein angenommen das ein objekt selber nicht NULL sein 
sollte. Auch wenn es technisch geht.

wenn du also ein objekt oder NULL zurückgeben willst. dann verwende ein 
zeiger.

von Rolf Magnus (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Hallo zusammen,
> ich frage mich gerade, wie unter C das Nullelement eines struct
> aussieht.

Was meinst du mit "das Nullelement eines Struct"? Ein struct besteht aus 
mehreren Variablen, von denen jedes je nach Typ 0 sein kann oder auch 
nicht.

> Ich habe eine Funktion, die in etwa so aufgebaut ist:
> typedef struct{ ...   } Objdata_t;
>
> Objdata_t Objinit(Objinittype_t Init)
> {
>     Objdata_t obj;
>
>     [...]
>
>     return obj;
>
>     error:
>     return NULL; // geht das?

Nein. NULL ist für Zeiger vorgesehen, um anzugeben, daß ein Zeiger keine 
gültige Objekt-Adresse enthält. Ein Struct enthält aber keine Adresse.

> }
>
> und ich würde in der aufrufenden Funktion gern prüfen, ob die
> Initialisierung fehlgeschlagen ist, etwa so[...]
> Objdat = Objinit(Init);
> if(Objdat==NULL) goto error;
> [...]
>
> Geht das so? Laut C-Standard kann man structs ja nicht vergleichen.

Wenn du deine Objekte irgendwie als "ungültig" markieren und später 
erkennen können willst, mußt du dir selber eine Methode dafür überlegen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Fred schrieb:
> Und dein return obj; "funktioniert" zwar insofern, als der Compiler es
> übersetzt, es macht aber nicht das, was du sillst und ist ein grober
> Schnitzer.

Nein. Es ist nicht sonderlich performant, aber es funktioniert im Sinne 
des Erfinders.

C kennt seit C89 Strukturzuweisungen, und damit kann eine Struktur auch 
als Rückgabewert einer Funktion verwendet werden.

Ineffizient ist es, weil bei einer Strukturzuweisung die komplette 
Struktur kopiert wird; hier wird sie gleich zweimal kopiert, einerseits 
bei der Übergabe via return (da wird die lokale Struktur auf den Stack 
kopiert (auch wenn sie als automatische Variable eh' schon auf dem Stack 
lieg) und andererseits bei der Zuweisung an "Objdat" als Rückgabewert 
der Funktion (da wird sie vom Stack wiederum in Objdat kopiert).

von Mark B. (markbrandis)


Lesenswert?

NULL ist kein sinnvoller Wert für eine Struktur. Aber für einen Zeiger 
auf eine Struktur schon.

von Udo S. (urschmitt)


Lesenswert?

Wobei das als Zeiger so aber auch nicht funktionieren würde, weil der 
Zeiger auf eine lokale Variable zeigt, die auf dem Stack liegt und bei 
Verlassen der Funktion nicht mehr gültig ist.
Dann sollte man den Zeiger auf die (ausserhalb definierte) Struktur 
besser als Parameter der Funktion übergeben und in der Funktion den 
Inhalt ggf ändern. Der Returnwert könnte dann anzeigen, daß etwas 
geändert wurde bzw. die Funktion erfolgreich war.

: Bearbeitet durch User
von Fred (Gast)


Lesenswert?

Peter II schrieb:
> warum denn nicht?

Weil ich mich mit dem Hinweis auf die Pointerrückgabe selbst verwirrt 
hatte. :-)

von Walter Tarpan (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Ineffizient ist es, weil bei einer Strukturzuweisung die komplette
> Struktur [...] gleich zweimal kopiert [wird], einerseits
> bei der Übergabe via return (da wird die lokale Struktur auf den Stack
> kopiert (auch wenn sie als automatische Variable eh' schon auf dem Stack
> lieg) und andererseits bei der Zuweisung an "Objdat" als Rückgabewert
> der Funktion (da wird sie vom Stack wiederum in Objdat kopiert).

Danke für die Info. Effizienz ist mir hier nicht so wichtig, weil Init 
über die gesamte Programmlaufzeit ohnehin nur wenige Male aufgerufen 
wird, aber es ist gut, das im Hinterkopf zu behalten.

Ich will keinen Zeiger auf ein struct haben- denn dann müßte ich das 
Struct schon vor dem Aufruf von Init übergeben. Oder eine dynamische 
Speicherverwaltung in Objinit() einbauen. Das macht keinen Spaß.

Aber dann muß ich in Objdata_t eben einen Marker einbauen, der es als 
gültig oder ungültig markiert. Geht auch.

Danke für die Diskussion!

von Udo S. (urschmitt)


Lesenswert?

Fred schrieb:
> Und man sieht dort sehr schön einen großen Vorteil von Rust

Schön, und was hilft das jetzt dem Programmierer mit seinem C Problem?
Rust ist so eine tolle Sprache, daß als erste Treffer in google 
"Europapark" erscheint.
Kommt der nächste jetzt mit Algol oder vieleicht Ruby oder Haskell, ...?

von Peter II (Gast)


Lesenswert?

Udo Schmitt schrieb:
> Wobei das als Zeiger so aber auch nicht funktionieren würde, weil der
> Zeiger auf eine lokale Variable zeigt, die auf dem Stack liegt und bei
> Verlassen der Funktion nicht mehr gültig ist.

das man dann die stuct mit new anlegen musst, sollte eigentlich klar 
sein.

von Rolf Magnus (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Fred schrieb:
>> Und dein return obj; "funktioniert" zwar insofern, als der Compiler es
>> übersetzt, es macht aber nicht das, was du sillst und ist ein grober
>> Schnitzer.
>
> Nein. Es ist nicht sonderlich performant, aber es funktioniert im Sinne
> des Erfinders.

Wenn der Compiler einigermaßen modern ist und "return value optimization 
(1)" kann, wird da gar nichts kopiert, und es ist die effizienteste Art, 
die Struktur rauszugeben.
Wenn er das nicht kann, hängt es letztendlich von der Größe der struct 
ab und davon, welche Alternative man wählt. Wenn man nämlich in der 
Funktion die struct dynamisch allokiert und später wieder freigeben muß, 
ist die Kopiererei wahrscheinlich effizienter, sofern die Struktur nicht 
allzu riesig ist.

> Ineffizient ist es, weil bei einer Strukturzuweisung die komplette
> Struktur kopiert wird; hier wird sie gleich zweimal kopiert, einerseits
> bei der Übergabe via return (da wird die lokale Struktur auf den Stack
> kopiert (auch wenn sie als automatische Variable eh' schon auf dem Stack
> lieg) und andererseits bei der Zuweisung an "Objdat" als Rückgabewert
> der Funktion (da wird sie vom Stack wiederum in Objdat kopiert).

Aber nur bei einem schlechten Compiler, denn beides kann heutzutage 
problemlos wegoptimiert werden.

(1) http://en.wikipedia.org/wiki/Return_value_optimization

von Udo S. (urschmitt)


Lesenswert?

Walter Tarpan schrieb:
> Ich will keinen Zeiger auf ein struct haben- denn dann müßte ich das
> Struct schon vor dem Aufruf von Init übergeben.

Ja und? Bei deinem Aufruf in dieser Art hast du Speicher als globale 
Variable oder lokale in main() oder wo auch immer so oder so schon 
reserviert:

Walter Tarpan schrieb:
> Objdat = Objinit(Init);

Das Objdat fällt ja nicht vom Himmel, irgendwo hast du es schon 
definiert, sonst könntest du es nicht zuweisen.

von Udo S. (urschmitt)


Lesenswert?

Rolf Magnus schrieb:
> Wenn der Compiler einigermaßen modern ist und "return value optimization
> (1)" kann, wird da gar nichts kopiert, und es ist die effizienteste Art,
> die Struktur rauszugeben.

Effizienter wäre trotzdem noch der Call by Reference, also über einen 
Pointer, oder optimiert der Compiler hier derart, daß Änderungen in der 
Funktion direkt in der Zielstruktur (die ausserhalb definiert ist) 
gemacht werden?

von Peter II (Gast)


Lesenswert?

Hier bietet sich wirklich Übergabe als Zeiger oder Refenz an.
1
obj Objdata_t;
2
3
if( !Objinit(obj) ) goto error;

von Fred (Gast)


Lesenswert?

Udo Schmitt schrieb:
> Schön, und was hilft das jetzt dem Programmierer mit seinem C Problem?

Akut nicht viel, aber das ist hier ja auch keine 1:1-Kommunikation.

> Rust ist so eine tolle Sprache, daß als erste Treffer in google
> "Europapark" erscheint.

Und bei "Go" erscheint ein Expreß-Kurier, und dann schließlich auf Platz 
zwei das Brettspiel.

Zu "C" kommt zuoberst ein Chart der Citigroup-Aktie.

Deine Bewertungsmethodik für Relevanz ist durchaus verbesserungsfähig.

Und dein Gefühl, wann du etwas zu einer Diskussion beiträgst, ebenso.

von Udo S. (urschmitt)


Lesenswert?

Peter II schrieb:
> das man dann die stuct mit new anlegen musst

Mir ist das klar, dem TO auch?
Wobei ein implizites alloc() in einer Funktion dann beim Nutzer der 
Funktion das explizite Wissen darüber erfordert daß diese Funktion das 
macht und deshalb schnell zu Memory leaks führen kann.
Das ist eigentlich eher schlechter Stil, wobei man da natürlich immer 
das richtige Augenmass braucht und es Ausnahmen gibt.

Fred schrieb:
> Deine Bewertungsmethodik für Relevanz ist durchaus verbesserungsfähig.

Gut, dann zeige die vielen tausend Stück Software die damit schon 
erfolgreich geschrieben und verkauft wurden, oder die Firmen, die die 
Sprache einsetzen, oder die offenen Stellen am Arbeitsmarkt, die diese 
Programmiersprache fordern.
Im Ernst, diese Verweise auf Programmiersprachenexoten hilft wahrlich 
nicht weiter. Eine weitere Diskussion darüber aber auch nicht :-/

: Bearbeitet durch User
von Fred (Gast)


Lesenswert?

Udo Schmitt schrieb:
> Gut, dann zeige die vielen tausend Stück Software die damit schon
> erfolgreich geschrieben und verkauft wurden, oder die Firmen, die die
> Sprache einsetzen, oder die offenen Stellen am Arbeitsmarkt, die diese
> Programmiersprache fordern.

Ich wiederhole mich:
>> Deine Bewertungsmethodik für Relevanz ist durchaus verbesserungsfähig.

von Udo S. (urschmitt)


Lesenswert?

Fred schrieb:
> Ich wiederhole mich:
>>> Deine Bewertungsmethodik für Relevanz ist durchaus verbesserungsfähig.

Rofl,
deine ist wohl: "ich mag das also ist es relevant" :-)

von Walter Tarpan (Gast)


Lesenswert?

Peter II schrieb:
> Hier bietet sich wirklich Übergabe als Zeiger oder Refenz an.
> obj Objdata_t;
>
> if( !Objinit(obj) ) goto error;

Objinit mit Rückgabewert für SUCCESS und FAIL ausstatten. Ist auch eine 
Überlegung wert.

von Daniel A. (daniel-a)


Lesenswert?

Walter Tarpan schrieb:
> Laut C-Standard kann man structs ja nicht vergleichen.

Doch, mit
1
memcmp(&obj1,&obj2,sizeof(obj_t))==0

: Bearbeitet durch User
von Fred (Gast)


Lesenswert?

Udo Schmitt schrieb:
> deine ist wohl: "ich mag das also ist es relevant" :-)

Nein, meine ist "es wird vermutlich einige Leute interessieren".

Es gibt hier durchaus Leser, die nicht alles in industrieller Verwendung 
messen. Angeblich sogar Hobbyisten. Gerüchtehalber gar Leute, die sogar 
Basic einsetzen. :-)

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

Fred schrieb:
> Gerüchtehalber gar Leute, die sogar
> Basic einsetzen. :-)

oder, noch schlimmer: GOTO in C-Programmen! ;-)

von Udo S. (urschmitt)


Lesenswert?

Fred schrieb:
> Nein, meine ist "es wird vermutlich einige Leute interessieren".

Ok, das lass ich gerne auch gelten. Ich habe wohl deinen Post dazu 
missverstanden. Klang für mich nach "Rust ist vel besser, also wechsle 
dazu" und war wohl eher gemeint als "Übrigens da gibts Rust, kann man 
sich bei Interesse mal anschauen".
:-)

von Udo S. (urschmitt)


Lesenswert?

Wegstaben Verbuchsler schrieb:
> GOTO in C-Programmen!

Gab sogar Bücher die das propagiert haben, als Fehlerabbruch.
Ist allemal besser als 20 mal if (!error) { ... }
bei Steuerungsfunktionen mit mehreren sequentiellen Aufrufen von 
Subfunktionen.

: Bearbeitet durch User
von Fred (Gast)


Lesenswert?

Udo Schmitt schrieb:
> Ok, das lass ich gerne auch gelten. Ich habe wohl deinen Post dazu
> missverstanden. Klang für mich nach "Rust ist vel besser, also wechsle
> dazu" und war wohl eher gemeint als "Übrigens da gibts Rust, kann man
> sich bei Interesse mal anschauen".
> :-)

Genau so. Schön, dann ist ja alles gut. :-)

von ben (Gast)


Lesenswert?

zu "Rust" als Programmiersprache gibt es nicht mal einen 
deutschsprachigen Wikipedia Eintrag. Das sollte genug drüber aussagen, 
wie relevant diese Sprache ist.

Professionel uninteressant, und für Hobbyisten wohl auch. Denn als 
Hobbyist ist ein wichtiges Bewertungskriterium: wieviele Infos finde ich 
darüber im Netz?

Ist ja schön und gut dass es für jedes Problem ein eigenes Werkzeug gibt 
und man nicht immer die C oder C++ Brechstange rausholen muss. Aber 
solche Insellösungen wie Rust sind dann auch nicht erstrebenswert.

Sorry, mir war fad.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Daniel A. schrieb:
>> Laut C-Standard kann man structs ja nicht vergleichen.
>
> Doch, mitmemcmp(&obj1,&obj2,sizeof(obj_t))==0

Das aber liefert ... unerwartete Ergebnisse, wenn das Alignment ins 
Spiel kommt.

Sind nämlich die Strukturelemente nicht an Byte-, sondern an 16- oder 
32-Bit-Adressen ausgerichtet, was eben auf 16- oder 32-Bit-Architekturen 
üblich ist, dann enthält die Struktur Lücken, in denen Zufallswerte 
stehen können.

Und die sorgen dafür, daß so ein Vergleich in die Hose geht.

von Udo S. (urschmitt)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Und die sorgen dafür, daß so ein Vergleich in die Hose geht.

Ausser man hat die Strukturen vor der Benutzung sauber mit memset 
genullt.

von Walter T. (nicolas)


Lesenswert?

Wegstaben Verbuchsler schrieb:
> oder, noch schlimmer: GOTO in C-Programmen! ;-)

Ich bin immer offen dafür, neues zu lernen. Insbesondere über 
Fehlerbehandlung/robuste Implementierung. Kennst Du eine solide Quelle? 
Dann muß ich nicht alle Fehler immer selbst machen.

von Rolf Magnus (Gast)


Lesenswert?

Udo Schmitt schrieb:
> Rolf Magnus schrieb:
>> Wenn der Compiler einigermaßen modern ist und "return value optimization
>> (1)" kann, wird da gar nichts kopiert, und es ist die effizienteste Art,
>> die Struktur rauszugeben.
>
> Effizienter wäre trotzdem noch der Call by Reference, also über einen
> Pointer, oder optimiert der Compiler hier derart, daß Änderungen in der
> Funktion direkt in der Zielstruktur (die ausserhalb definiert ist)
> gemacht werden?

Ja, genau das ist ja die Idee hinter dieser Optimierung.

von W.S. (Gast)


Lesenswert?

Wegstaben Verbuchsler schrieb:
> oder, noch schlimmer: GOTO in C-Programmen! ;-)

läßt sich steigern: int anstelle int16_t oder char anstelle uint8_t und 
so weiter. Das geradezu teuflische an goto ist, daß es fest im 
Grundwortschatz von C enthalten ist ;-)

Die Skale der erreichbaren Unleserlichkeit bei C ist wie die 
Richterskale: nach oben offen..

W.S.

von B. S. (bestucki)


Lesenswert?

W.S. schrieb:
> läßt sich steigern: int anstelle int16_t oder char anstelle uint8_t und
> so weiter.

Naja, die (u)intXX_t Typen sind auch nicht gerade toll, da stdint.h 
diese nicht bereitstellen muss und auf einigen Architekturen teilweise 
nicht vorhanden sind. Besser ist da die Verwendung der (u)int_fastXX_t 
und (u)int_leastXX_t Typen, die stdint.h zwingend bereitstellen muss. 
Nutzt man printf oder ähnliches, sollte man natürlich auch die 
Formatierungszeichen aus inttypes.h verwenden.

Die (u)intXX_t Typen verwende ich nur in hardwareabhängigen Funktionen, 
die direkt auf Hardware zugreifen.

von Rolf Magnus (Gast)


Lesenswert?

So ist das ja auch gedacht. Exakte größen braucht man außer für 
Memory-Mapped-Register nur zur Kommunikation nach außen (Binäre Files & 
Übertragungsprotokolle).

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.