Forum: Compiler & IDEs Eine "Kinderfrage" bzg. *.h und *.c


von Maxim B. (max182)


Lesenswert?

Guten Tag,
ich habe eine "Kinderfrage" - das muß eigentlich sehr einfach sein, aber 
ich kapiere das noch nicht. Suche hat auch kein Ergebnis gebracht 
(komischerweise).

Die Frage: wie verteilt man Programm auf mehrere Files?
Ich kenne #include, und soweit ich etwas auf ein *.h - File umplatziere 
und danach mit #include in Haupttext einbinde - dann arbeitet alles so, 
als ob der Inhalt zweiten Files direkt im Text wäre. Aber wenn ich 
versuche, wie empfolen, in *.h nur #define und Prototypen von Funktionen 
zu schreiben, und die Funktionen selbst in dem gleichnahmigen *.c - 
File, dann klappt nichts. Nur Fehlermeldungen.

Ich denke, ich kann nicht richtig *.c und *.h zusammenbinden.
Ob jemand mir bitte erklären könnte, woran das liegt, oder noch besser 
ein Link geben, wo Einbinden von anderen Files beschrieben wird?
Leider gibt meine Suche alles mögliches über C selbst, aber nichts über 
Hantieren mit *.h und *.c.

Vielen Dank im voraus,
Maxim.

: Verschoben durch Moderator
von Olga (Gast)


Lesenswert?

Maxim B. schrieb:
> Nur Fehlermeldungen.

Profi-Tip: Die Fehlermeldungen haben sogar einen Text, den man lesen 
kann. Lesen muss du den Text schon selber, denn wir kennen ihn nicht.

von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> Aber wenn ich
> versuche, wie empfolen, in *.h nur #define und Prototypen von Funktionen
> zu schreiben, und die Funktionen selbst in dem gleichnahmigen *.c -
> File, dann klappt nichts.

ALLE *.c Files müssen Teil vom Projekt sein.
Die Header kommen dann über die #include Anweisungen dazu.
JEDE *.c muss alle Header einbinden, in denen die Prototypen von 
Funktionen stehen, welche in der jeweiligen *.c verwendet werden.

von Maxim B. (max182)


Lesenswert?

>
> Profi-Tip: Die Fehlermeldungen haben sogar einen Text, den man lesen
> kann.
Leider bringt das wenig: nur Info, daß das und das nicht definiert 
wurde. Obwohl in Hauptfile schon alles da und allein mit *.h alles 
arbeitet... Warum mit *.h wird alles richtig kompiliert und mit *.h und 
*.c zusammen nicht? Wie bindet man diese Files zusammen?

von Peter II (Gast)


Lesenswert?

Maxim B. schrieb:
> Warum mit *.h wird alles richtig kompiliert und mit *.h und
> *.c zusammen nicht? Wie bindet man diese Files zusammen?

zeige uns doch mal ein Beispiel wie du es gemacht hast.

von Mark B. (markbrandis)


Lesenswert?

Maxim B. schrieb:
>>
>> Profi-Tip: Die Fehlermeldungen haben sogar einen Text, den man lesen
>> kann.
> Leider bringt das wenig: nur Info, daß das und das nicht definiert
> wurde.

Wenn es eine "undefined reference" Meldung ist, dann bringt die 
Information sehr viel: Dann kommt die Fehlermeldung nämlich nicht vom 
Compiler, sondern vom Linker.

Wie baust Du denn die Software? Mit einer bestimmten IDE? Mit selbst 
geschriebenen makefiles? Anders?

von Maxim B. (max182)


Lesenswert?

> JEDE *.c muss alle Header einbinden, in denen die Prototypen von
> Funktionen stehen, welche in der jeweiligen *.c verwendet werden.
Danke!
Bedeutet das, daß ich in *.c alle #include, die im Hauptfile schon 
angegeben sind, wiederholen muß?
Ich weiß - das sind Kinderfragen. Aber noch dummer wäre es, nicht zu 
fragen :)

von foo (Gast)


Lesenswert?

Volker S. schrieb:
> JEDE *.c muss alle Header einbinden, in denen die Prototypen von
> Funktionen stehen, welche in der jeweiligen *.c verwendet werden.

...bzw. anders ausgedrückt: jede C-Datei muss einzeln übersetzbar 
sein...

von Maxim B. (max182)


Lesenswert?

> Wie baust Du denn die Software? Mit einer bestimmten IDE? Mit selbst
> geschriebenen makefiles? Anders?
Mit AVR Studio 4.18 Build 716. Und dazu WinAVR-20100110

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Maxim B. schrieb:
>> Wie baust Du denn die Software? Mit einer bestimmten IDE? Mit selbst
>> geschriebenen makefiles? Anders?
> Mit AVR Studio 4.18 Build 716. Und dazu WinAVR-2100110

Vielleicht sind nicht alle C-Dateien dem Projekt hinzugefügt. Das würde 
ich mir mal als erstes anschauen.

von DirkB (Gast)


Lesenswert?

Jede .c wird für sich selber übersetzt in ein Objectfile. Das macht der 
Compiler

Dann werden die Objectfiles mit den Bibliotheken zu einem Ausführbaren 
Programm gebunden. Das macht der Linker.

Deine IDE muss jetzt wissen, welche .c-Dateien (und Bibliotheken) zu 
deinem Programm gehören. Dazu gibt es meist ein Project-Menü, in dem man 
das machen kann.

Es geht auch mit der Hand, bzw etwas einfacher mit einem makefile.

von Maxim B. (max182)


Lesenswert?

Ich denke, das Problem liegt daran, daß ich in *.h und *.c falsche 
Angaben mache.
Ich habe nun versucht, in "Nebenfile.c" alle #include zu machen.
Nun kommt folgende Spielerei:
wenn ich in *.h keine Variablen gebe und alle nur in Hauptfile, sagt 
Compieler: undefinierte Variable in "Nebenfile.c".
Wenn ich aber die Variablen in *.h platziere und von beiden *.c includen 
lasse, dann sagt Compieler "multiple definition".
Was mache ich falsch?

