Nullelement eines struct

Gast #3627540
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.
Gast #3627547
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.
Gast #3627548
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).
Gast #3627549
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.
Gast #3627550
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.
Persönliche Seite #3627551
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).
#3627563
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.
Gast #3627569
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!
Gast #3627577
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
#3627580
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.
#3627585
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?
Gast #3627589
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.
#3627604
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 :-/
Gast #3627610
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.
Gast #3627691
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. :-)
Gast #3627775
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. :-)
Gast #3627832
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.
Persönliche Seite #3627900
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.
Gast #3628115
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.
Gast #3628378
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.
#3628461
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.

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