C(++) Deklaration, Definition

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
starcow
Establishment
Beiträge: 560
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

C(++) Deklaration, Definition

Beitrag von starcow »

Guten Tag liebe ZFX'ler :-)

Ich stelle grad etwas schockiert fest, dass ich wohl über Jahre eine falsche Vorstellung von Deklaration und Definition mit mir rum getragen hatte.
Im Buch von Heiko Kalista "C++ für Spieleprogrammierer" (2. Auflage) steht auf Seite 29. folgendes:
Geht man wie oben beschrieben vor und teilt dem Compiler mit, von welchem Typ eine Variable ist und welchen Namen sie haben soll, so hat man eine Variable deklariert. Weist man der Variable danach einen Wert zu, so hat min die Variable definiert.
Es ist wichtig, diese beiden Begriffe zu kennen und auseinander halten zu können. Sobald man eine Variable deklariert, wird automatisch der benötigte Speicher vom Compiler reserviert und unsere fisch gebackene Variable zur Verfügung stellt (natürlich erst dann, wenn das Programm kompiliert wurde).

...

Damit nun durch nicht initialisierte (definierte) Variablen keine Fehler enstehen können, gibt uns der Compiler in einem solchen Fall eine Warnung aus, die auch beachtet werden sollte.
Auch im Buch sind an dieser Stelle die beiden Wörter fett gedruckt.

Dazu ein Auszug des Listings:

Code: Alles auswählen

int main()
{
	int Lebensenergie;   // Variable deklarieren
	Lebensenergie = 100; // Variable definieren

	return 0;
}
Diese Erklärung scheint ja wohl definitiv falsch zu sein. Denn "int Lebensenergie;" ist in diesem Fall definitiv eine Deklaration UND eine Definition. Zumindest laut zwei anderen Büchern, die ich hier liegen hab.
Bevor ich mich aber zu weit aus dem Fenster lehne, würde ich die Frage gerne an euch Experten weiter geben.

Ich weiss übrigens nicht, ob sich in der neusten Auflage des Buches hinsichtlich dieser Stelle etwas geändert hat.
Das Buch hatte mir damals eigentlich sehr geholfen und ich fand, dass viele Themen gut vermittelt wurden - auch wenn das Buch nicht den Anspruch hat, in die Tiefe zu gehen.
Das Buch scheint nach wie vor beliebt zu sein und die Rezensionen auf Amazon liegen im oberen Bereich.
https://www.amazon.de/C-f-C3-BCr-Spiele ... b_image_bk

Gibt es eigentlich sowas wie eine Referenz (für C und für C++)? Eine Art "Bibel", auf die man sich im Zweifelsfall berufen kann?
Wo in allerletzter Instanz festgelegt ist, was tatsächlich gilt?

Gruss starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Alexander Kornrumpf
Moderator
Beiträge: 2138
Registriert: 25.02.2009, 13:37

Re: C(++) Deklaration, Definition

Beitrag von Alexander Kornrumpf »

starcow hat geschrieben: 18.08.2022, 12:04 Diese Erklärung scheint ja wohl definitiv falsch zu sein. Denn "int Lebensenergie;" ist in diesem Fall definitiv eine Deklaration UND eine Definition. Zumindest laut zwei anderen Büchern, die ich hier liegen hab.
Bevor ich mich aber zu weit aus dem Fenster lehne, würde ich die Frage gerne an euch Experten weiter geben.
Ich weiß die "richtige" Antwort nicht, aber ich habe eine praktische Antwort: Es spielt keine Rolle.

Bei Funktionen ist der Unterschied völlig offensichtlich: du hast die Version ohne "Rumpf", die sagt dem Compiler nur "es gibt eine Funktion mit dieser Signatur" und die Version mit "Rumpf", die sagt dem Compiler was die Funktion tut.

Bei Variablen solltest du wahrscheinlich das tun, was "Lord Delvin" im anderen Thread vorschlägt und C++ benutzen, als wäre es Java. Dann sind alle Variablen entweder lokal oder "Klassen"variablen ("Member" in C++, "Fields" in Java) und die Frage stellt sich von vornherein nicht, weil du nie in die Verlegenheit kommst, dass du eine ungebundene Variable aus einer anderen Übersetzungseinheit brauchst.

Gibt es eigentlich sowas wie eine Referenz (für C und für C++)? Eine Art "Bibel", auf die man sich im Zweifelsfall berufen kann?
Wo in allerletzter Instanz festgelegt ist, was tatsächlich gilt?
Naja, C und C++ sind standardisiert und die Compiler versuchen diese Standards zu implementieren. Aber nicht nur können Compiler Fehler enthalten, sondern die Standards sind auch nicht unbedingt in einer Prosa geschrieben, die dir helfen würde.