von Honk (Gast)


Lesenswert?

Einfaches Beispiel:

myfunction.c:
1
void MyFunction(void)
2
{
3
4
}

Den Prototypen hierzu gibt es in myfunction.h:
1
extern void MyFunction(void);


In jeder Datei, die MyFunction nun verwenden will, muss der Prototyp 
bekannt sein. Dies wird idR durch Inkludierung des Headers gemacht:

main.c:
1
#include "myfunction.h"
2
3
...
4
MyFunction();

Und zwar nicht nur in der Datei, die main() beinhaltet, sondern in jeder 
Datei, die MyFunction aufruft:

AnderesModul.c:
1
#include "myfunction.h"
2
3
...
4
MyFunction();

Auch in myfunction.c ist es aus mehreren Gründen empfehlenswert, 
myfunction.h einzubinden.

von Honk (Gast)


Lesenswert?

Nachtrag:

Zunächst mal sind globale Variablen nicht unbedingt empfehlenswert. Um 
eine Mehrfachdefinition zu verhindern, bedient man sich eines so 
genannten "Include Guards":

myfunction.h:
1
#ifndef _MYFUNCTION_H_
2
#define _MYFUNCTION_H_
3
4
hier kommt der eigentliche Inhalt des Headerfiles
5
6
#endif

von Dirk B. (dirkb2)


Lesenswert?

Honk schrieb:

> Den Prototypen hierzu gibt es in myfunction.h:
1
 extern void MyFunction(void);

Bei Funktionsdeklarationen verzichtet man i.A. auf das extern.

von Honk (Gast)


Lesenswert?

Noch ein Nachtrag:

Wenn du eine globale Variable in main.c anlegst, dann kannst du auch aus 
anderen Modulen daraus zugreifen, wenn die Variable dem Modul vorher 
bekannt gemacht wird:

main.c:
1
uint32_t MyVar = 0;

nebenfile.c:
1
extern uint32_t MyVar;
2
3
MyVar = 12345ul;

Wie gesagt versucht man aber normalerweise, globale Variablen zu 
vermeiden. Die Gründe und Argumente hierfür sind vielfältig. Bei Deinem 
ersten C-Programm kann man aber wohl darüber hinwegsehen.

von Honk (Gast)


Lesenswert?

Dirk B. schrieb:
> Bei Funktionsdeklarationen verzichtet man i.A. auf das extern.

Richtig, viele verwenden es nur bei Variablen. Eine alte Angewohnheit 
von mir :)

von Dirk B. (dirkb2)


Lesenswert?

In die Headerdatei kommen alle Informationen, die andere c-Dateien 
brauchen um die Funktionen zu benutzen.
Mehr nicht.

Interne Funktionen, Variablen und Typen bleiben in der .c (und werden 
als static definiert)

von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> Wenn ich aber die Variablen in *.h platziere und von beiden *.c includen
> lasse, dann sagt Compieler "multiple definition".
> Was mache ich falsch?

Wenn du das machst, dann wird ja, wie du ganz oben schon selber gesagt 
hast, der Text des Headers beiden *.c Dateien zugefügt.
Das ist also genau das gleiche, als ob du in beiden *.c die gleiche 
globale Variable definierst.
"Definitionen" im Sinne von etwas, dass direkt Programmcode erzeugt oder 
Speicherplatz reserviert, haben in einem Header nichts verloren!

: Bearbeitet durch User
von Maxim B. (max182)


Angehängte Dateien:

Lesenswert?

Vielen Dank!
Um klarer zu machen, wo ich hinke, gebe ich hier die Dateien aus meinem 
Probeprojekt:

Vielen Dank im voraus für Fehleranweisungen!
Hier geht es um ein Voltmeter für sehr rauscharme Umgebung, daher 
statische LED.

Bestimmt fehlt mir noch Klarheit, was wohin gehört...

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> Bestimmt fehlt mir noch Klarheit, was wohin gehört...

Wenn du die Fehlermeldungen noch posten würdest, wäre alles viel 
einfacher. Warum soll sich eigentlich jemand anderes die Mühe machen?

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Maxim B. schrieb:
> Bestimmt fehlt mir noch Klarheit, was wohin gehört...

Variablen sollte nicht in .h Dateien angelegt werden.
1
volatile int volt = PWM_init / 2; //

dort sollte sie nur als Extern vorhanden sein.
1
extern volatile int Volt;

und dann müssten sie in die passende C rein.
1
volatile int volt = PWM_init / 2; //

von Maxim B. (max182)


Lesenswert?

