Forum: Compiler & IDEs Wie verarbeitet der GCC die Speicherklasse extern?


von Daniel S. (pallas)


Lesenswert?

Hallo zusammen,
ich versuche gerade mir die Programmiersprache C von Grundauf 
anzueignen.
Leider komme ich immer wieder an Punkte an denen ich mir nicht wirklich 
erklären kann was ich da gerade programmiere.
So mag mein Code zwar richtig sein und führt zum korrekten Ergebnis.
Trotzdem ist es für mich unbefriedigend, weil mir nicht klar ist WIESO 
mein Code richtig ist.
Ein gutes Beispiel ist die Speicherklasse extern. So weiß ich, dass 
extern nur für globale Variablen gilt und nur in der Header-Datei 
benutzt wird.
Auch der Unterschied zwischen einer Deklaration und einer Definition ist 
mir bewusst. Leider weiß ich nicht - natürlich nur bei Variablen - wie 
der Compiler jetzt die Deklaration und die Definition voneinander 
unterscheidet.
Das ist wichtig für mich um zu verstehen wie extern dann auch 
letztendlich wirksam wird.
Ein Beispiel:
-> es gibt drei Dateien: main.c, erste_datei.h, erste_datei.c. Die 
Header-Datei wird via #include in die anderen beiden Dateien 
eingebunden.
Die Speicherklasse extern bewirkt, dass eine globale Variable auch 
wirklich nur einmal definiert wird. Der Rest ist dann für den Compiler 
eine Deklaration derselben Variable. Selbst wenn der Programmierer das 
selber nicht so gedacht hatte.
Das ganze funktioniert beim GCC nämlich auch ohne das "extern" vor der 
Deklaration.
Hier sind die erwähnten Dateien:
1
/*main.c*/
2
3
#include <stdio.h>
4
#include "erste_datei.h"
5
6
int ersteVariable = 13; /*<- 1*/
7
int zweiteVariable;
8
9
int main(int argc, char* argv[])
10
{
11
  zweiteVariable = initialisiereVariable(zweiteVariable);
12
  printf("Die zweite Variable ist %d.\n", zweiteVariable);
13
  printf("Die erste Variable ist %d.\n", ersteVariable);
14
  return 0;
15
}
1
/*erste_Datei.h*/
2
3
#ifndef ERSTE_DATEI_H
4
#define ERSTE_DATEI_H
5
6
extern int ersteVariable; /*funktioniert auch prima ohne "extern"*/
7
8
extern void hello();
9
extern int initialisiereVariable(int);
10
11
#endif
1
/*erste_datei.c*/
2
3
#include "erste_datei.h"
4
#include <stdio.h>
5
6
int ersteVariable; /*Fehler bei Initialisierung an dieser Stelle*/ /*<- 2*/
7
8
void hello()
9
{
10
  printf("Hello World!\n");
11
}
12
13
int  initialisiereVariable(int param)
14
{
15
  param = 100212;
16
  return param;
17
}

Ich frage mich also nach welchen Kriterien der Compiler nun entscheidet, 
welche Definition (1 oder 2) er nun auch wirklich als Definition zulässt 
und welche er in eine Deklaration umwandelt.
Diese Frage bezieht sich sowohl auf die Definition mit Initialisierung 
und jene ohne eine Initialisierung.
Wiese dürfen nicht beide Definitionen eine Initialisierung enthalten?
Ich glaube die Antwort hängt auch damit zusammen in welcher Reihenfolge 
der Kompiler die Quelltexte bearbeitet.
Da ich darüber aber nur über bruchstückhaftes Wissen verfüge hilft mir 
das auch nur bedingt und mit etwaigen Hilfestellungen weiter.
Kann mir eventuell jemand hier im Forum weiterhelfen?

Mit freundlichen Grüßen
Pallas

von M.K. B. (mkbit)


Lesenswert?

Wenn du es genauer verstehen willst, dann solltest du dich in den Ablauf 
im Bildprozess einarbeiten. Also was macht der Compiler mit welchen 
Dateien und was macht der Linker.

Der Unterschied ist die Deklaration und Definition. Mit extern 
deklariert du eine Variable und sagst dem Compiler, dass es später eine 
Variable mit dem Namen und Typ geben wird. In einer C Datei muss dann 
die Variable definiert werden, damit diese beim Linken vorhanden ist. Es 
gilt außerdem, dass jede Variable nur einmal definiert werden darf. Wenn 
du natürlich nur eine C Datei hast, dann fällt das nicht auf, ob extern 
richtig gesetzt ist.

von Sven P. (Gast)


Lesenswert?

Daniel S. schrieb:
> Ich frage mich also nach welchen Kriterien der Compiler nun entscheidet,
> welche Definition (1 oder 2) er nun auch wirklich als Definition zulässt
> und welche er in eine Deklaration umwandelt.
Er entscheidet das garnicht, denn diese Entscheidung hast du ihm ja 
gerade abgenommen: Mit extern ist es eine Deklaration, ohne ist es 
eine Definition.

Dem Compiler ist das überdies auch völlig egal, denn er verteilt ja 
keinen Speicher. Das macht nämlich der Linker: Im Prinzip reserviert er 
für jede Definition (also ohne extern) ein Plätzchen im Speicher und 
vergibt diesem Plätzchen den Variablennamen. Und für jede Deklaration 
(mit /extern) schaut er nach, ob er irgendwo ein passendes Plätzchen mit 
dem Variablennamen hat.

Dabei gibts zwei Fälle: Jemand hat eine Deklaration verwendet und er 
findet kein Plätzchen. Das gibt diese Linker-Fehler "unresolved symbol" 
oder "undefined symbol" oder so ähnlich. Oder aber er möchte ein neues 
Plätzchen mit einem Namen reservieren, aber es gibt schon ein Plätzchen 
mit dem Namen.

Das, was du jetzt gerade beobachtest, nennt sich *tentative 
declaration*. Das ist quasi eine Definition ohne Initialisierung.
Solange eine Definition keine Initialisierung hat (also eine tentative 
declaration ist), kannst du die beliebig oft hinschreiben. Der Linker 
behandelt sie dann so, als ob sie mit extern vereinbart wurde. 
Andernfalls darf es genau eine Initialisierung geben, wie du ja 
beobachtet hast, die dann auch verwendet wird.

Ist m.M.n. etwas blöd in C (und wurde in C++ zu einem Fehler geändert). 
Auf globaler (Datei-)Ebene sollte man alle Variablen static machen...

von Daniel S. (pallas)


Lesenswert?