Was du ja eigentlich nur brauchst ist ein Verständnis, das gut genug funktioniert um damit zu arbeiten.

Disclaimer: Das letzte Mal, dass ich was mit C gemacht habe war hier: viewtopic.php?p=69693#p69693
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 597
Registriert: 05.07.2003, 11:17

Re: C(++) Deklaration, Definition

Beitrag von Lord Delvin »

Pauschal für Programmiersprachen gibt es meines Wissens keine Instanz, die das klärt. Üblich wären aber die Begriffe Deklaration, Definition und Initialisierung. Das kann je nach Konstrukt und Sprache für den Nutzer alles zusammenfallen, für den Compiler ist es aber in der Regel nicht so, selbst wenn es vom selben Token verursacht wird.

Im normalen Sprachgebrauch ist jede Definition auch eine Deklaration; Initialisierung ist etwas anderes.
Deklarationen sind üblicherweise Konstrukte, die etwas in einen Namensraum so einführen, dass man es verwenden kann. Also eine Typdeklaration oder eine Namensdeklaration.
Eine Definition führt das Konstrukt so ein, dass der Compiler versteht, wie es repräsentiert ist.
Beides hat erstmal nichts mit Ressourcen oder Initialisierung zu tun.
Das führt leider auch dazu, dass man beim Entwurf manchmal Definition verwendet, wo man Deklaration hätte verwenden dürfen; bei Namensräumen ist das z.B. nicht ganz so eindeutig, da daran keine Ressourcen hängen und der auch sonst eigentlich nicht repräsentiert ist.

Bei einer konstanten Variablen z.B. hast du eigentlich immer direkt Definition, Deklaration und Initialisierung in einem. Ob eine konstante Variable Speicher verbraucht ist dagegen heutzutage nicht mehr gesagt :)

Meinem Verständnis nach gibt es in deinem Beispiel nur Definitionen und Verwendungen und keine Initialisierung; es kann aber sein, dass die Semantik in C++ da anders ist.

Wenn die Frage tatsächlich auf C++ bezogen war, dann solltest du dir die Begriffe im Iso-Standard ansehen. Die Onlinedoku dazu scheint zumindest halbwegs zu meiner Position zu passen: https://en.cppreference.com/w/cpp/language/declarations

Für einen Compiler bedeutet eine Deklaration, einen Member in einen Namensraum einzuführen, in der Regel ein Platzhalter; eine Definition ersetzt alle solchen Platzhalter mit dem Wissen über die Repräsentation; eine Initialisierung ist üblicherweise ein Feld im Definitionsobjekt, da man bei Initialisierungen oft anders Code generiert, als bei Zuweisungen auf dieselbe Entität (bei Konstanten z.B. ist das hoffentlich direkt klar).

HTH
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
udok
Beiträge: 40
Registriert: 01.02.2022, 17:34

Re: C(++) Deklaration, Definition

Beitrag von udok »

Diese Erklärung scheint ja wohl definitiv falsch zu sein. Denn "int Lebensenergie;" ist in diesem Fall definitiv eine Deklaration UND eine Definition. Zumindest laut zwei anderen Büchern, die ich hier liegen hab.
Bevor ich mich aber zu weit aus dem Fenster lehne, würde ich die Frage gerne an euch Experten weiter geben.
Da liegst du richtig. Bei lokalen Variablen ist Deklaration immer auch Definition (= Speicherreservierung auf dem Stack).
Der Inhalt der Variable ist nicht definiert, ausser es handelt sich um eine C++ Klasse mit einem Default Constructor.

Auf der sicheren Seite bist du, wenn du "extern" vor eine Declaration schreibst, und die Variable nicht initialisierst.
Das ist dann immer eine Declaration (= keine Speicherreservierung).

Wenn die globale Variable nicht initialisiert wird und kein "exterrn" hat, dann landet sie im BSS (Common) Linker Segment, und wird mit 0 initialisiert.
Mehrere Variablen gleichen Namens werden vom Linker zu einer Variable zusammengefasst.

Ist die Variable Initialisiert, dann landet sie im Daten oder Constant Linker Segment (const), und mehrere gleiche Variablendefinitionen sind
ein Linker Fehler.

Bei C++ wird es noch mal unübersichtlicher, weil eventuell ein Constructor vor main() aufgerufen wird.
Da wird eine Funktion vom Compiler erzeugt, die in einem speziellen Segment landet (MS).
Die Funktion installiert dann den Destruktor als atexit() Funktion, der Destruktor wird nach main() aufgerufen.

Globale Variablen mit "const" sind in C wie externe Variablen, aber in C++ sind die "static" (= Sichtbar nur im aktuellen Modul).