Build started 26.7.2016 at 13:26:38
avr-gcc -mmcu=atmega324pa -Wl,-Map=Voltmeter_2016_07_24_v3.map 
Voltmeter_2016_07_24_v3.o Ind_stat.o     -o Voltmeter_2016_07_24_v3.elf
Ind_stat.o: In function `vyvod':
D:\A_Radioelementen_Daten\ZZ_Projekt\Schematik\2016_07_Voltmeter\Program 
m\Voltmeter_2016_07_24_v3\default/../Ind_stat.c:12:  multiple definition 
of `zif1'
Voltmeter_2016_07_24_v3.o:D:\A_Radioelementen_Daten\ZZ_Projekt\Schematik 
\2016_07_Voltmeter\Programm\Voltmeter_2016_07_24_v3\default/../Voltmeter 
_2016_07_24_v3.c:45:  first defined here
Ind_stat.o: In function `vyvod':
D:\A_Radioelementen_Daten\ZZ_Projekt\Schematik\2016_07_Voltmeter\Program 
m\Voltmeter_2016_07_24_v3\default/../Ind_stat.c:12:  multiple definition 
of `zif2'
Voltmeter_2016_07_24_v3.o:D:\A_Radioelementen_Daten\ZZ_Projekt\Schematik 
\2016_07_Voltmeter\Programm\Voltmeter_2016_07_24_v3\default/../Voltmeter 
_2016_07_24_v3.c:45:  first defined here
Ind_stat.o: In function `vyvod':
D:\A_Radioelementen_Daten\ZZ_Projekt\Schematik\2016_07_Voltmeter\Program 
m\Voltmeter_2016_07_24_v3\default/../Ind_stat.c:12:  multiple definition 
of `zif3'
Voltmeter_2016_07_24_v3.o:D:\A_Radioelementen_Daten\ZZ_Projekt\Schematik 
\2016_07_Voltmeter\Programm\Voltmeter_2016_07_24_v3\default/../Voltmeter 
_2016_07_24_v3.c:45:  first defined here
Ind_stat.o: In function `vyvod':
D:\A_Radioelementen_Daten\ZZ_Projekt\Schematik\2016_07_Voltmeter\Program 
m\Voltmeter_2016_07_24_v3\default/../Ind_stat.c:12:  multiple definition 
of `zif4'
Voltmeter_2016_07_24_v3.o:D:\A_Radioelementen_Daten\ZZ_Projekt\Schematik 
\2016_07_Voltmeter\Programm\Voltmeter_2016_07_24_v3\default/../Voltmeter 
_2016_07_24_v3.c:45:  first defined here
Ind_stat.o:(.data+0x0): multiple definition of `volt'
Voltmeter_2016_07_24_v3.o:(.data+0x0): first defined here
make: *** [Voltmeter_2016_07_24_v3.elf] Error 1
Build failed with 1 errors and 0 warnings...

Es tut mir leid, aber ich verstehe Englisch nur so halbwegs... mehrfache 
definition von Var. - das ist mir klar. Aber warum und wie ist das zu 
verbessern?

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> multiple definition of `zif1'

Volker S. schrieb:
> "Definitionen" im Sinne von etwas, dass direkt Programmcode erzeugt oder
> Speicherplatz reserviert, haben in einem Header nichts verloren!

zif1 wird in einem Header "Definiert" ...

von Honk (Gast)


Lesenswert?

Die Lösung hierfür hat Peter II einen Beitrag darüber beschrieben.

von Volker S. (vloki)


Lesenswert?

Volker S. schrieb:
> zif1 wird in einem Header "Definiert" ...

Warum eigentlich überhaupt "global" bekannt machen?
Wird doch nur in einem einzigen *.c File verwendet.
Dann noch nicht mal in dem zugehörigen Header aufführen!

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Es tut mir leid: ich kapiere immer noch nicht...
Wenn ich eine Var. in beiden *.c verwende - wo muß ich sie deklarieren? 
In Hauptfile, in Nebenfile oder in Header?

von Volker S. (vloki)


Lesenswert?

WELCHE Variable verwendest du in beiden *.c Dateien?

von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> Es tut mir leid: ich kapiere immer noch nicht...
> Wenn ich eine Var. in beiden *.c verwende - wo muß ich sie deklarieren?
> In Hauptfile, in Nebenfile oder in Header?

Deklarieren musst du sie in einem Header (mit extern davor).
Definieren in einer c-Datei.

Zu welchem Source gehöre die logisch?

von Peter II (Gast)


Lesenswert?

Maxim B. schrieb:
> Es tut mir leid: ich kapiere immer noch nicht...
> Wenn ich eine Var. in beiden *.c verwende - wo muß ich sie deklarieren?
> In Hauptfile, in Nebenfile oder in Header?

jede Variabel darf nur EINMAL deklariert werden. Damit kann es nicht in 
einer Header Datei sein.

zu jeder .h gibt es meist eine .c und dort gehört die Variable rein.

Und es müssen nur Funktionen in der .h die außerhalb er .c genutzt 
werden sollen.

von Dirk B. (dirkb2)


Lesenswert?

Peter II schrieb:
> jede Variabel darf nur EINMAL deklariert werden. Damit kann es nicht in
> einer Header Datei sein.
Sie darf nur einmal definiert werden.

deklarieren kannst du sie, so ogft du willst.

von Maxim B. (max182)


Lesenswert?

Na, Danke an alle! Geklappt!
Aber ich sollte eine Zeile in Hauptfile ändern:
statt
  OCR1B = volt; // PWM in Halbe
sollte ich schreiben
  OCR1B = PWM_init/2; // PWM in Halbe
Dann blieb var. volt nur für Ind_stat.c und auch nur dort erklärt.

Nun läuft Programm als ob alles in einem File... Wie auch gedacht...

Aber trotzdem: wenn ich eine Var. in mehreren Files verwende, wo genau 
und wie wäre sie zu deklarieren? Ich möchte das verstehen...

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Verzichte auf die globalen Variablen.

Ein char-Array (kein C-String) mit 5 Elementen kannst du auch gut als 
Paramter für die Funktionen nehmen,

von Maxim B. (max182)


Lesenswert?

Z.B. wenn eine Var. in ISR geändert wird und von vielen anderen 
Funktionen abgefragt - wo und wie muß sie gefiniert werden?

von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> Na, Danke an alle! Geklappt!
> Aber ich sollte eine Zeile in Hauptfile ändern:
> statt
>   OCR1B = volt; // PWM in Halbe
> sollte ich schreiben
>   OCR1B = PWM_init/2; // PWM in Halbe
> Dann blieb var. volt nur für Ind_stat.c und auch nur dort erklärt.

Bravo ;-)

Maxim B. schrieb:
> Aber trotzdem: wenn ich eine Var. in mehreren Files verwende, wo genau
> und wie wäre sie zu deklarieren? Ich möchte das verstehen...

Da wo die Variable eben logisch hin gehört. Definiert wird sie in dem 
logisch zugehörigen *.c File und im entsprechenden Header als extern 
deklariert, damit der Compiler bei anderen *.c Files die diesen Header 
einbinden weiß, dass es diese Variable irgendwo gibt und wie sie 
Aussieht.
Da soll sich dann später der Linker darum kümmern, ob das alles so 
wirklich passt ;-)

von Mark B. (markbrandis)


Lesenswert?