In den Kompiliervorgang möchte und werde ich mich noch tiefer 
einarbeiten.
Mich verwirrt nur, dass auch dieser Quellcode problemlos funktioniert:
1
/*erste_Datei.h*/
2
3
#ifndef ERSTE_DATEI_H
4
#define ERSTE_DATEI_H
5
6
int ersteVariable; /*funktioniert auch prima ohne "extern"*/
7
8
extern void hello();
9
extern int initialisiereVariable(int);
10
11
#endif
Wieso ist das so?
Kann doch eigentlich gar nicht sein?
Und wenn es sich dabei um eine reine Deklaration handelt. Was passiert 
dann, wenn ich den Header in eine Datei einbinde, die noch gar keine 
globale Variable gleichen Typ und Namens definiert hat?
Das kann ja auch vorkommen.
Also mir hat man das so erklärt, dass die Deklaration nur sagt, in einer 
Datei, die eben diesen Header inkludiert wird eine solche Variable 
definiert. Deswegen muss die Variable ja auch unbedingt in der 
erste_datei.c definiert werden. Es reicht nicht aus sie einfach zu 
benutzen.
Im obigen Beispiel könnte man schließlich anführen, es würde doch 
reichen dem Compiler mitzuteilen das es die Variable gibt.
Da es aber, wie beschrieben, auch vorkommen kann das der Header in eine 
Datei eingebunden wird, welche die abesagte Variable nie definiert hat, 
muss sie unbedingt in der erste_datei.c definiert werden. So ist 
sichergestellt, dass wenigstens eine Definition vorliegt.
So weit glaube ich also verstanden zu haben.
Ich verstehe also was der Unterschied zwischen dem deklarieren und dem 
definieren ist und wieso wenigstens in der erste_datei.c eine Definition 
erfolgen muss.
Was ist nicht verstehe ist folgendes:
-> Wieso funktioniert es in der erste_datei.h auch OHNE "extern"?
Hängt der Compiler das vielleicht standardmäßig davor? Er weiß ja, dass 
es in .h-Dateien sowieso nur Deklarationen gibt.
-> Wieso darf ich nicht in allen Dateien bei der Definition eine 
Initialisierung ausführen?
Ich vermute das der Compiler an eben dieser Initialisierung festmacht, 
aus welcher Datei die Varaiblendefinition nun auch wirklich als 
Definition ausgeführt wird.
Besteht als in Datei A eine Definition MIT Initialisierung und in Datei 
B eine OHNE Initialisierung, wird die Definition in Datei B gestrichen.
Aber das ist nur eine Vermutung.
-> Wie verfährt der Compiler bei der Wahl der Definition, wenn es gar 
keine Initialisierung gibt?
Meine Vermutung ist, dass er die Datei welche vor dem Linken entsteht 
von oben nach unten durchsucht und die erste Definition nimmt. Alle 
anderen streicht er.

Lieben Gruß
Pallas

von Markus F. (mfro)


Lesenswert?

... um zur weiteren Verwirrung beizutragen (bzw. ein tieferes 
Verständnis zu bekommen, was da eigentlich passiert), solltest Du dir 
auch mal die Compiler-Optionen -fcommon bzw. -fno-common anschauen.

von Daniel S. (pallas)


Lesenswert?

Sven P. schrieb:
> Daniel S. schrieb:
>> Ich frage mich also nach welchen Kriterien der Compiler nun entscheidet,
>> welche Definition (1 oder 2) er nun auch wirklich als Definition zulässt
>> und welche er in eine Deklaration umwandelt.
> Er entscheidet das garnicht, denn diese Entscheidung hast du ihm ja
> gerade abgenommen: Mit extern ist es eine Deklaration, ohne ist es
> eine Definition.

Das stimmt schon. Und für mich bedeutet das, dass die Deklaration in 
erste_datei. eben auch wirklich eine Deklaration ist. Wofür braucht der 
Compiler dann die Definition in erste_datei.c. Oder ist das gar keine 
Definition weil in der Header-Datei das "extern" steht.
Meine Frage also: steht die Deklaration in der Header-Datei oder steht 
dort nur der, ich sag jetzt mal "Hinweis" auf die Existenz einer 
Deklaration in der zugehörigen .c-Datei.
Eben so wie bei Funktionen.
Ich dachte eben dieser "Hinweis" IST eine Deklaration.

Sven P. schrieb:
> Jemand hat eine Deklaration verwendet und er
> findet kein Plätzchen. Das gibt diese Linker-Fehler "unresolved symbol"
> oder "undefined symbol" oder so ähnlich.

In meinem Beispiel ist das offensichtlich nicht ganz zutreffend. Selbst 
wenn ich die Definition in der main.c weglasse, läuft der Compiler 
problemlos durch.

von Sven P. (Gast)


Lesenswert?

Daniel S. schrieb:
> Und wenn es sich dabei um eine reine Deklaration handelt. Was passiert
> dann, wenn ich den Header in eine Datei einbinde, die noch gar keine
> globale Variable gleichen Typ und Namens definiert hat?
Garnichts. Der Compiler baut eine Variable mit dem Variablennamen ein 
und überlässt es dem Linker, anstelle des Variablennamens ein Plätzchen 
im Speicher einzusetzen.

Das kann man sogar recht wörtlich so verstehen: Der Compiler erzeugt ein 
unaufgelöstes Symbol in seiner Ausgabe (also im Maschinencode, wenn 
man so will). Der Linker muss dieses Symbol dann auflösen, indem er es 
durch eine konkrete Speicheradresse ersetzt.
Darum heißt er ja auch Linker (oder früher: Binder). Weil er solche 
unaufgelösten Symbole aus mehreren Stücken Maschinencode (quasi pro 
.c-Datei ein Stück) aufsammelt und zusammenbindet.

> Das kann ja auch vorkommen.
Das ist der Normalfall.

> Also mir hat man das so erklärt, dass die Deklaration nur sagt, in einer
> Datei, die eben diesen Header inkludiert wird eine solche Variable
> definiert. Deswegen muss die Variable ja auch unbedingt in der
> erste_datei.c definiert werden. Es reicht nicht aus sie einfach zu
> benutzen.
Korrekt. Mit der Definition erzeugst du ein echtes Symbol, mit dem der 
Linker die ganzen unaufgelösten Symbole verknoten kann.

> Im obigen Beispiel könnte man schließlich anführen, es würde doch
> reichen dem Compiler mitzuteilen das es die Variable gibt.
Genau das tust du ja mit der /extern/-Deklaration.

> Da es aber, wie beschrieben, auch vorkommen kann das der Header in eine
> Datei eingebunden wird, welche die abesagte Variable nie definiert hat,
> muss sie unbedingt in der erste_datei.c definiert werden. So ist
> sichergestellt, dass wenigstens eine Definition vorliegt.
Ja. Und in der Regel will man, dass es genau eine Definition gibt. 
Darum sind diese tentative Deklarationen eigentlich blöd.
Man kann das -fno-common einschalten, was Markus vorschlägt. Dann wird 
das Verhalten etwas intuitiver.

> Wieso funktioniert es in der erste_datei.h auch OHNE "extern"?
> Hängt der Compiler das vielleicht standardmäßig davor? Er weiß ja, dass
> es in .h-Dateien sowieso nur Deklarationen gibt.
Nein, weiß er nicht. Genaugenommen ist ihm die Dateiendung (.h) auch 
ziemlich egal. Er sieht davon ja auch nix mehr, denn noch bevor der 
Compiler deinen Quelltext sieht, wurden alle #include-Zeilen einfach 
durch den Inhalt der entsprechenden Dateien ersetzt (vom Präprozessor).