Wobei in C++ der Linker nicht verwendete Funktionen und Variablen aus dem Program automatisch rausschmeißt, in C bleiben sie meist ohne spezielle Linkeroptionen drinnen.
Benutzeravatar
starcow
Establishment
Beiträge: 560
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: C(++) Deklaration, Definition

Beitrag von starcow »

Alles klar! Vielen Dank für die guten Hinweise und die Klärung.

Manchmal kann es schon ein wenig verunsichernd (und frustrierend) sein. Verlässt man sich doch bei einem Buch mit guten Rezensionen darauf, dass essentielle Kernaussagen auch wirklich stimmen. Ich meine, diese falsche Erklärung ist jetzt nicht die ganz grosse Katastrophe - doch ich bin kürzlich auf ein C Buch von Ulla Kirch und Peter Prinz gestossen - beide haben eigentlich schon einige Bücher über die C++ Programmierung geschrieben - und was ich dort bei den Kommentar lesen musste, war schon heftig.

Das Buch:
https://www.amazon.de/Einf%C3%BChrung-p ... op?ie=UTF8

Auszug aus dem Kommentar von "Max"
Bei konkreten Beispielcodes offenbaren sich dann weitere Unverständnisse der Autoren:
- #include <stdlib.h>
void srand(unsigned seed);
-> die in diesem Beispiel explizit erneut deklarierte Standardfunktion ist schlecht, prinzipiell sollte man nie irgendwas aus den Standardheadern 'verbessern' oder gar ändern
- fflush(stdin) wird häufig in Beispielen verwendet und explizit nochmal ausführlich erklärt, dass man so den Eingabepuffer löschen kann -> falsch, UB
- int ch;
scanf("%c",&ch);
-> falsch, Zeiger auf char erwartet, Zeiger auf int übergeben, die müssen gemäß Standard NICHT kompatibel sein -> UB
- Verwendung von "void main()" -> nicht standardkonform
- ".. unter den Betriebssystemen WINDOWS/DOS und UNIX kann ein dritter Parameter in main verwendet werden..."
int main(int argc,char**argv,char**envp)
Das ist kein C-Standard, sondern implementierungs- also compilerabhängig und NICHT BS-abhängig.
- (int*)malloc -> unnötiger Cast in C
- free(ptr) "...MUSS zuvor mit malloc/calloc/realloc reserviert worden sein..." : kein Hinweis, dass hier auch NULL möglich ist
- Verwendung von gets (zwar mit Hinweis, dass fgets besser ist, besser wäre dann aber gleich die Verwendung von fgets im Beispiel)
- while( scanf("%1[JNjn]",a)==0 )
fflush(stdin);
hier sind gleich 3 Fehler vorhanden
- fflush auf stdin ist UB
- liefert scanf hier EOF (d.h. -1), dann würde das Programm dies als gültige Eingabe interpretieren (und falsch weiterarbeiten)
- nach Ende von while ist der Einpuffer ungelöscht
- der übliche Fehler bei Callback-Funktion für das Sortieren von int mit qsort wird gemacht:
*(int*)a - *(int*)b;
was bei Werten in der Nähe von den int-Grenzen zu Fehlern führt
- fcloseall beschrieben: keine Standardfunktion
- exit(); und return aus main beschrieben ohne EXIT_SUCCESS/EXIT_FAILURE aus dem Standard zu nennen
- im Beispiel zu einer String-Ersetzungsfunktion void replace_all(char *old,char *new)
hätte sich gut eine Erklärung zum Unterschied zw.
char s[]="hello world";
und
char *s="hello world";
angeboten
- scanf("%80s", stringvar); ohne Hinweis auf den Abbruch beim 1. Whitespace dieser Variante
- NULL-Zeiger: "...ist als 0 definiert..." -> falsch, der Standard definiert NULL als Integerkonstante mit Wert 0, d.h. neben 0 wäre auch 0L oder (void*)0 denkbar; dies ist dem jeweiligen Compilerhersteller überlassen, also implementierungsabhängig und nicht etwa fest vorgegeben, wie hier beschrieben
- "Die Zeigerversion ist in der Regel der Indexversion vorzuziehen..." wenig verständlich in der heutigen Zeit der hochoptimierden Compiler ist das Compilat häufig sehr ähnlich wenn nicht gar identisch; lesbarer und einfacher ist die Arrayvariante, da kein extra Zeiger zum Iterieren definiert werden muss sondern ausschließlich das Array selbst referenziert wird
- durchgängige Verwendung von Vektor statt Array; schlechte Abgrenzung zum vector in C++, was dort etwas ganz anderes bedeutet ( die Autoren schreiben auch Bücher über C++ und hätten das erkennen müssen )
- unsinnige Empfehlung: "Vektoren mit größerem Speicherbereich sollten global definiert werden"; ohne Hinweis auf dynamischen Speicher, der einige Kapitel später vorgestellt wird; hier offenbart sich der mangelnde Überblick der Autoren über ihr eigenes Werk
- Vektorname ist identisch zur Adresse des 1.Elements -> ohne die im Standard definierten Ausnahmen zu nennen;
wenn das so wäre, wäre z.B. auch
int array[10];
array = NULL;
möglich, denn wenn array IMMER ein Zeiger wäre, würde hier kein Fehler auftreten, array wäre einfach ein Zeiger und als Lvalue möglich
- Entscheidungsoperator "?:" wird im Operator-Kapitel nicht genannt (findet man erst 2 Kapitel weiter in "Kontrollstrukturen", wo er nicht hingehört, da Operator und keine Kontrollstruktur )