Maxim B. schrieb:
> Aber trotzdem: wenn ich eine Var. in mehreren Files verwende, wo genau
> und wie wäre sie zu deklarieren? Ich möchte das verstehen...

Wenn Du schon globale Variablen verwendest, dann sollten diese 
wenigstens nur in einem einzigen Modul beschrieben werden. Dieses Modul 
ist dann sozusagen der Besitzer dieser Variablen, hier wird der 
Speicherplatz dafür angelegt. Die anderen Module lesen diese Variable 
nur.

Noch besser wäre es, auf globale Variablen ganz zu verzichten - oder 
zumindest überall da, wo es möglich ist.

Kauf Dir ein Buch über C-Programmierung!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Peter II schrieb:
> jede Variabel darf nur EINMAL deklariert werden. Damit kann es nicht in
> einer Header Datei sein.

Falsch.

Jede Variable kann so oft Du willst deklariert werden. Aber sie darf nur 
einmal definiert werden.

Das ist die Definition:

int foo;

Das ist die Deklaration:

extern int foo;

Und das ist für einen C-Compiler kein Problem:

extern int foo;
extern int foo;
extern int foo;
extern int foo;
int foo;

Du hast offenbar Definition und Deklaration verwechselt.

: Bearbeitet durch Moderator
von Dirk B. (dirkb2)


Lesenswert?

Maxim B. schrieb:
> Aber trotzdem: wenn ich eine Var. in mehreren Files verwende, wo genau
> und wie wäre sie zu deklarieren? Ich möchte das verstehen...

Volker S. schrieb:
> "Definitionen" im Sinne von etwas, dass direkt Programmcode erzeugt oder
> Speicherplatz reserviert, haben in einem Header nichts verloren!

Wenn du eine Funktion/Variable definierst, dann wird dafür Speicher 
verbraucht.
Darf nur einmal gemacht werden. Das gehört in eine C-Datei

Wenn du eine Funktion/Variable deklarierst, dann gibst du dem Compiler 
bekannt, das ein Objekt mit diesem Namen existiert. Kann oft gemacht 
werden.
Das gehört in eine H-Datei, aber nur, wenn diese Information ausserhalb 
der eigenen C-Datei gebraucht wird.

Da du aus main nur ismerenie und indikator aufrufst, brauchst du die 
anderen Funktionen auch nicht in der H-Datei deklarieren.

von Dirk B. (dirkb2)


Lesenswert?

Eine Definition ist (meist) auch eine Deklaration.
Aber nicht umgekehrt

von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> Z.B. wenn eine Var. in ISR geändert wird und von vielen anderen
> Funktionen abgefragt - wo und wie muß sie gefiniert werden?

Da würde ich auf das Main-C-File "tippen". Also die Datei, welche die 
Definition der Funktion main() enthält.
Dann meinetwegen in einem Header Namens "global....h" als extern 
deklarieren und diesen in allen C-Files einbinden die auf die Variable 
zugreifen.
Grundsätzlich aber trotzdem überlegen, ob es nicht manchmal doch besser 
ist, Variablen beim Funktionsaufruf zu übergeben.

von Mark B. (markbrandis)


Lesenswert?

Was um alles in der Welt soll "ismerenie" und "vyvod" bedeuten?

Funktionen und Variablen gibt man sinnvolle Namen. Wenn man das nicht 
kann, dann sollte man vielleicht lieber Pommes am Bahnhof verkaufen.

von Maxim B. (max182)


Lesenswert?

Danke!
Ich habe in global.h geschrieben
extern volatile int volt;
und in
Ind_stat.c volatile int volt = PWM_init / 2;
weiter zurück gemacht bei Init in Voltmeter_2016_07_24_v3.c:
OCR1B = volt; // PWM in Halbe

Ist das so korrekt?
Programm scheint zu funktionieren, aber ich möchte für die Zukunft 
wissen...

Über das Buch habe ich auch schon gedacht. Aber von Bildschirm leichter 
zu lesen...
Es gibt viele Bücher und viel Kritik... Wenn ich nach PDFs beurteilen 
darf, es gibt überall etwas Wichtiges, was nicht besprochen wird. Z.B. 
über heutige Problem habe ich nirgendwo etwas gefunden... Obwohl ich 
auch schon Bloodshed Dev-C++ installiert habe - um mit C als solcher 
etwas vertrauter zu werden (obwohl mein Ziel ausschließlich 
Mikrokontroller sind)...

Welches Buch wäre am besten?

von Maxim B. (max182)


Lesenswert?

Mark B. schrieb:
> Was um alles in der Welt soll "ismerenie" und "vyvod" bedeuten?
>
Als eingebürgerte Russe erlaube ich mir ab und zu auch mit russischen 
Worten hantieren :) Für mich sind sie verständlich :) Ich bin in 
Elektronik ein Liebhaber, schreibe eigentlich nur für mich selbst.

ismerenie heißt Messen
vyvod heißt Ausgabe.

: Bearbeitet durch User
von Honk (Gast)


Lesenswert?

Maxim B. schrieb:
> Wenn ich eine Var. in beiden *.c verwende - wo muß ich sie deklarieren?
> In Hauptfile, in Nebenfile oder in Header?

Modul1.c:
1
int Var = 0;

Modul1.h:
[c}
extern int Var;
1
Modul2.c:
2
[c]
3
#include "Modul1.h"
4
...
5
Var = 1;

Das ist jetzt nochmal exakt das Gleiche, das ich oben schon mal 
beschrieben habe! Es gibt im Allgemeinen auch keine "Haupt- und 
Nebenfiles". Die  main() stellt lediglich den Einsprungpunkt dar, nicht 
mehr und nicht weniger.

von Volker S. (vloki)


Lesenswert?

Mark B. schrieb:
> Was um alles in der Welt soll "ismerenie" und "vyvod" bedeuten?

zif1..4 ist auch nicht viel besser.
Vielleicht das nächste mal in einem russischsprachigem Forum posten ;-)

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Volker S. schrieb:

> Vielleicht das nächste mal in einem russischsprachigem Forum posten ;-)