> -> Wieso darf ich nicht in allen Dateien bei der Definition eine
> Initialisierung ausführen?
Naja, weil es sich beißt. Welche soll denn letztlich zählen?

> Ich vermute das der Compiler an eben dieser Initialisierung festmacht,
> aus welcher Datei die Varaiblendefinition nun auch wirklich als
> Definition ausgeführt wird.
Ja, solange alle anderen Definitionen tentativ sind. Durch die 
Initialisierung wird die quasi festgenagelt und es darf ansonsten keine 
weitere mit Initialisierung mehr geben. Wie gesagt, macht man 
normalerweise nicht mehr.

Daniel S. schrieb:
> -> Wie verfährt der Compiler bei der Wahl der Definition, wenn es gar
> keine Initialisierung gibt?
Laut C-Standard werden die mit 0 initialisiert.

> Meine Vermutung ist, dass er die Datei welche vor dem Linken entsteht
> von oben nach unten durchsucht und die erste Definition nimmt. Alle
> anderen streicht er.
Ja. Darum heißen sie auch tentativ (englisch: vorläufig). Solange die 
alle gleich lauten, macht der Linker nachher ein einziges Plätzchen im 
Speicher draus. Wenn eine davon eine Initialisierung hat, dann zählt die 
und weitere mit Initialisierung führen zum Fehler. Wenn sie alle extern 
sind, gibt es kein Symbol und auch einen Fehler. Wenn eine static ist, 
dann sieht der Linker sie garnicht.

Daniel S. schrieb:
> Sven P. schrieb:
>> Jemand hat eine Deklaration verwendet und er
>> findet kein Plätzchen. Das gibt diese Linker-Fehler "unresolved symbol"
>> oder "undefined symbol" oder so ähnlich.
>
> In meinem Beispiel ist das offensichtlich nicht ganz zutreffend. Selbst
> wenn ich die Definition in der main.c weglasse, läuft der Compiler
> problemlos durch.
Ja klar, dann zieht halt die Definition in erste_datei.c.

von Daniel S. (pallas)


Lesenswert?

Wow, das ist ja grausam für einen Anfänger. :-)
Es gibt also sogar noch eine Art Zwischenstufe zwischen Definition und 
Deklaration. -> vorläufige Deklarationen

Jetzt muss ich mich mal konzentrieren.
Im Beispiel welche ich im ersten Post beschrieben habe ist also die 
Variable ersteVariable aus der Datei main.c die Definition.
Und zwar aus mehreren Gründen:
-> einmal wegen dem "extern" in erste_datei.h. Dieses markiert die 
Definition in erste_datei.c erst einmal als Deklaration?
Oder markiert es die entsprechende Zeile in der Header-Datei selbst als 
Deklaration?
-> und selbst ohne das "extern" im Header handelt es sich in 
erste_datei.h um eine vorläufige Definition. Diese wird so oder so von 
der Definition mit Initialisierung überschrieben.

von Sven P. (Gast)


Lesenswert?

Daniel S. schrieb:
> Es gibt also sogar noch eine Art Zwischenstufe zwischen Definition und
> Deklaration. -> vorläufige Deklarationen
Ja, aber vergiss die am besten wieder, die ist von historischer 
Bedeutung.

Darum verweise ich ja auf das -fno-common-Flag: Damit entfällt diese 
Zwischenstufe und es werden echte Definitionen draus, mit entsprechendem 
Fehler, wenn sie mehrfach auftauchen.

> Jetzt muss ich mich mal konzentrieren.
> Im Beispiel welche ich im ersten Post beschrieben habe ist also die
> Variable ersteVariable aus der Datei main.c die Definition.
Es ist erstmal eine Definition von möglicherweise vielen.
Aber es ist eine mit Initialisierung.

> Und zwar aus mehreren Gründen:
> -> einmal wegen dem "extern" in erste_datei.h. Dieses markiert die
> Definition in erste_datei.c erst einmal als Deklaration?
> Oder markiert es die entsprechende Zeile in der Header-Datei selbst als
> Deklaration?
Nur diese Zeile in der Header-Datei markiert sie als Deklaration, 
denn nur dort steht das extern.

> -> und selbst ohne das "extern" im Header handelt es sich in
> erste_datei.h um eine vorläufige Definition. Diese wird so oder so von
> der Definition mit Initialisierung überschrieben.
Nur ohne das extern handelt es sich darum.


Ich würds so zusammenfassen, wobei ich das mit der vorläufigen 
Definition erstmal unterschlage:
- Es ist völlig egal, was im Header und was im Source steht. Der 
Compiler sieht eh nix von dieser Unterscheidung.
- Wenn irgendwo eine Variable mit extern steht, dann hat sie externe 
Speicherklasse, ist also eine Deklaration.
- Wenn irgendwo eine Variable ohne extern steht, dann ist das eine 
Definition.

Deklarationen kann man beliebig kopieren. Das macht man ja etwa durch 
das #include vom Header. Dadurch landet die Deklaration (mit extern) in 
jedem Durchlauf. Und man kann die damit deklarierte Variable verwenden, 
wodurch ein unaufgelöstes Symbol entsteht.

Eine Deklarierte Variable muss irgendwo einmalig definiert werden, 
damit sie auch im Speicher platziert wird. Typischerweise in einer 
Source-Datei. Es ist egal, ob die Variable dabei initialisiert wird oder 
nicht. Dadurch entsteht ein echtes Symbol.

Bim Linken werden die unaufgelösten Symbole dann mit dem einen echten 
Symbol verknotet.

Bis hierhin ist jetzt alles wasserdicht, denke ich. In der main.c wird 
also ein unaufgelöstes Symbol erzeugt (durch das extern im Header) und 
gleich darauf eine passende Definition. In der anderen Source wird nur 
das unaufgelöste Symbol erzeugt.



Das mit der vorläufigen Definition heißt "common section". Dabei erzeugt 
der Compiler möglicherweise mehrere gleichnamige echte Symbole, weil 
es ja an mehreren Stellen (tentative) Definitionen gibt. Der Linker 
sammelt diese dann auf und verstaut sie einmalig an dieselbe 
Speicherstelle.
Das ist nicht ganz risikolos, denn möglicherweise definiert irgendeine 
andere Source-Datei eine Variable von der du garnichts weißt, und die 
genauso heißt wie eine von dir. Dann fasst der Linker die zusammen und 
du wunderst dich...

von Daniel S. (pallas)


Lesenswert?

Sven P. schrieb:
>> Und zwar aus mehreren Gründen:
>> -> einmal wegen dem "extern" in erste_datei.h. Dieses markiert die
>> Definition in erste_datei.c erst einmal als Deklaration?
>> Oder markiert es die entsprechende Zeile in der Header-Datei selbst als
>> Deklaration?
> Nur diese Zeile in der Header-Datei markiert sie als Deklaration,
> denn nur dort steht das extern.

Ich glaube ich verstehe nicht ganz was die Zeile mit dem "extern" in der 
Header-Datei darstellen soll.
Auf alle drei Dateien bezogen:
Wie viele Definitionen / Deklarationen gibt es?
-> gibt es drei verteilt auf alle drei Dateien
-> oder gibt es nur zwei, verteilt auf die Dateien mit der Endung .c