Bei "Typ ganzzahliger Konstanten" ohne Suffix für Dezimalkonstanten beschreiben die Autoren nur "int, long, long long" ohne Hinweis, dass dies nur C99 relevant ist und C89 etwas anderes vorschreibt: 'int, long, unsigned long'.
Das Buch hat jetzt nicht total schlechte Bewertungen. Da fragt man sich schon, wie man als Nicht-Experte, die wirklich guten Bücher erkennen kann, auf die man sich dann auch wirklich verlassen kann.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Alexander Kornrumpf
Moderator
Beiträge: 2138
Registriert: 25.02.2009, 13:37

Re: C(++) Deklaration, Definition

Beitrag von Alexander Kornrumpf »

starcow hat geschrieben: 19.08.2022, 16:43 Das Buch hat jetzt nicht total schlechte Bewertungen. Da fragt man sich schon, wie man als Nicht-Experte, die wirklich guten Bücher erkennen kann, auf die man sich dann auch wirklich verlassen kann.
Ich habe "The Rust Book" am Stück runtergelesen und würde es auch dann empfehlen, wenn du gar nicht vor hast Rust zu lernen. Online hier und da steht auch wie man die nicht-html Version bekommt: https://doc.rust-lang.org/book/title-page.html Insbesondere kann ich aber selbst nicht genug Rust um zu beurteilen ob es ein gutes Rust Buch ist. Es ist meiner Meinung nach ein gutes Programmierbuch.

Davor habe ich ewig kein Programmierbuch mehr gelesen, gerade weil die alle das von dir genannte Problem haben. Meistens willst du ja doch eher irgendein spezifisches Problem lösen und dann googlest du halt das genaue Problem, no?

Mit "Effective C++" machst du wahrscheinlich auch heute nichts verkehrt, wenn du es noch nicht kennst, ist aber 15-20 Jahre her, dass ich das gelesen habe und da war das Buch selbst auch schon 15-20 Jahre alt. Scheiße wir werden alt hier.
Benutzeravatar
starcow
Establishment
Beiträge: 560
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: C(++) Deklaration, Definition

Beitrag von starcow »

Weil's inhaltlich gut zu meiner damaligen Frage passt und ich es euch nicht vorenthalten möchte:

Scheinbar sind sich auch gcc und clang nicht immer einig, wann's denn eine Deklaration und wann's eine Definition ist.
Für clang ist die Einführung einer Stack-Variable immer eine Definition.
Hingegen scheint es gcc davon abhängig zu machen, ob ein Initializer vorhanden ist. Ohne Initialzier ist es für gcc eine Deklaration.

Code: Alles auswählen

void foo(void)
{
	int k;
	int k;

	int q = 0;
	int q = 0;
}
gcc:
error: redeclaration of 'k' with no linkage
note: previous declaration of 'k' with type 'int'
error: redefinition of 'q'
note: previous definition of 'q' with type 'int'

clang:
error: redefinition of 'k'
note: previous definition is here
error: redefinition of 'q'
note: previous definition is here


Ich glaube noch besser lies es sich bei

Code: Alles auswählen

extern int x;
darüber streiten, ob das nun eine Deklaration oder eine Definition ist.
Man könnte ja argumentieren, dass analog zu einem struct die Details (sprich, der Typ) zu x ja nun kommuniziert sind: x ist ein int.
Es wird zwar zurzeit des Compilings kein Speicher reserviert - doch das wird es bei einer struct definition ja auch nicht.

Code: Alles auswählen

struct s; // Declaration of struct s
struct s {int x;}; // Definition of struct s
extern x; // Compile error
extern int x; // Definition or Declaration? Meinem "Empfinden" nach: Declaration
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Antworten