Danke für Tipp. Würde ich weiter ein russischer Bürger bleiben und in 
Rußland leben, so würde ich so auch tun.

von Maxim B. (max182)


Lesenswert?

Honk schrieb:

>
> Modul1.c:
> [c]

Vielen, vielen Dank!

von Honk (Gast)


Lesenswert?

Irgendwas ist mit der Formatierung durcheinandergekommen, hier nochmal 
richtig:

Modul1.c:
1
int Var = 0;


Modul1.h:
1
extern int Var;


Modul2.c:
1
#include "Modul1.h"
2
...
3
Var = 1;


Rein theoretisch geht das auch ohne Header, Modul2.c müsste dann eben 
direkt die Deklaration enthalten:

Modul2.c
1
extern int Var;
2
...
3
Var = 1;

von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> Volker S. schrieb:
>
>> Vielleicht das nächste mal in einem russischsprachigem Forum posten ;-)
>
> Danke für Tipp. Würde ich weiter ein russischer Bürger bleiben und in
> Rußland leben, so würde ich so auch tun.

Sollte nur ein kleiner Wink mit dem Zaunpfahl sein. Es ist für die Leute 
hier doch viel einfacher, wenn Namen von Funktionen und Variablen 
verständlich sind. Du scheinst ja auf jeden Fall keine Probleme mit der 
deutschen Sprache zu haben. Da ist es doch eine nette kleine zusätzliche 
Übung auch deutsche Namen zu vergeben. (Ich versuche das zur Übung alles 
auf Englisch)

von Maxim B. (max182)


Lesenswert?

Vielen Dank noch einmal!
Ich habe mir das als kleine Einleitung gespeichert.

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Volker S. schrieb:
Es ist für die Leute
> hier doch viel einfacher, wenn Namen von Funktionen und Variablen
> verständlich sind. Du scheinst ja auf jeden Fall keine Probleme mit der
> deutschen Sprache zu haben.
Ich habe das auch nicht böse gemeint. Obwohl außer Thema - ich möchte 
mit Dank sagen, daß Deutschland vielleicht einziges Land auf der Erde 
bleibt, wo auch die Ausländer in der Hochschule kostenlos studieren 
dürfen! Ohne dieser Option könnte ich hier nie sein...

Ja, ich habe vor dem Posten alle Kommentare auf Russisch für Kommentare 
auf Deutsch ersetzt. Aber die Namen von Funktionen irgendwie verpaßt, zu 
korrigieren... Ich wußte nicht, daß das so reizen kann...

Hier in Pritzwalk reagieren die Leute auf Russisch eher mit Nostalgie 
und nie mit Abneigung.

Kommentar muß vor allem für mich selber so verständlich wie nur möglich 
sein - davon ging ich aus.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Honk schrieb:
> Modul1.c:int Var = 0;
>
> Modul1.h:extern int Var;
>
> Modul2.c:#include "Modul1.h"
> ...
> Var = 1;


Ich empfehle, Modul1.h zusätzlich auch in Modul1.c zu "inkludieren", 
also:

Modul1.c:
1
#include "Modul1.h"
2
int Var = 0;

Das hat einen - nicht auf Anhieb ersichtlichen - aber einfachen Grund: 
So meckert der Compiler sofort, wenn man versehenlich eine Variable 
inkonsistent definiert, z.B.:

Modul1.c:int Var = 0;

Modul1.h:extern double Var;

Modul2.c:#include "Modul1.h"
...
Var = 1;

Diese Situation fällt nicht auf! Im Programm passiert dann garantiert 
nicht das erwartete!

Wenn aber das Include-Statement in jedem relevanten C-Modul steht:

Modul1.c:#include "Modul1.h"
int Var = 0;

... dann kann der Compiler sofort eine Fehlermeldung ausgeben.

von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> Ich wußte nicht, daß das so reizen kann...

Also zumindest ich bin überhaupt nicht gereizt.
Da bin ich viel zu sehr abgehärtet von unseren Studierenden.
Wenn die einen Variablennamen mit zehn Zeichen vergeben, dann sind dann 
oft max. zwei bis drei Zeichen dabei, welches die Variable und deren 
Zweck irgendwie sinnvoll beschreibt. "Variable_1" oder so was sehe ich 
auch recht häufig ;-)

von Maxim B. (max182)


Lesenswert?

<< Frank M. schrieb:
Ich habe mir das als Merkzettel geschrieben:
Modul1.c: Variable mit Wert
Modul1.h: extern Variable ohne Wert
Modul2.c: #include "Modul1.h" oder extern Variable ohne Wert

Modul1.c: zusätzlich als Prüfung: #include "Modul1.h" vor der Variable 
mit Wert

Alles richtig?

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Volker S. schrieb:

> Also zumindest ich bin überhaupt nicht gereizt.

das ist gut. Ich habe mich zuerst so gewundert: bisher habe ich in etwa 
10 Jahren in D. nur einmal oder zweimal überhaupt etwas erlebt, was man 
annähernd als Unfreundlichkeit gegen Ausländern bezeichnen kann... :) In 
TV erzählt man viel Schreckchen, das ist aber weit vom Leben entfernt...

Ja, ich verstehe schon: wenn man an die Studierende täglich etwas sagt 
und wieder und immer noch und sie hören sowieso nicht... :)
Ich habe Musik studiert, bei uns ist das weniger stark geprägt, weil 
Unterricht meistens 1:1 ist. Aber ich kann schon einen Dozenten gut 
verstehen...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> Modul1.c: zusätzlich als Prüfung: #include "Modul1.h" vor der Variable
> mit Wert
>
> Alles richtig?

Jepp, so ist es richtig. Übrigens macht man es so auch für die 
Definitionen/Deklarationen von Funktionen - also wenn man 
modulübergreifend eine Funktion aufruft:

Modul1.c:
1
#include "Modul1.h"
2
3
int foo (int a, int b)
4
{
5
   bar (a, b);
6
}

Modul1.h:
1
extern int foo (int);