Letzten Endes möchte ich also wissen ob die Einträge in der Header-datei 
"nur" Verweise darstellen oder ob sie selbst Definitionen / 
Deklarationen sind.

von W.S. (Gast)


Lesenswert?

Daniel S. schrieb:
> Ein gutes Beispiel ist die Speicherklasse extern. So weiß ich, dass
> extern nur für globale Variablen gilt und nur in der Header-Datei
> benutzt wird.

Speicherklasse? Nö, sowas gibt's hier nicht. Das Wort 'extern' zeigt dem 
Compiler lediglich an, daß dasjenige, was dahinter kommt, eben nicht 
in der gerade vorliegenden Quelle vorhanden ist, sondern irgendwo anders 
und darum kümmert sich dann nicht der Compiler, sondern der Linker.

Das ist alles.

Bei externen Variablen muß man das extern in der .h davorschreiben, da 
diese ja auch in andere Quellen inkludiert wird.

Und man kann in der .h das extern auch vor Funktionen schreiben - der 
Einheitlichkeit zuliebe, aber man braucht das in diesem Falle 
ausnahmsweise mal nicht.

W.S.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Daniel S. schrieb:
> Die Speicherklasse extern

Die Speicherklasse nennt sich "Static Storage".

Üblicherweise wird extern verwendet, um die Linkage von Objekten im 
Static Storage festzulegen.

In C werden Objekte per Identifier referenziert, und der 
Gültigkeitsbereich eines Identifiers ist maximal eine Compilation 
Unit.  Um ein Objekt von einer anderen Compilation Unit aus zu 
referenzieren als von der, die es definiert, gibt es in C External 
Linkage.  Diese dient quasi dazu, Identifier über Compilation Units 
hinweg so zu "verbinden", dass sie sich auf das gleiche Objekt (immer 
Speicherklasse Static Storage) beziehen.

Um das ganze verwirrender zu machen, kann man External Linkage auch ohne 
"extern" erreichen, und "extern" kann sich auf eine tentative 
Declaration beziehen:
1
static int x; // Tentative Decl (Internal Linkage)
2
extern int x; // Bezieht sich auf "static int x".
3
static int x = 2; // Definition mit Internal Linkage.
aber:
1
extern int x; // Decl x (External Linkage).
2
static int x; // Error.
oder
1
static int x; // Tentative Decl
2
extern int x; // Bezieht sich auf "static int x".
3
// End of File => Tentative Decl wird zue Definition "static int x = 0".
oder
1
int x; // Tentative Decl, External Linkage
2
// End of File => Tentative Decl wird zur Definition "int x = 0".

Um die Sache weiter zu verkomplizieren, kann man (Legacy) extern 
tentative Decls in mehreren Modulen haben, die dann in einer Common 
Section (.comm) "überlagert" werden — zumindest wenn der Compiler das 
implementiert, was zum Beispiel bei GCC der Fall ist (-fcommon ist immer 
noch Default).  Wenn man das nicht möchte, compiliert man mit 
-fno-common, und erst dann meckert der Linker "multiple definition of" 
an.  Definitionen (nicht tentative) kommen in eine andere Section (bei 
elf-Targets üblicherweise .bss, .data oder .rodata), die dieses common 
Feature nicht haben, und wenn mehrfach versucht wird, das gleiche Symbol 
in so eine Section zu lokatieren, meckert der Linker (auch mit 
-fcommon).

Ok, das waren die Stolperfallen und Seltsamkeiten die man üblicherweise 
nicht braucht. Stattdessen hat man i.d.R folgende 3 Anwendungsfälle:

1) External Linkage: Verwendung in mehreren Compilation Units:
1
extern int x; // Decl im Header
2
int x = 2; // Definition in *einem* Modul

2) Internal Linkage: Verwendung in einer Compilation Unit:
1
static int x = 2; // Decl im Modul

3) No Linkage (und Static Storage) Verwendung in einer Funktion:
1
void func (void)
2
{
3
    static int x = 2; // Gehört zu func.
4
}
5
6
void proc (void)
7
{
8
    static int x; // Gehört zu proc.
9
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Johann L. schrieb:
> [...] um die Linkage von Objekten im Static Storage festzulegen.

Soll natürlich "Linkage von Identifiern" heißen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Daniel S. schrieb:
> Ich glaube ich verstehe nicht ganz was die Zeile mit dem "extern" in der
> Header-Datei darstellen soll.
> Auf alle drei Dateien bezogen:
> Wie viele Definitionen / Deklarationen gibt es?
> -> gibt es drei verteilt auf alle drei Dateien
> -> oder gibt es nur zwei, verteilt auf die Dateien mit der Endung .c

main.c:6
1
int ersteVariable = 13; /*<- 1*/

Das ist eine Definition mit Initialisierung (offiziell "external
definition" genannt). Sie sorgt u.a. dafür, dass für die Variable
Speicher reserviert wird. So eine "external definition" darf für einen
bestimmten Variablennamen im gesamten Programm einschließlich der
hinzugelinkten Module und Bibliotheken nur ein einziges Mal auftauchen,
sonst wird eine Fehlermeldung ausgegeben. Da Header-Files i.Allg. in
mehreren Übersetzungseinheiten genutzt werden, findet man dort i.Allg.
keine Definitionen dieser Art.

erste_Datei.h:6
1
extern int ersteVariable; /*funktioniert auch prima ohne "extern"*/

Das ist eine reine Deklaration (ich schreibe hier "reine", weil auch
Definitionen Deklarationen sind). Sie teilt dem Compiler lediglich mit,
dass irgendwo eine Variable dieses Namens und dieses Typs existiert,
veranlasst aber keine Speicherreservierung. Sie darf im gesamten
Programm  beliebig oft auftauchen, weswegen man sie oft in Header-Files
findet.

erste_Datei.c:6
1
int ersteVariable; /*Fehler bei Initialisierung an dieser Stelle*/ /*<- 2*/

Das ist eine Definition ohne Initialisierung (offiziell "tentative
definition" genannt). Sie darf innerhalb einer Übersetzungseinheit
beliebig oft auftauchen, verweist dabei aber immer auf dasselbe Objekt
und damit denselben Speicherort. Enthalten andere Übersetzungseinheiten
ebenfalls Definitionen für denselben Namen, hängt ihre Behandlung von
den Compiler-Optionen ab:

Default bzw. mit -fcommon: sämtliche im gesamten Programm auftretenden
"tentative definitions" und ggf. die (höchstens einmal vorhandene)
"external definition" werden zu einem einzigen Objekt zusammengefasst.
Existiert keine "external definition", wird dennoch Speicher reserviert
und dieser mit 0 initialisiert.

Mit -fno-common: Das im vorigen Abschnitt (Default) beschriebene
Vorgehen wird nicht auf das gesamte Programm, sondern auf jede einzelne
Übersetzungseinheit angewandt. Stellt sich am Ende heraus, dass die
Variable in mehreren Übersetzungseinheiten definiert wurde, wird eine
entsprechende Fehlermeldung ausgegeben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Daniel S. schrieb:
> Ich frage mich also nach welchen Kriterien der Compiler nun entscheidet,
> welche Definition (1 oder 2) er nun auch wirklich als Definition zulässt

Beide :-) #1 Führt zur Definition eines globalen Symbols in .data (siehe 
oben was ich zu tentative Decl schrieb), und #2 ergibt ein globales 
Symbol in .comm (mit -fcommon, GCC-Default).  Der Linker legt dies 
jedoch nicht nach COMMON, da er zusätzlich eine Definition in .data 
vorfindet.  Die genauen Regeln und historischen Absonderlichkeiten kenn 
ich auch nicht, aber in 99.99% der Fälle fährt man besser mit 
-fno-common:  Wie oben erklärt landet dann #2 in .bss und der Linker 
meckert wie erhofft "multiple definition of 'x'".

> und welche er in eine Deklaration umwandelt.

Keine.  Aber #2 wird am Ende der Compilation Unit von einer tentative 
Decl zu einer Definition (mit external Linkage).

von Yalu X. (yalu) (Moderator)


Lesenswert?

Vielleicht noch ein Paar Worte zur Historie des "extern"-Schlüsselworts,
die dieses Durcheinander mit und ohne "extern" etwas nachvollziehbarer
macht:

Der erste C-Compiler von Ritchie lief auf einer PDP-11 unter Unix,
dessen Linker in der Lage war, mehrere Definitionen einer globalen
Variable zusammenzufassen, so wie dies auch heute unter Unix noch der
Fall ist. Das "extern" diente lediglich dazu, innerhalb einer Funktion
eine Variable als global zu deklarieren. Mit "extern" war also nicht
"außerhalb der Übersetzungseinheit", sondern "außerhalb der Funktion"
gemeint. In Deklarationen außerhalb von Funktionen (ISO: "external
declarations") brauchte man "extern" nicht, und es wurde einfach
ignoriert, wenn man es trotzdem hinschrieb. Da auch "extern" innerhalb
von Funktionen nur sehr selten benötigt werden, wurde es deswegen so gut
wie überhaupt nicht verwendet. Auch heute noch wird es zumindest bei der
Deklaration von Funktionen immer noch weggelassen. So war das eigentlich
eine runde und in sich konsistente Sache.

C wurde aber auch auf andere Plattformen portiert, wo der Linker dieses
Zusammenfassen gleichnamiger Variablen nicht beherrschte. Hier musste
eine globale Variable explizit einer einzigen Übersetzungseinheit
zugeordnet werden, in allen anderen durfte keine Variable desselben
Namens angelegt werden. Trotzdem musste natürlich auch in diesen die
Variable irgenwie deklariert werden. Also entschied Ritchie, diese
Deklaration mit dem an dieser Stelle sonst redundanten "extern" zu
kennzeichnen.

Zitat aus dem "C Reference Manual" von Dennis M. Ritchie:

1
In other operating systems, however, the compiler must know in just
2
which file the storage for the identifier is allocated, and in which
3
file the identifier is merely being referred to. In the implementations
4
of C for such systems, the appearance of the extern keyword before an
5
external definition indicates that storage for the identifiers being
6
declared will be allocated in another file. Thus in a multi-file
7
program, an external data definition without the extern specifier must
8
appear in exactly one of the files. Any other files which wish to give
9
an external definition for the identifier must include the extern in the
10
definition. The identifier can be initialized only in the file where
11
storage is allocated.
12
13
In PDP-11 C none of this nonsense is necessary and the extern specifier
14
is ignored in external definitions.

Die heutigen unixoiden Linker sind natürlich mindestens genauso schlau
wie der Linker auf der PDP-11, so dass es eigentlich keinen Grund gibt,
das "extern" in globalen Deklarationen zu erzwingen. Aus Rücksicht auf
zurückgebliebene Linker unter anderen Betriebssystemen (ich glaube, der
Microsoft-Linker gehört(e) auch dazu), hat es sich aber eingebürgert,
das "extern" genauso zu verwenden, wie in obigem Zitat beschrieben, auch
wenn das "Nonsense" ist :)

von Sven P. (Gast)


Lesenswert?

Johann L. schrieb:
> Um das ganze verwirrender zu machen, kann man External Linkage auch ohne
> "extern" erreichen, und "extern" kann sich auf eine tentative
> Declaration beziehen:static int x; // Tentative Decl (Internal Linkage)
> extern int x; // Bezieht sich auf "static int x".
> static int x = 2; // Definition mit Internal Linkage.
Das Symbol x ist aber außerhalb der Übersetzungseinheit nicht 
erreichbar, denn es ist ja static:
1
haku:~/temp/ld $ nm main.o 
2
0000000000000000 D ersteVariable
3
                 U initialisiereVariable
4
0000000000000000 T main
5
                 U printf
6
0000000000000004 d x
7
0000000000000004 C zweiteVariable


W.S. schrieb:
> Bei externen Variablen muß man das extern in der .h davorschreiben, da
> diese ja auch in andere Quellen inkludiert wird.
Muss man ja gerade nicht, wegen der tentativen Deklarationen. Genau 
darum gehts ja gerade.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sven P. schrieb:
> Johann L. schrieb:
>> static int x; // Tentative Decl (Internal Linkage)
>> extern int x; // Bezieht sich auf "static int x".
>> static int x = 2; // Definition mit Internal Linkage.
> Das Symbol x ist aber außerhalb der Übersetzungseinheit nicht
> erreichbar,

Das braucht es auch nicht, mein Code ist in einer Compilation Unit 
(ansonsteen hätte ich das natürlich dazu geschrieben).  Frag mich aber 
nicht, wozu das gut ist (wenn man wirklich sich nochmal auf x beziehen 
will — warum auch immer — dann ginge das ja auch mit "static int x;" 
statt dem "extern int x;".

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Zu tentative Definitionen hatte Johann hier mal eine sehr ausfuehrliche 
Antwort gegeben:
Beitrag "Re: GCC: Welches (globale) Symbol nimmt der Compiler bei gleichem Namen und Typ?"

von Sven P. (Gast)


Lesenswert?

Johann L. schrieb:
> Das braucht es auch nicht, mein Code ist in einer Compilation Unit
> (ansonsteen hätte ich das natürlich dazu geschrieben).
Achso, von der Seite habe ich das noch garnicht gesehen.

Man bezieht sich quasi mit der extern-Deklaration auf ein globales 
Symbol, welches man gleich danach auch definiert, aber nicht 
exportiert...


Naja. Obwohl das nicht ganz korrekt ist, habe ich mir in meinem Weltbild 
extern und static in etwa symmetrisch hingelegt. Entweder ist etwas eine 
extern-Deklaration, dann kommt es (z.B. per Linker) von irgendwoher. 
Oder es ist eine static-Definition, dann soll es ausdrücklich 
nirgendwoher kommen und nur in der Übersetzungseinheit bleiben.

Das erscheint mir auch insofern sinnvoll, als dass die 
static-Speicherklasse quasi die Lebensversicherung dafür ist, nicht 
versehentlich mit fremden Symbolen zu kollidieren.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sven P. schrieb:
> Johann L. schrieb:
>> Das braucht es auch nicht, mein Code ist in einer Compilation Unit
>> (ansonsten hätte ich das natürlich dazu geschrieben).
> Achso, von der Seite habe ich das noch garnicht gesehen.
>
> Man bezieht sich quasi mit der extern-Deklaration auf ein globales
> Symbol, [...]

Nein, in dem speziellen Beispiel ist das Symbol nicht global (internal 
Linkage, keine external Linkage) und dennoch kann man sich mit 
extern darauf beziehen.

von Daniel S. (pallas)


Lesenswert?

Hallo,
da bin ich wieder.
Entschuldigt das ich mich so lange nicht gemeldet habe.
Ich war die letzten Tage leider ziemlich verplant, daher...

Es hat sich ja eine rege Unterhaltung entwickelt.
Die habe ich natürlich auch mitgelesen.
Dabei ist mir allerdings von Beitrag zu Beitrag mehr klar geworden, dass 
ich offensichtlich ein viel grundlegenderes Problem habe.
Mir fehlt Wissen über den Compiler und den Kompiliervorgang an sich.
Das verwundert natürlich nicht, da ich mich gerade erst am Anfang 
befinde.

Trotzdem drängen sich mir einige Fragen auf, welche ich zur 
unkoplitzierten Klärung einfach mal hier in den Raum stellen möchte.
Sollten das bereits Themen für neue Threads sein, bitte ich einfach um 
eine kurze Rückmeldung diesbezüglich.

Vor allem habe ich aber ein Quellenproblem.
Es wäre nicht so, dass ich keine Bücher zur Thematik hier liegen hätte.
Leider beziehen sie sich auf die reine Programmierung. Da mich aber auch 
interessiert was im Hintergrund passiert, scheinen solche Quellen für 
mich einfach nicht ausreichend zu sein.
Internetquellen sind auch so eine Sache. Manche stimmen, manche nicht. 
Sobald sie sich wiedersprechen gibt es ein Entscheidungsproblem, was in 
der Regel beide Quellen nutzlos macht.

Deswegen gleich meine erste Frage:
-> Kann mir jemand ein Buch zur Thematik empfehlen?

Meine weiteren Fragen sind folgende:
-> Weswegen brauche ich überhaupt einen Header? Könnte ich nicht einfach 
direkt die .c-Datei einbinden (in diese wären die Funktionsdeklarationen 
dann direkt eingebunden.
-> Wann im Kompiliervorgang werden die Funktionsdeklarationen im Header 
gegen die echten Implementierungen getauscht? Beim Linken?
-> Wieso muss ich die Variable ersteVariable im Header deklarieren? Kann 
ich sie nicht einfach direkt in der passenden .c-Datei definieren? Was 
wären die Folgen?

von Sven P. (Gast)


Lesenswert?

Johann L. schrieb:
> Sven P. schrieb:
>> Johann L. schrieb:
>>> Das braucht es auch nicht, mein Code ist in einer Compilation Unit
>>> (ansonsten hätte ich das natürlich dazu geschrieben).
>> Achso, von der Seite habe ich das noch garnicht gesehen.
>>
>> Man bezieht sich quasi mit der extern-Deklaration auf ein globales
>> Symbol, [...]
>
> Nein, in dem speziellen Beispiel ist das Symbol nicht global (internal
> Linkage, keine external Linkage) und dennoch kann man sich mit
> extern darauf beziehen.
Tippfehler, lokales Symbol natürlich. Es wird ja grad nicht 
exportiert...

Daniel S. schrieb:
> Quellen
Dein Quellenproblem ist nachvollziehbar. Die Erklärungen von mir und 
Johann kann man leider noch am besten bei uralten Compilern bzw. in 
alter Literatur nachvollziehen. Mit aktuellen Toolchains ist der 
Compiler mit dem Linker heute ziemlich verzahnt.

> -> Weswegen brauche ich überhaupt einen Header? Könnte ich nicht einfach
> direkt die .c-Datei einbinden (in diese wären die Funktionsdeklarationen
> dann direkt eingebunden.
Im Prinzip schon, den Compiler juckts nicht. Allerdings würdest du ja 
dann auch die Definitionen immer wieder mit einbinden und dadurch 
wiederholen. Das willst du im Falle globaler Variablen ja gerade nicht 
(wobei die tentativen Definitionen ja ein bisschen Ausnahme sind).

Zum Beispiel würden static-Variablen dann mehrfach im Speicher erzeugt. 
Eventuell ist dein Linker so klug und sammelt die Funktionsdefinitionen 
(also die Rümpfe) nachher wieder zusammen, ansonsten hast du auch die 
nachher mehrfach im Speicher.

> -> Wann im Kompiliervorgang werden die Funktionsdeklarationen im Header
> gegen die echten Implementierungen getauscht? Beim Linken?
Ja. Aber nicht nur die Funktionsdeklarationen, auch die globalen 
Variablen. Tauschen ist auch Ansichtssache. Der Compiler setzt einfach 
nur Platzhalter-Adressen ein, wann immer eine Variable oder Funktion 
braucht, von der er keine Definition (sondern nur eine Deklaration) hat.
Der Linker ersetzt diese Platzhalter anschließend durch echte Adressen. 
Der Linker ist nämlich auch derjenige, der den ganzen Kram im Speicher 
anordnet und Platz für Variablen verteilt, also kennt er die Adressen.

> -> Wieso muss ich die Variable ersteVariable im Header deklarieren? Kann
> ich sie nicht einfach direkt in der passenden .c-Datei definieren? Was
> wären die Folgen?
Definieren oder Deklarieren?
Normalerweise will man sie im Header deklarieren (mit extern), damit 
andere Leute, die den Header einbinden, darauf zugreifen können. Im 
Prinzip könntest du sie auch kurz vor Verwendung händisch deklarieren 
(mit extern). Im Header ist es aber praktischer, weil der eine schöne 
Schnittstelle bietet. Normalerweise betreust du als Entwickler ja eine 
Source mitsamt Header und kannst beide zusammen pflegen.
Das heißt, in deiner Source hast du die Definition und in deinem Header 
machst du sie per Deklaration bekannt, damit andere Leute deinen 
Programmteil nutzen können.

von Dirk B. (dirkb2)


Lesenswert?

In den Header kommen die Deklarationen rein, die eine andere .c Datei 
braucht, um deine .c benutzen zu können.

Nicht mehr.

Es sind die Schnittstelleninformationen.

Natürlich nurnötig, wenn du auch mehrere .c benutzt.

von Daniel S. (pallas)


Lesenswert?

Sven P. schrieb:
>> -> Wieso muss ich die Variable ersteVariable im Header deklarieren? Kann
>> ich sie nicht einfach direkt in der passenden .c-Datei definieren? Was
>> wären die Folgen?
> Definieren oder Deklarieren?
> Normalerweise will man sie im Header deklarieren (mit extern), damit
> andere Leute, die den Header einbinden, darauf zugreifen können. Im
> Prinzip könntest du sie auch kurz vor Verwendung händisch deklarieren
> (mit extern). Im Header ist es aber praktischer, weil der eine schöne
> Schnittstelle bietet. Normalerweise betreust du als Entwickler ja eine
> Source mitsamt Header und kannst beide zusammen pflegen.
> Das heißt, in deiner Source hast du die Definition und in deinem Header
> machst du sie per Deklaration bekannt, damit andere Leute deinen
> Programmteil nutzen können.

Ich meine wirklich definieren.
Ich kann mir aber fast schon die Antwort darauf geben.
Steht die Deklaration nicht im Header, kann im Falle das die Variable 
nicht schon bereits in der einbindenden Datei existiert auch nicht auf 
die Variable zugegriffen werden.
Sie ist schlicht nicht bekannt.
So wird der Header eingebunden, die Variable "vorgestellt" und in der 
zugehörigen .c-Datei definiert.
Wird sie nur dort definiert, ist sie im einfachsten Fall eben auch nur 
dort verfügbar.

Das ist offensichtlich auch der Grund warum es Header-Files gibt.
Mir ist einfach noch nicht ganz klar, warum ich nicht ANSTELLE der 
Header-Datei nicht einfach die ganze .c-Datei einbinde.

von W.S. (Gast)


Lesenswert?

Sven P. schrieb:
> Das erscheint mir auch insofern sinnvoll, als dass die
> static-Speicherklasse quasi die Lebensversicherung dafür ist, nicht
> versehentlich mit fremden Symbolen zu kollidieren.

Sollte kein Problem sein, denn heutzutage gibt es beim Linker anstelle 
eines gelinkten Files eine geharnischte Fehlermeldung, wenn es mehrere 
gleichnamige Variablen oder Funktionen in verschiedenen Modulen gibt. 
Das static kann man deshalb getrost weglassen - sofern man nicht 
dediziert exakt gleichnamige globale Variablen in jeweils mehreren 
Modulen habe will.


Yalu X. schrieb:
> Die heutigen unixoiden Linker sind natürlich mindestens genauso schlau
> wie der Linker auf der PDP-11, so dass es eigentlich keinen Grund gibt,
> das "extern" in globalen Deklarationen zu erzwingen. Aus Rücksicht auf
> zurückgebliebene Linker unter anderen Betriebssystemen

Das ist mal wieder eine recht seltsame Weltsicht, die hier zutage kommt.

Das Normale bei nicht zurückgebliebenen Programmiersprachen ist, daß man 
vor externen Entitäten eben das "extern" davorschreiben sollte oder 
einen anderen Weg der Kennzeichnung wählen sollte, um die Tatsache der 
Externität zu dokumentieren und den Compiler nicht vergeblich nach der 
eigentlichen Deklaration dieser Entität innerhalb der vorhandenen Quelle 
suchen zu lassen.

Aber C kennt ja von alldem nichts und hat auch keinerlei Modulsystem - 
und die Sichtweise von K&R ist einfach nur krank. Das ist noch viel 
schlimmer als die vorgefaßten Typzuweisungen in Fortran bei i,j,k,l...

Also: Zurückgeblieben ist lediglich die Sichtweise von Ritchie und der 
Compiler der PDP-11. Das ist alles.

W.S.

von W.S. (Gast)


Lesenswert?

Daniel S. schrieb:
> Das ist offensichtlich auch der Grund warum es Header-Files gibt.
> Mir ist einfach noch nicht ganz klar, warum ich nicht ANSTELLE der
> Header-Datei nicht einfach die ganze .c-Datei einbinde.

Kannst du machen, aber in diesem Falle darfst und brauchst du diese 
Datei auch garnicht separat zu übersetzen und erst recht nicht ins 
Projekt mit zu linken, denn sie iest damit ja bereits enthalten.

Das ist alles ein altertümliches Kuddelmuddel.

Also merke dir - im direkten Gegensatz zu den Auffassungen von Yalu - 
dieses:

In .h Dateien kommt - so wie es ist - nur so etwas hinein, was selbst 
keinen Speicherplatz kostet. Also Typdeklarationen und Konstanten per 
#define.
Alles, was hingegen Speicherplatz kosten würde, kommt nur mit 
vorgestelltem extern hinein - einfach um damit anzuzeigen, daß das in 
der Headerdatei Befindliche nur ein Verweis ist auf etwas, das woanders 
tatsächlich steht.

Und Ritchie kann im Jahre 2019 seinen Drang zur unsauberen 
Programmierung einfach mal steckenlassen. Gilt auch für seine 
Apologeten.

W.S.

von Sven P. (Gast)


Lesenswert?

W.S. schrieb:
> Sollte kein Problem sein, denn heutzutage gibt es beim Linker anstelle
> eines gelinkten Files eine geharnischte Fehlermeldung, wenn es mehrere
> gleichnamige Variablen oder Funktionen in verschiedenen Modulen gibt.
> Das static kann man deshalb getrost weglassen - sofern man nicht
> dediziert exakt gleichnamige globale Variablen in jeweils mehreren
> Modulen habe will.
Nein, gibt es eben nicht. Gleichnamige Variablen sammelt der Linker in 
der .comm-Sektion auf und macht globale draus. Das hat Johann aber oben 
auch länglich erklärt.

Das static ist unbedingt notwendig, weil man ja eben nicht weiß, was in 
anderen Modulen vereinbart wird.

Daniel S. schrieb:
> Mir ist einfach noch nicht ganz klar, warum ich nicht ANSTELLE der
> Header-Datei nicht einfach die ganze .c-Datei einbinde.
Naja ganz einfach, weil du den Inhalt des Headers (also die 
Variablen-Vorstellungen, wie du ziemlich treffend geschrieben hast) 
möglicherweise auch in mehreren anderen Sourcen benötigst. Und wenn du 
in jeder dieser Sourcen gleich die ganze .c-Datei einbindest, dann hast 
du es doppelt.

Abgesehen davon wird die Variable ja durch eine Deklaration schon 
bekannt, bevor sie definiert wird (Vorwärts-Deklaration). Das ist 
manchmal notwendig, weil es manchmal schlicht keine mögliche Reihenfolge 
gibt, in der man alle .c-Dateien in zusammenkopieren könnte. Das 
passiert zum Beispiel, wenn sich zwei Funktionen gegenseitig aufrufen. 
Ohne eine Deklaration von einer der beiden würde das nicht gehen, denn 
egal wie herum die die Funktionen in die .c-Datei schreibst: die erste 
kennt die zweite nicht. Außer du spendierst eine Deklaration vorab.


Abgesehen von dem K&R-Gesabbel ist folgende aber eine ganz passable 
Merkregel von W.S.:
W.S. schrieb:
> In .h Dateien kommt - so wie es ist - nur so etwas hinein, was selbst
> keinen Speicherplatz kostet.

von Daniel S. (pallas)


Lesenswert?

Okay, danke für eure vielen Antworten erst einmal.
Ich denke das sollte bei der tollen Resonanz hier nicht zu kurz kommen.

Ich suche eigentlich eine sehr hardwarenahe Programmiersprache mit der 
ich für ein Linux-Betriebssystem programmieren kann.
Da Linux größtenteils in C geschrieben ist dachte ich, das dies auch die 
geeignete Sprache der Wahl ist.
Inzwischen bin ich mir aber nicht mehr sicher ob ich C überhaupt jemals 
richtig lernen kann.
Es scheint unglaublich viele Varaiblen zu geben, die für einen Anfänger 
nur wirklich schwer einzusehen sind.
Selbst eingearbeitete Profis sind sich bei einigen Dingen - zumindest 
uneins.
Nicht zuletzt hatte ich C ausgewählt, weil es auch recht populär ist.
Bitte versteht mich nicht falsch. Ich möchte hier nichts infrage stellen 
und schon gar nicht will ich jemandem auf die Füße treten.
Ich frage mich lediglich, ob es für mich als Einsteiger nicht eine 
bessere Wahl gibt.
Die Sprache die ich suche sollte:
-> statisch typisiert sein
-> möglichst hardwarenah sein
-> geschriebene Programme sollten maximal portabel sein
-> ich möchte Systemaufrufe in Linux umsetzen können und auch ganz 
allgemein höchstmögliche Linuxkompatibilität haben
-> die Sprache sollte gut dokumentiert sein und deutsche Literatur 
sollte verfügbar sein
-> die Sprache sollte in sich konsistent sein
-> die Sprache sollte zukunftssicher sein

Sicher habe ich mich noch nicht gegen C entschieden.
Ich will einfach nur sicherstellen, dass ich mich hier nicht in 
Unmöglichkeiten vergallopiere.
Wenn ich mehr Zeit mit Raten verbringen würde statt mit verstehen, wäre 
C ganz eindeutig falsch für mich.

Was meint Ihr denn dazu?

von Sven P. (Gast)


Lesenswert?

Daniel S. schrieb:
> Was meint Ihr denn dazu?
Fang einfach mit C an.

Bis jetzt scheinst du doch alles verstanden zu haben?

Linkage ist nunmal da, auch bei anderen Sprachen. Häng das nicht so hoch 
auf und mach dir nicht so einen Kopf darum bei akademischen Beispielen. 
Wenn es in deinem Programm so weit ist, dann wird sich das ganz 
geschmeidig einfügen.

Und richtig lernen. Ich denke, 50% der "Programmierer" in der Industrie 
können nicht richtig mit ihren Werkzeugen umgehen. Das sehe ich jedes 
Mal wieder, wenn ich fremden Quelltext angucke, der aus der "Industrie" 
kommt.

Allein damit, dass du die ganze Geschichte mit der Linkage und der 
Speicherklasse überhaupt mal durchdacht hast, hast du davon vermutlich 
schon mehr kapiert, als deine Kollegen... Und da du ja ohnehin systemnah 
arbeiten willst, ist das doch ein optimaler Weg.

von Daniel S. (pallas)


Lesenswert?

Okay, ich denke ich werde mich einfach da durchbeißen, möglichst ohne 
mich zu verbissen an etwas aufzuhängen.
Wahrscheinlich versteht man viele Zusammenhänge auch erst wenn man 
unabhängig an verschiedenen Stellen tiefer in die Materie eingedrungen 
ist.

Ich denke ich habe hier auch eine sehr gute Anlaufstelle um Fragen zu 
Stellen.
Von daher rechne ich mir meine Chancen schon mal nicht so schlecht aus.

Ich hatte eben deswegen gefragt, weil ich einfach auch mal die Meinung 
von jemandem hören wollte, der sich besser mit C auskennt und daher auch 
das Naturell der Sprache an sich einschätzen kann.

Aber wenn man mir das an der Stelle zutraut bin ich beruhigt.

:-)

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Daniel S. schrieb:
> Inzwischen bin ich mir aber nicht mehr sicher
> ob ich C überhaupt jemals richtig lernen kann.

C ist eine relativ kleine Sprache, mit einer relativ kleinen 
Standardbibliothek. Das macht es grundsätzlich zu einer einfach zu 
lernenden Sprache.

Allerdings hat C einige Eigenheiten, die historisch gewachsen sind und 
einstmals sinnvoll waren, es heute aber nicht mehr unbedingt sind. Von 
deren Existenz muss man zumindest wissen, um Fehler vermeiden zu können.

Und dann wäre da noch der Fakt, dass C mit sich alles machen lässt, was 
irgendwie einigermaßen möglicherweise sinnvoll sein könnte. Das macht C 
zu einer komplexen und äußerst mächtigen Sprache.

Die restliche Infrastruktur um C herum (Compiler, Linker, ...) ist 
ähnlich primitiv wie der eigentliche Sprachkern und erfordert, dass man 
die Grundlagen verstanden hat.

Moderne Sprachen verheimlichen die interne Funktionsweise, teilweise bis 
zum Punkt "hier klick für START und dort klicken für EXE", was prima 
funktioniert, bis es mal nicht funktioniert. Dann muss man das alles 
trotzdem lernen, es wird einem aber besonders schwer gemacht.

Daniel S. schrieb:
> Selbst eingearbeitete Profis sind sich bei
> einigen Dingen - zumindest uneins.

Programmieren (Programmentwicklung) ist an sich eine kreative Tätigkeit, 
im Gegensatz zum recht mechanischem Code-Tippen. Da gibt es oft kein 
"richtig" oder "falsch", sondern unterschiedliche Meinungen mit jeweils 
eigenem Für und Wider, die man zudem ständig neu bewerten muss. Die 
Realität erfordert ständig Kompromisslösungen.

Zum Abschluss wäre zu sagen: Deine Probleme scheinen nicht "C lernen" zu 
sein, sondern "Programmieren lernen". Wenn du das einmal gründlich durch 
hast, dann ist die Sprache eher egal.

von GEKU (Gast)


Lesenswert?

Der Compiler muss nur die Eigenschaften aber nicht die Lage von 
Variablen oder Funktionen kennen.

Eigenschaften der Variablen sind z.B. die Größe oder die 
Veränderbarkeit.
Warum? Weil der Compiler je nach der Größe (char, int, long, float . ..) 
der Variablen einen unterschiedlichen Maschinencode einsetzen muss.

Beispiele:     char  =>   mov.b      int  =>   mov.w    bei float reicht 
meist ein einzelner Maschinencode nicht aus. Es wird ein komplettes 
Unterprogramm aus einer Bibliothek eingefügt.
Auch ob die Variable vorzeichenbehaftet oder vorzeichenlos (unsigned) 
ist, hat Einfluss auf den vom Compiler eingesetzten Maschinencode.
Wird eine Variable mit der Eigenschaft const versehen, dann verbietet 
der Compiler jeglichen Maschinencode, der eine Veränderung bewirken 
würde. Die Eigenschaft volatil untersagt dem Compiler Optimierungen mit 
der Variablen durchzuführen. Einzig, die AdressInformation, wo die 
Variable liegt, bleiben bei der Erzeugung des Maschinencodes offen, es 
wird nur der Platz in der Objektdatei reserviert, der dann vom Linker 
ausgefüllt wird.
Für Funktionen gelten ähnliche Gründe, z.B bei der Parameterübergabe.

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.