Modul2.h:
1
#include "Modul1.h"
2
...
3
   foo (123);

Erst durch das include in Modul1.c fällt auf, dass die Funktion foo() 
eigentlich zwei Parameter braucht.

: Bearbeitet durch Moderator
von Volker S. (vloki)


Lesenswert?

Maxim B. schrieb:
> Ja, ich verstehe schon: wenn man an die Studierende täglich etwas sagt
> und wieder und immer noch und sie hören sowieso nicht... :)

Da hilft eigentlich nur der Blick in die Vergangenheit (so 25 Jahre)
und die Einsicht, dass es schon immer viel einfacher war aus eigenen 
Fehlern zu lernen ;-)

von Maxim B. (max182)


Lesenswert?

Dann aber stimmt Prototyp und Funktion nicht mehr überein?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> Dann aber stimmt Prototyp und Funktion nicht mehr überein?

Ja. Das fällt nur auf, wenn Modul1.h auch in Modul1.c includiert wird. 
Sonst ist der Prototyp einfach für die Katz.

Im Laufe der Software-Entwicklung kommt es immer wieder vor, dass 
Funktionen um zusätzliche Parameter erweitert werden. Nur so merkt man, 
dass man nicht alles andere vollständig angepasst hat. Es gibt noch 
nichtmals einen Fehler vom Compiler und man sucht sich dumm und 
dusselig.

von Joachim B. (jar)


Lesenswert?

Maxim B. schrieb:
> ich möchte
> mit Dank sagen, daß Deutschland vielleicht einziges Land auf der Erde
> bleibt, wo auch die Ausländer in der Hochschule kostenlos studieren
> dürfen!
Danke!

Das ist was leider viele nicht verstehen, du hast es wenigstens mal 
benannt!
(ok rel. kostenlos, oder sind Studiengebühren wieder abgeschafft?)

Zu dem Sprachmix, ich hatte mal in einem Großraumbüro Prüfgeräte 
entwickelt und die Prüfsoftware geschrieben,

es war kein Problem Gespräche am Nachbartisch in deutsch auszublenden 
oder mitzuhören je nach dem (wir hatten durchaus Themenüberschneidungen 
bei denen ein kollegialer Austausch sinnvoll war), aber wenn die 
Kollegen auf türkisch anfingen zu reden konnte ich mich nicht mehr 
konzentrieren, mein Kopf versuchte immer zu verstehen was geredet wurde, 
ausblenden unmöglich.

Deswegen zur besseren Lesbarkeit hier für die meisten User zur späteren 
Hilfestellung scheinen deutsche lesbare Vasiablen- und Funktionsnamen 
sinnvoll.

von Volker S. (vloki)


Lesenswert?

Joachim B. schrieb:
> (ok rel. kostenlos, oder sind Studiengebühren wieder abgeschafft?)

Ja, die "Studiengebühren" (für das Erststudium) sind soweit ich weiß 
wieder abgeschafft. Es gibt allerdings noch "Verwaltungsgebühren" und 
Beiträge zum Studierendenwerk ...
z.B. an der Hochschule Ulm 
https://studium.hs-ulm.de/de/Seiten/Beitraege_Gebuehren.aspx?SearchCategory=Studienstart;

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Joachim B. schrieb:
> Deswegen zur besseren Lesbarkeit hier für die meisten User zur späteren
> Hilfestellung scheinen deutsche lesbare Vasiablen- und Funktionsnamen
> sinnvoll.

Meiner Ansicht nach hinkt Dein Vergleich. Man muss die Variablen- und 
Funktionsnamen ja nun nicht deshalb unbedingt auf türkisch schreiben.

Ich finde es zum Beispiel prägnanter...
1
void delay (int msec)
2
{
3
   ...
4
}
zu schreiben als:
1
void Verzoegerung (int msek)
2
{
3
   ...
4
}

Diesen Mischmasch aus deutschen Bezeichnernamen und englischen 
Schlüsselwörtern finde ich genauso anstrengend wie das Zuhören von 
türkischsprachigen Tischnachbarn :-)

Aber dem kann man ja Abhilfe schaffen:
1
#define Zeichen                      char
2
#define Hauptfunktion                main
3
#define Ganzzahl                     int
4
#define LangeGanzzahl                long    
5
#define Gleitkommazahl               float   
6
#define DoppeltGenaueGleitkommazahl  double  
7
#define Fuer                         for
8
#define Waehrend                     while
9
#define GebeZurueck                  return
10
11
Dann siehts so aus:
12
13
Ganzzahl Hauptfunktion ()
14
{
15
    Ganzzahl Zaehler;
16
    Zeichen Buchstabe;
17
18
    Fuer (Zaehler = 0; Zaehler < 100; Zaehler++)
19
    {
20
        Waehrend (ZeichenAusUartNichtBereit ())
21
        {
22
             WarteDochEinWeilchenUndPfeifSolangEinLiedchen ();
23
        }
24
        Buchstabe = ZeichenAusUartAuslesen ();
25
        ZeichenWiederAusgeben (Buchstabe);
26
    }
27
    GebeZurueck 0;
28
}

Mir wird gerade schlecht....

: Bearbeitet durch Moderator
von Volker S. (vloki)


Lesenswert?

Frank M. schrieb:
> Mir wird gerade schlecht....

Witzig, genau so was habe ich vor einigen Jahren für Kinder geschrieben:
1
#ifndef _DEUTSCH_H
2
#define _DEUTSCH_H
3
4
// Anschluesse
5
6
//allgemein
7
#define EINGANG     1
8
#define AUSGANG     0
9
...
10
11
// Befehle ???
12
#define solange     while
13
#define fuer        for
14
#define wenn        if
15
#define sonst       else
16
#define wiederhole  do
17
18
#define pruefe      switch
19
#define falls       case
20
#define fertig      break
21
#define falls_nichts default
22
23
#define fuerimmer()         while(1)
24
#define bleib_hier_stehen() while(1){;}
25
26
#define in_anderer_datei extern
27
28
#define zahl_0_255      unsigned char
29
#define zahl_0_65536    unsigned short
30
31
#define aufzaehlung     enum
32
#define typendefinition typedef
33
34
// Operatoren
35
//#define umgedreht ~
36
37
// Funktionen ???
38
#define hauptprogramm()       void main(void)
39
#define starteinstellungen()   void __init(void)
40
41
#define pause() _asm nop nop nop nop nop nop nop nop nop nop nop nop _endasm
42
...

Das Hauptprogramm sah dann so aus:
1
//void main()
2
hauptprogramm()
3
{
4
5
  tasten.ALLE = 0;  // Tastenbenachrichtigung löschen
6
  menue = FANGDENPUNKT;        // Startmenue festelegen
7
8
  fuerimmer(){
9
    wenn(tasten.T_S){LED_RGB = 0; tasten.T_S = 0; wenn(menue > 0) menue--;}// > erstes Menue
10
    wenn(tasten.T_K){LED_RGB = 0; tasten.T_K = 0; wenn(menue < (LETZTES_MENUE)) menue++;}
11
12
    pruefe (menue){
13
          falls DIGITALPOTI: digitalisieren(); fertig;
14
          falls HELLER_DUNKLER : hellerdunkler(); fertig;
15
          falls RGB: rotgruenblau(); fertig;
16
          falls ZAEHLEN: zaehlen(); fertig;
17
          falls MULTIPLIZIEREN: multiplizieren(); fertig;
18
          falls FARBEN: farbenmischen(); fertig;
19
          falls BILDER: bilder(); fertig;
20
          falls PUNKT: wanderpunkt(); punktanzeigen(); fertig;
21
          falls JOYSTICK: joystick(); positionanzeigen(); fertig;
22
          falls FANGDENPUNKT: zufall(); joystick(); fangdenpunkt(); fertig;
23
//          falls MALEN: malen(); fertig;
24
//          falls TEXT: text(); fertig;
25
//          falls LAUFSCHRIFT: laufschrift(); fertig;
26
//          falls TOENE: toeneabspielen(); fertig;
27
          falls_nichts: menue = 0; fertig;  // nur zur Sicherheit ;-)
28
    }
29
  }
30
}

von Joachim B. (jar)


Lesenswert?

Frank M. schrieb:
> Ich finde es zum Beispiel prägnanter...void delay (int msec)

ich auch, bin ganz deiner Meinung!

Frank M. schrieb:
> GebeZurueck 0;
> }
>
> Mir wird gerade schlecht....

mir auch!

aber wenn schon, denn schon

GEBE_ZURUECK = 0;

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:
> #define GebeZurueck                  return

Joachim B. schrieb:

> aber wenn schon, denn schon
>
> GEBE_ZURUECK = 0;

Nö.

  GebeZurueck 0;

ergibt zurückübersetzt:

  return 0;

Und das ist allemal besser als

  return = 0;

:-)

von Eric B. (beric)


Lesenswert?

Frank M. schrieb:
> Waehrend (ZeichenAusUartNichtBereit ())

Dann musst du aber auch Uaes schreiben statt Uart ;-)

von Mark B. (markbrandis)


Lesenswert?

Maxim B. schrieb:
> Mark B. schrieb:
>> Was um alles in der Welt soll "ismerenie" und "vyvod" bedeuten?
>>
> Als eingebürgerte Russe erlaube ich mir ab und zu auch mit russischen
> Worten hantieren :) Für mich sind sie verständlich :) Ich bin in
> Elektronik ein Liebhaber, schreibe eigentlich nur für mich selbst.
>
> ismerenie heißt Messen
> vyvod heißt Ausgabe.

Nun ja. So hast Du halt einen Mischmasch:

-manches ist auf Englisch, z.B. PWM_init
-manches ist auf Russisch, z.B. ismerenie
-manches ist auf Deutsch,  z.B. Null_volt

Wenn Du da noch durchblickst und es ein Projekt für Dich allein ist... 
okay.

Sobald mehr als ein Entwickler ins Spiel kommt (also im beruflichen 
Kontext fast immer), sollte man sich dann schon auf eine Sprache 
einigen und die dann auch konsequent überall verwenden.

von Joachim B. (jar)


Lesenswert?

Frank M. schrieb:
> Joachim B. schrieb:
>
>> aber wenn schon, denn schon
>>
>> GEBE_ZURUECK = 0;
>
> Nö.
>
>   GebeZurueck 0;

war ein Tippfehler, ich meinte doch

#define GEBE_ZURUECK return

GEBE_ZURUECK 0;

von W.S. (Gast)


Lesenswert?

Maxim B. schrieb:
> Ich denke, ich kann nicht richtig *.c und *.h zusammenbinden.

Es ist eigentlich ganz einfach. Mit einem Eintrag #include "dateiname" 
kannst du beliebige Dateien in deine C-Quelle einbinden. Ob sowas nun 
auf .h endet oder irgendwie anders, ist im Grunde völlig egal. Das *.h 
hat man sich lediglich angewöhnt.

Die Frage steht nun, was denn in so einer *.h Datei stehen muß. Antwort: 
Alles, was die aufrufenden Programme von der C-Datei wissen müssen, auf 
die sich die .h Datei bezieht. Und alles andere eben NICHT. Schreib also 
nur das Nötigste in eine .h Datei hinein. Ausgenommen sind sinnvolle 
Kommentare, die du oder ein anderer nach 5 Jahren immer noch versteht.

Beispiel: ein UART, also eine serielle Schnittstelle. Für sowas schreibt 
man zumeist einen separaten Handler, damit sich höhere Programmschichten 
nicht um die Einzelheiten der Schnittstelle kümmern müssen.
Im Treiber uart.c kommen z.B. vor:

void Char_Out(char c)
{ ...
}
und
char Char_In(void)
{ ...
}
und
void Init_Uart(long Baudrate)
{ ...
}

Also schreibt man sich eine Headerdatei uart.h, wo dann folgendes 
hineinkommt:

/* UART-Anschluss von Maxim */
#ifndef Maxims_Uart
#define Maxims_Uart
extern void Char_Out(char c);
extern char Char_In(void);
extern void Init_Uart(long Baudrate);
#endif

Wenn eine andere C-Quelle, z.B. burtsev.c jetzt diese uart.h Datei 
einbindet, dann kann man die Funktionen von uart.c dort in burtsev.c 
benutzen, weil ja der Compiler alles weiß, was er zum Aufrufen von 
Char_Out und so wissen muß. Obendrein weiß er, daß alle diese Funktionen 
nicht in der burtsev.c vorkommen, sondern woanders stehen. Er wird 
deswegen auch nicht meckern, daß ja die Funktionsrümpfe fehlen. Das 
macht dann viel später der Linker, falls ihm was fehlt.
Man bindet normalerweise die eigene .h auch in die C-Quelle mit ein, 
damit der Compiler die Konsistenz zwischen den Prototypen und den 
eigentlichen Funktionen prüfen kann. Das "extern" stört dabei nicht. Das 
mit dem #ifndef.. und #define dient dazu, bei Mehrfacheinbindung 
derselben Headerdatei keine eventuellen Duplikate zu bekommen. Ist 
normalerweise bei sauberer Programmierung überflüssig, aber ich mach 
sowas immer und aus Prinzip.

Und wenn dein Treiber so etwas wie ein struct benötigt, was auch die 
aufrufenden Programmteile kennen müssen, dann schreibt man die 
Definition dieses struct's in die .h Datei. Wenn das aber was internes 
ist, und alle anderen Programmteile davon nichts wissen müssen, dann 
schreibt man es in die C-Quelle und nicht in die zugehörige .h hinein, 
denn das würde nur den Rest der Welt verwirren.

Nochwas: Deine Frage ist durchaus KEINE Kinderfrage, sondern eine sehr 
ernste Frage.

W.S.

von Maxim B. (max182)


Lesenswert?

Vielen Dank für die Erklärungen!
Das ist für mich sehr, sehr wert!

Ich werde sicher nie ein Profi in diesem Bereich, mein Unterhalt 
verdiene ich an der Orgel - aber das alles macht mir viel Spaß!
Außerdem gibt es viele Kleinigkeiten, die nur ich alleine brauche oder 
nur wenige Kollege - dann ist das nicht zu kaufen und nur selbst zu 
basteln...

Mit *.h und *.c ist besonders wichtig, da die Klarheit hier auch die 
Bibliotheke von Anderen zu nutzen ermöglicht. So wie i2c oder für 
Indikator.

Vielen Dank!

Was die Sprache betrifft: innerlich sind das für mich keine zwei 
Sprachen, sondern eine große Sprache, die Russisch und Deutsch mit 
einschließt. Mit der Zeit geht es immer leichter, zwischen den Sprachen 
umzuschalten, und irgendwann gibt es die Grenze nicht mehr... Auch wenn 
bei mir herum in der Familie alles nur deutschsprachig ist... Da ich ein 
Musiker bin, war alles für mich dadurch einfacher, weil die Noten auf 
allen Sprachen gleich aussehen (nur in Buchstab-Notation gibt es 
Unterschiede zwischen Englischen und Deutschen: die Engländer schreiben 
komischerweise statt "H" "B" und statt "B" "B mit be" - das ist 
gewöhnungsbedürftig!. Im russissprachigen Raum nutzt man deutsche 
Variante (außer in Pop-Musik).

von Paul B. (paul_baumann)


Lesenswert?

Frank M. schrieb:
> WarteDochEinWeilchenUndPfeifSolangEinLiedchen ();

:))
Herrlich. Wenn die Syntax gleich von Anfanfg an so geplant gewesen wäre, 
würde niemand mehr eine andere Sprache benutzen wollen.

MfG Paul

von Rolf M. (rmagnus)


Lesenswert?

Maxim B. schrieb:
> Was die Sprache betrifft: innerlich sind das für mich keine zwei
> Sprachen, sondern eine große Sprache, die Russisch und Deutsch mit
> einschließt. Mit der Zeit geht es immer leichter, zwischen den Sprachen
> umzuschalten, und irgendwann gibt es die Grenze nicht mehr...

Naja, dann kannst du diese Fähigkeit nutzen, um Code zu schreiben, den 
nur wenige Leute verstehen.

> Musiker bin, war alles für mich dadurch einfacher, weil die Noten auf
> allen Sprachen gleich aussehen (nur in Buchstab-Notation gibt es
> Unterschiede zwischen Englischen und Deutschen: die Engländer schreiben
> komischerweise statt "H" "B"

Ich würde es umgekehrt sagen: Die Deutschen schreiben komischerweise 
statt "B" "H". Ich hab mich schon in der Grundschule gefragt, warum um 
alles in der Welt das H zwischen dem A und dem C steht und nicht das B.

von Max182 (Gast)


Lesenswert?

Rolf M. schrieb:
> warum um
> alles in der Welt das H zwischen dem A und dem C steht und nicht das B.

Weil die musikalische Tradition in Deutschland sehr lang und tief ist. 
Historisch war Tonleiter kein Dur oder moll, wie die meisten es heute 
kennen, sondern gerade A-B-C-D-E-F-G. "H" kam erst viel später in 
Gebrauch, deshalb hat nächste Zeichen bekommen, noch frei.
Andere länder (englichsprachigen) kennen keine so lange musikalische 
Tradition wie Deutschland. Sie haben alles übernommen, wie Tonleiter 
schon längst komplettiert wurde. Für die schien es einfacher, statt "H" 
"B" zu schreiben, was die Profi in D. eher als mangelhafte Ausbildung 
wahrnehmen.

von Spiegelneuronen (Gast)


Lesenswert?

Ich muss mal meinen Senf dazugeben, wenns OK ist...
Dieser Thread ist in meinen Augen der wichtigste zum Thema C 
überhaupt!!!
Hier wird etwas behandelt was ich schon bestimmt 1 Million mal falsch 
gemacht habe. Danke dafür!

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.