Forum: Compiler & IDEs AVR Studio4: Sourcefile zufügen, was beachten?


von DMC (Gast)


Lesenswert?

Hallo,

beim AVR Studio4 kann man ja zur "main.c"-Datei zusätzlich noch andere 
Source Files hinzufügen, was ja auch oft gemacht wird.
Dazu habe ich ein paar Fragen.

Warum wird das gemacht, um das Programm übersichtlicher zu halten oder 
gibt es noch andere Gründe?

Kann man einfach Funktionen in einem zusätzlichen Soure File 
unterbringen und von der Main-Funktion aus aufrufen - oder gibt es 
wichtige Punkte, die man vorher beachten muss?

Was ist mit Informationen zu Standardbibliotheken etc. wie z.B.
#include<stdio.h>, können die auch problemlos in einem zusätzlichen 
Soure File untergebracht werden?

von Karl H. (kbuchegg)


Lesenswert?

DMC schrieb:
> Hallo,
>
> beim AVR Studio4 kann man ja zur "main.c"-Datei zusätzlich noch andere
> Source Files hinzufügen, was ja auch oft gemacht wird.
> Dazu habe ich ein paar Fragen.
>
> Warum wird das gemacht, um das Programm übersichtlicher zu halten oder
> gibt es noch andere Gründe?

Zugegeben.
Gerade als Neuling wird es dir eher selten passieren, das deine 
Codegrößen ein paar 100 Zeilen überschreiten.

Aber irgendwann ist es auch bei dir soweit, dass die Programmgrößen 
zunehmen. Spätestens dann verspürst du den Drang, im Sinne der 
Reibeflächen deiner Maus auf die endlosen Scrollvorgänge durch den Code 
zu verzichten und statt dessen den Code nach Themenkreisen geordnet in 
einzelne C-Files aufzuteilen. Zb. alle Low-Level LCD Funktionen in eine 
C-Datei, in der nur diese Funktionen drinnen sind. Oder die Low-Level 
UART Funktionen. Oder ...
Angenehmer Nebeneffekt: Auf die Art entsteht ganz von alleine eine 
Sammlung von nützlichen Software-Baugruppen, die man ganz einfach von 
einem Projekt zum nächsten übernehmen kann. Denn die LCD Ansteuerung 
(eines Standard-HD44780 Text-LCD) ist ja da wie dort die gleiche. 
Lediglich andere Texte werden ausgegeben.

> Kann man einfach Funktionen in einem zusätzlichen Soure File
> unterbringen und von der Main-Funktion aus aufrufen - oder gibt es
> wichtige Punkte, die man vorher beachten muss?

Im C-File steht die Implementierung der Funktionen.
Im H-File (dem Header) stehen die Schnittstellendefinitionen, also die 
'Kennungen' der Funktionen.

Der verwendende Code includiert das Header File und kann damit die 
Funktionen aufrufen. Der Compiler weiß, dass es die Funktionen gibt und 
welche Parameter diese haben. Alles weitere macht dann der Linker, der 
die einzelenen, bereits übersetzten Code-Blöcke zum kompletten Programm 
zusammenbaut.

> Was ist mit Informationen zu Standardbibliotheken etc. wie z.B.
> #include<stdio.h>, können die auch problemlos in einem zusätzlichen
> Soure File untergebracht werden?

Wozu soll das gut sein?
im Header File (erkennbar an der Endung h) ist im Regelfall sowieso kein 
COde direkt enthalten. Das Header File dient als Beschreibung dessen, 
was an Code an anderer Stelle verfügbar ist! Aber der eigentliche Code, 
der eine Funktion implementiert ist (bis auf Ausnahmefälle) da nicht 
drinnen.

von Karl H. (kbuchegg)


Lesenswert?


von DMC (Gast)


Lesenswert?

Danke für die ausführliche Antwort!!!

Ich verstehe es so, dass ich dann problemlos in anderen .c-Source-Files 
problemlos Funktionen unterbringen kann, die dann von main.c aus 
aufgerufen werden können!?!

von Bitflüsterer (Gast)


Lesenswert?

DMC schrieb:
> Danke für die ausführliche Antwort!!!
>
> Ich verstehe es so, dass ich dann problemlos in anderen .c-Source-Files
> problemlos Funktionen unterbringen kann, die dann von main.c aus
> aufgerufen werden können!?!

Problemlos? Nö. Da gibt es noch genug Probleme.
Aber für's Erste kannst Du das problemlos so als problemlos gegeben 
nehmen.

von DMC (Gast)


Lesenswert?

Bitflüsterer schrieb:
> Problemlos? Nö. Da gibt es noch genug Probleme.
> Aber für's Erste kannst Du das problemlos so als problemlos gegeben
> nehmen.

Dann werde ich das mal testen und der Probleme harren, die da kommen ;O)

von DMC (Gast)


Lesenswert?

Ich habe jetzt mal spaßeshalber eine Funktion, die Zahlen über den UART 
ausschmeißt, in ein zweites Sourcefile gesteckt (uartzahl.c).

Das gab viele Fehlermeldungen.

Vor allem konnte "uint8_t" in uartzahl.c nicht mehr vom Compiler erkannt 
werden.
Habe dann das #include<avr/io.h> von mai.c nach uartzahl.c rüberkopiert 
und jetzt geht es.
Warum es jetzt geht, verstehe ich allerdings nicht wirklich.

von DMC (Gast)


Lesenswert?

DMC schrieb:
> mai.c

= main.c

von DingsDa (Gast)


Lesenswert?

DMC schrieb:
> "uint8_t" in uartzahl.c nicht mehr vom Compiler erkannt
> werden.

Weil uint8_t eine Definition ist, die in dem Headerfile erfolgt.
Ohne diesen Header kennt der Compiler den Typ nicht.
Schau Dir mal die io.h an, dann siehst Du es.

von Bitflüsterer (Gast)


Lesenswert?

DMC schrieb:
> Ich habe jetzt mal spaßeshalber eine Funktion, die Zahlen über den UART
> ausschmeißt, in ein zweites Sourcefile gesteckt (uartzahl.c).
>
> Das gab viele Fehlermeldungen.
>
> Vor allem konnte "uint8_t" in uartzahl.c nicht mehr vom Compiler erkannt
> werden.
> Habe dann das #include<avr/io.h> von mai.c nach uartzahl.c rüberkopiert
> und jetzt geht es.
> Warum es jetzt geht, verstehe ich allerdings nicht wirklich.

Der wesentliche Verständnispunkt hier ist, dass jede C-Datei dem 
Compiler einzeln vorgelegt wird. Wird die Datei uartzahl.c compiliert, 
dann wird dabei nicht berücksichtigt, dass Du in der Datei main.c (oder 
auch irgendeiner anderen C-Datei) schon die Zeile beginnend mit 
"#include ..." eingefügt hast. Auch spielt es keine Rolle, ob und das 
der Compiler (evtl. sogar unmittelbar vorher) die Datei main.c übersetzt 
hat.

Jede C-Datei ist eine Einheit für sich.

Dabei spielt auch keine Rolle, ob in einer Datei eine Funktion aus einer 
anderen Datei aufgerufen wird.

Karl Heinz kann das viel schöner erklären und hat auch mehr Übung darin. 
:-)

von Bitflüsterer (Gast)


Lesenswert?

Damit nun aus einer Reihe verschiedener C-Dateien, die Funktionen 
enthalten (das ist mindestens main), die wiederrum Funktionen aus 
anderen C-Datein enthalten sind, am Ende ein Programm herauskommt, dass 
auch funktioniert, wird als letztes noch der sogenannte Linker 
aufgerufen.

Wenn nämlich in einer C-Datei eine Funktion aufgerufen wird, die in 
derselben C-Datei nicht enthalten ist, dann erzeugt der Compiler an der 
Stelle, an der die Adresse dieser Funktion eigentlich stände (und am 
Ende dann schliesslich stehen wird) einen Platzhalter.

Wenn hingegen eine C-Datei eine Funktion enthält, egal ob sie nirgends 
in der selben Datei aufgerufen wird oder nicht (es sei denn sie ist 
static deklariert), dann fügt der Compiler eine Information hinzu, die 
vom Namen der Funktion (und den Parametern) abgeleitet einen Hinweis auf 
die 'vorläufige' Adresse der Funktion hinleitet. Vorläufig, weil ja die 
endgültige Adresse der Funktion im fertigen Programm zu dem Zeitpunkt 
noch nicht bekannt sein kann.

Der Linker nun, ist das Programm, dass sich alle Dateien anschaut 
(diese Objekt-Dateien genannten Files [oft mit der Endung o.] enthalten 
wiegesagt enweder Platzhalter für Adressen von externen Funktionen 
oder/und Tabellen mit den relativen Adressen von Funktionsdefinitionen. 
Der Linker ersetzt, in dem er in diesen Tabellen nachschaut, die 
Platzhalter durch die realen Adressen; zusätzlich berücksichtigt er aber 
auch noch, wo im fertigen Programm die fraglichen Funktionen zu liegen 
kommen (welche realen Adressen sie erhalten).

von DMC (Gast)


Lesenswert?

Vielen herzlichen Dank für die ausführlichen Erklärungen!
Das muss ich erst mal sacken lassen!

Immerhin gibt der UART in uartzahl.c mit
UDR0 = zahl;
alles klaglos aus, obwohl
der UART selber in main.c initialisiert wurde.

Ansonsten bekomme ich zu uartzahl.c eine Warnung:
return type defaults to 'int'
Die Zeile, auf die sich die Warnung bezieht, lautet:
uartausgabe(uint64_t ausgabezahl) <-- Warnung
{...; return 0;}

(nicht über die Verwendung von uint64_t wundern, ich gebe damit wirklich 
große Dezimalzahlen von einem Frequenzzähler aus)

von Stefan E. (sternst)


Lesenswert?

DMC schrieb:
> Die Zeile, auf die sich die Warnung bezieht, lautet:
> uartausgabe(uint64_t ausgabezahl) <-- Warnung

Du hast keinen Typ für den Rückgabewert angegeben, daher
> return type defaults to 'int'

von DMC (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Du hast keinen Typ für den Rückgabewert angegeben, daher
>> return type defaults to 'int'

Danke für die schnelle Antwort!

Wie gibt man in dem Fall günstigerweise für Typ für den Rückgabewert an 
(der ja von meiner Seite eigentlich nicht gebraucht wird)?

von Bitflüsterer (Gast)


Lesenswert?

DMC schrieb:
> Stefan Ernst schrieb:
>> Du hast keinen Typ für den Rückgabewert angegeben, daher
>>> return type defaults to 'int'
>
> Danke für die schnelle Antwort!
>
> Wie gibt man in dem Fall günstigerweise für Typ für den Rückgabewert an
> (der ja von meiner Seite eigentlich nicht gebraucht wird)?

Jetzt ist aber dann doch die Gelegenheit für beliebten Hinweis auf ein 
C-Buch gekommen.

Wenn Du keinen Wert zurückgeben willst, dann ist 'void' der Datentyp.

von DMC (Gast)


Lesenswert?

Prima, danke, ich entsinne mich.

Habe auch meine C-Bücher wieder rausgekramt, Danke für den Hinweis!

Noch mal zum AVR-Studio.

Dann macht es möglicherweise wenig Sinn, Dinge wie den ADC, die 
PWM-Ausgabe, Interrupts oder ähnliches außerhalb von main.c zu 
initialisieren (also in einer anderen verlinkten Sourcecode-c.-Datei)???

von DMC (Gast)


Lesenswert?

Noch mal meine Frage, macht es möglicherweise wenig Sinn, Dinge wie den 
ADC, die PWM-Ausgabe, Interrupts oder ähnliches außerhalb von main.c zu
konfigurieren (also in einer anderen Source-Code-Datei als uartzahl.c)?

Oder spielt das hier keine Rolle, weil die benutzten Register (z.B. 
ADMUX beim ADC) so oder so im Controller eingetragen werden?

von npn (Gast)


Lesenswert?

Ich mache es normalerweise immer so, daß ich beispielsweise eine uart.c 
habe, in der sich dann sämtliche Funktionen befinden, die die UART 
betreffen. Unter anderem auch die Initialisierung der UART. Und mit den 
anderen Baugruppen genauso, z.B. die Timer, den ADC, die SPI usw.
Das hindert dich ja dann nicht daran, die Konfiguration während des 
Programmlaufes zu verändern (z.B. steigende auf fallende Flanken 
umschalten und ähnliches).

von Bitflüsterer (Gast)


Lesenswert?

DMC schrieb:
> Noch mal meine Frage, macht es möglicherweise wenig Sinn, Dinge wie den
> ADC, die PWM-Ausgabe, Interrupts oder ähnliches außerhalb von main.c zu
> konfigurieren (also in einer anderen Source-Code-Datei als uartzahl.c)?
>
> Oder spielt das hier keine Rolle, weil die benutzten Register (z.B.
> ADMUX beim ADC) so oder so im Controller eingetragen werden?

Letzteres. Es spielt technisch gesehen keine Rolle, in welcher C-Datei 
welche Funktion steht (in Bezug auf die Behandlung von Peripherie).

Die Tatsache, dass man mehrere C-Dateien zu einem Programm zusammenfügen 
kann, wird im allgemeinen dazu genutzt, etwa alle ADC-relevanten 
Funktionen, alle PWM-relevanten Funktionen etc. in eigene Dateien 
zusammenzufassen. Das gilt für die Initialisierungsfunktionen genauso 
wie für Funktionen die das Resultat auslesen, den PWM-Duty-Cylce 
verändern oder ähnliches.

Die jeweiligen Interrupt-Funktionen für ADC, PWM etc. kann man auch 
einzeln in die jeweilige Datei schreiben. Dafür spricht, dass fast immer 
auch Variablen beteiligt sind, die etwa den ADC-Wert aufnehmen und die 
vorzugsweise in der Datei definiert werden sollte in der auch die 
restlichen ADC-relevanten Funktionen enthalten sind.

Das alles sind allerdings keine festen Regeln. Wichtiger ist fast, dass 
man überhaupt eine Ordnung hat. Aber das spielt natürlich auch alles 
erst eine Rolle, wenn die Projekte grösser werden. Wenn etwa ein 
ADC-Wert nur an einer Stelle z.B. ausgegeben wird, dann wäre es 
überflüssig ordentlich, diese Variable nun in einer besonderen Datei zu 
definieren. Genauso, ist es nicht wirklich nötig, die 
Initialisierungsfunktion für den UART auszulagern, wenn das die einzige 
Peripherie ist, die man überhaupt nutzt.
Allenfalls ergeben sich solche Fälle wenn man schon einen Satz an 
C-Dateien hat, den man für andere Projekte verwendet hat. Dann braucht 
man das alles nicht erst umzukopieren.



P.S. Sinn wird nicht "gemacht". Etwas hat einen Sinn oder nicht.

von DMC (Gast)


Lesenswert?

Vielen herzlichen Dank für die ausführliche Antwort!!!

Bitflüsterer schrieb:
> Die Tatsache, dass man mehrere C-Dateien zu einem Programm zusammenfügen
> kann, wird im allgemeinen dazu genutzt, etwa alle ADC-relevanten
> Funktionen, alle PWM-relevanten Funktionen etc. in eigene Dateien
> zusammenzufassen.

Das ist ein guter Ansatz, werde das auch so versuchen für die Zukunft.

Bitflüsterer schrieb:
> Die jeweiligen Interrupt-Funktionen für ADC, PWM etc. kann man auch
> einzeln in die jeweilige Datei schreiben. Dafür spricht, dass fast immer
> auch Variablen beteiligt sind, die etwa den ADC-Wert aufnehmen und die
> vorzugsweise in der Datei definiert werden sollte in der auch die
> restlichen ADC-relevanten Funktionen enthalten sind.

Also wie bei main.c außerhalb des Funktionsblocks?!

Für ein Interrupt benötigt man volatile Variablen, wenn sie außerhalb 
des Interrupts verwendet werden sollen (so weit ich weiß). Wo ist dann 
der Vorteil?

Bitflüsterer schrieb:
> P.S. Sinn wird nicht "gemacht". Etwas hat einen Sinn oder nicht.

Da hast du wahr!

Sinn ist eine digitale Größe wie 0 und 1:

0 = Unsinn
1 = Sinn
;O)

von Bitflüsterer (Gast)


Lesenswert?

DMC schrieb:
> Vielen herzlichen Dank für die ausführliche Antwort!!!
> Bitflüsterer schrieb:
>> Die jeweiligen Interrupt-Funktionen für ADC, PWM etc. kann man auch
>> einzeln in die jeweilige Datei schreiben. Dafür spricht, dass fast immer
>> auch Variablen beteiligt sind, die etwa den ADC-Wert aufnehmen und die
>> vorzugsweise in der Datei definiert werden sollte in der auch die
>> restlichen ADC-relevanten Funktionen enthalten sind.
>
> Also wie bei main.c außerhalb des Funktionsblocks?!

So ungefähr. Jedenfalls nicht lokal in einer Funktion. (Es gibt aber 
andererseits auch sinnvolle Anwendungen von Funktionslokalen Variablen. 
Ich erwähne das nur der vollständigkeit halber und empfehle das Studium 
der Abschnitte über Variablen-Lebensdauer resp. Scope).

> Für ein Interrupt benötigt man volatile Variablen, wenn sie außerhalb
> des Interrupts verwendet werden sollen (so weit ich weiß). Wo ist dann
> der Vorteil?

Ich hoffe ich verstehe die Frage richtig: Es geht darum, die Variablen 
zu den Funktionen passend zu gesellen. So ist etwa, der in einer ISR 
ausgelesene AD-Wandlungswert ein Beispiel für einen volatile Variable, 
die  man in dem C-File, mit der ISR definieren könnte. Meiner Meinung 
nach, sollte man das auch so tun, auch wenn die Variable noch in anderen 
Dateien gelesen wird. Das ist zu einem gewissen Grad willkürlich aber 
nicht eigentlich unsinnig. Strenge Argumente gibt es nicht dafür. 
Technisch ginge es auch andersherum.

von DMC (Gast)


Lesenswert?

Noch mal Danke für die ausführlichen Antworten und guten Erklärungen!
Ja, ich meinte es so mit den Variablen in einer ISR.


Noch mal ein kleiner Sprung zurück:
1
void uartausgabe(uint64_t ausgabezahl)  
2
{...; return 0;}

Nachdem ich jetzt in uartzahl.c der Funktion ein "void" verpasst habe, 
ergibt sich nun in main.c eine Warnung beim Compilieren, die bei "Built" 
aber wieder verschwindet:

warning: implicit declaration of function 'uartausgabe'

Kann man feststellen, woran das liegt?

von Bitflüsterer (Gast)


Lesenswert?

> Kann man feststellen, woran das liegt?

Es ist ja schon festgestellt. Die Feststellung wird Dir als Warnung 
ausgegeben.

Du hast irgendwo einen Widerspruch. Die Warnung bezieht sich auf eine 
bestimmte Zeile. Diese Zeile musst Du Dir anschauen. Und bei dieser 
spezifischen Warnunge alle anderen Stellen an denen der Funktionsname 
erscheint. Ausserdem die Reihenfolge.

Alle Deklarationen, Definitionen und Verwendungen müssen konsistent 
sein. Z.B. ist es sinnlos in einer Funktion die 'return'-Anweisung mit 
eine Parameter zu verwenden, wenn die Funktion selbst mit 'void' 
definiert ist, denn das heisst ja das sie keinen Wert zurückgibt. 
Nirgendwo sonst darf diese Funktion bzw. deren Verwendung implizit oder 
explizit einen Rückgabewert festlegen oder erwarten. Aktiviere am besten 
mit -Wall alle Warnungen.

von Bitflüsterer (Gast)


Lesenswert?

Die Warnungen sind eigentlich (in der Regel) genügend aussagekräftig. 
Aber man muss schon ein wenig Englisch können oder eben bei Leo oder im 
Wörterbuch nachschauen. Dann auch ggf. im C-Buch.

warning: implicit declaration of function 'uartausgabe'

Auf Deutsch: Implizite Deklaration der Funktion 'uartausgabe'

Jetzt muss man wissen, was eine "Implizite Deklaration" ist. Das steht 
im C-Buch. Im wesentlichen heisst dass, das der Compiler, wenn Du beim 
Aufruf oder der Deklaration oder der Definition den Rückgabetyp nicht 
ausdrücklich nennst (dazu zählt auch void), ein 'int' annimmt.

von DMC (Gast)


Lesenswert?

Danke für die Erklärungen!!!

Dazu habe ich zwei Fragen.

1. Warum muss man bei void (also wenn kein Rückgabewert gewollt ist) 
trotzdem den Rückgabetyp angeben.

2. Wie kann man konkret Abhilfe schaffen, damit der Compiler keine 
Warnung diesbezüglich mehr ausgibt?


Bitflüsterer schrieb:
> Aktiviere am besten
> mit -Wall alle Warnungen.

Schon geschehen!

von Karl H. (kbuchegg)


Lesenswert?

DMC schrieb:

> 1. Warum muss man bei void (also wenn kein Rückgabewert gewollt ist)
> trotzdem den Rückgabetyp angeben.

Weil an dieser Stelle das Schlüsselwört 'void' nun mal genau diese 
Bedeutung hat.

Etwas weiter ausgeholt hat das ganze historische Gründe. Im ganz frühen 
C war die Sache so, dass der Compiler für so gut wie alles was er nicht 
kannte, erst mal den Datentyp int annehmen musste.
'void' als Kennung für 'nichts bestimmtes' ist erst sehr viel später 
Sprachbestandteil geworden.

> 2. Wie kann man konkret Abhilfe schaffen, damit der Compiler keine
> Warnung diesbezüglich mehr ausgibt?

Indem man die Ursache abstellt.


Eine implizite Deklaration einer Funktion tritt an der verwendenden 
Stelle auf. Wenn das dein Code ist
1
int main()
2
{
3
  uart_init();
4
}
und der Compiler keinen Protoypen der Funktion vor der Verwendung der 
Funktion präsentiert bekommen hat, dann muss er die Funktion 'implizit 
deklarieren'. D.h. er muss Annahmen treffen. Eine dieser Annahmen ist 
zb, dass eine Funktion einen int zurückliefert.
Diese Annahme kann richtig sein, sie kann aber auch falsch sein. Daher 
warnt dich der Compiler.

Die Warnung kann man also auch so übersetzen:
Hör mal. Ich soll da eine Funktion aufrufen. Aber ich weiß nichts von 
der Funktion. Daher geh ich mal mit den Standardannahmen an die Sache 
ran, auch wenn die falsch sein mögen. Kontrollier das bitte und am 
besten präsentierst du mir vor der Verwendung der Funktion erst mal 
einen Funktionsprotoypen, dann brauch ich nicht raten.

In deinem Fall, in dem der Protoyp der Funktion im Header File uart.h 
steht
1
#ifndef UART_H_INCLUDED_
2
#define UART_H_INCLUDED_
3
4
void uart_init( void );
5
6
#endif

bedeutet das, das du einfach einen entsprechenden #include in dein File 
reinmachst
1
#include "uart.h"
2
3
int main()
4
{
5
  uart_init();
6
}

dann wird dem Compiler der Protoyp der Funktion präsentiert, ehe es zum 
Funktionsaufruf kommt, der Compiler kennt dann die Signatur der Funktion 
und muss nicht mehr mit Standardannahmen raten -> die Warnung ist weg.

: Bearbeitet durch User
von Michael (Gast)


Lesenswert?

DMC schrieb:
> Warum muss man bei void (also wenn kein Rückgabewert gewollt ist)
> trotzdem den Rückgabetyp angeben.

Weil das in C so ist. Nimm es hin.

von Karl H. (kbuchegg)


Lesenswert?

Ein anderer Fall, der aber ganz ähnlich gelagert ist, ist ein ganz 
einfaches Reihenfolgeproblem.
Der Compiler liest den Quellcode von oben nach unten. Und zwar nur ein 
einziges mal.

Hast du im Code
1
void foo()
2
{
3
  bar();
4
}
5
6
void bar()
7
{
8
}
dann hast du ein ähnlich gelagertes 'implicite declration' Problem.
Von oben nach unten gelesen erfolgt die Verwendung der Funktion (also 
der Aufruf) bevor die Funktion definiert wird. Der Compiler kann also 
zum Zeitpunkt des Funktionsaufrufs noch nicht wissen, wie die Funktion 
bar aussieht. Die wird ja erst danach definiert.

Die Abhilfe ist einfach. Es gibt 2 Möglichkeiten.
* Dreh die Reihenfolge der Funktionen um
1
void bar()
2
{
3
}
4
5
void foo()
6
{
7
  bar();
8
}
so dass die Signatur der Funktion bar bekannt ist, noch ehe im Quelltext 
der Aufruf der Funktion erfolgt.

* Oder aber:
präsentier dem Compiler einen Prototypen für die Funktion vor der 
Verwendung der Funktion
1
void bar( void );
2
3
void foo()
4
{
5
  bar();
6
}
7
8
void bar()
9
{
10
}


Letzten Endes liegt bei einer 'implicite declaration' die immer gleiche 
Ursache vor: Eine Funktion wird verwendet, von der die Aufrufsignatur 
nicht bekannt ist (wieviele Argumente nimmt die Funktion, welchen 
Datentyp haben die Argumente, wie ist der Returntyp der Funktion). Stell 
das ab und die Warnung verschwindet. Das das eine Warnung ist und kein 
echter Fehler hat historische Ursachen und ist dem konservativen 
Vorgehen der Normungsgremien geschuldet, die nur sehr ungern wesentliche 
Sprachbestandteile ändern, wenn das bedeutet, das alter Code (selbst 
wenn er falsch war) dadurch nicht mehr compilieren würde.

: Bearbeitet durch User
von Bitflüsterer (Gast)


Lesenswert?

DMC schrieb:
> Danke für die Erklärungen!!!
>
> Dazu habe ich zwei Fragen.
>
> 1. Warum muss man bei void (also wenn kein Rückgabewert gewollt ist)
> trotzdem den Rückgabetyp angeben.

Falls Du einen Compiler nach und einschliesslich C99 benutzt ist 
implizite Deklaration ohnehin nicht mehr erlaubt. Du musst grundsätzlich 
den Typ des Rückgabewertes angeben. Selbst als das noch erlaubt war, hat 
das kaum einer so gemacht.

Das habe ich schon erklärt. Ergänzend kannst Du mal im Kernighan & 
Ritchie, 2. Auflage ANSI C, S. 71 nachschauen. Aber beachte: Das ist ein 
alter Standard. Das ist nur noch zur Erläuterung, worum es sich handelt.

> 2. Wie kann man konkret Abhilfe schaffen, damit der Compiler keine
> Warnung diesbezüglich mehr ausgibt?

Es ist sehr davon abzuraten, Warnungen abzustellen oder zu ignorieren, 
falls das Dein Hintergedanke ist. Für den Anfänger gilt das, meiner 
Ansicht nach, auf jeden Fall!

von Bitflüsterer (Gast)


Lesenswert?

Oh. Da habe ich wohl zu lange getippt.

von Karl H. (kbuchegg)


Lesenswert?

Bitflüsterer schrieb:

> Es ist sehr davon abzuraten, Warnungen abzustellen oder zu ignorieren,
> falls das Dein Hintergedanke ist. Für den Anfänger gilt das, meiner
> Ansicht nach, auf jeden Fall!

Auf jeden Fall.
Für den Profi hingegen gilt: setz den Warning-Level so hoch wie es nur 
geht. Du willst jede Warnung haben, damit du die Ursache im Code finden 
und abstellen kannst.
(ok, bei ein paar Warnungen hat insbesondere Microsoft meiner Meinung 
nach ein wenig übertrieben, die stell ich auch einzeln ab. Aber generell 
gilt gerade für Profis: Warning Level rauf. Warnings werden wie Fehler 
behandelt und müssen im Code korrigiert werden. Produktionscode muss 
komplett ohne Fehler (eh klar) und Warnungen compilieren.

: Bearbeitet durch User
von Bitflüsterer (Gast)


Lesenswert?

Karl Heinz schrieb:
> Bitflüsterer schrieb:
>
>> Es ist sehr davon abzuraten, Warnungen abzustellen oder zu ignorieren,
>> falls das Dein Hintergedanke ist. Für den Anfänger gilt das, meiner
>> Ansicht nach, auf jeden Fall!
>
> Auf jeden Fall.
> Für den Profi hingegen gilt: setz den Warning-Level so hoch wie es nur
> geht. Du willst jede Warnung haben, damit du die Ursache im Code finden
> und abstellen kannst.

Huch? Das ist doch das was ich meinte!
Ich rate in meinem Beitrag davon ab, die Warnungen abzustellen. Und die 
implizite Einschränkung bei Nicht-Anfängern ist, auch enthalten, wenn 
auch nicht so ausgeführt wie bei Dir.

> bei ein paar Warnungen ..., die stell ich auch einzeln ab

Was genau bewog Dich zu Deinem Beitrag?

von Bitflüsterer (Gast)


Lesenswert?

Ach. Ich glaube ich ahne was. Du meinst den Default-Warn-Level.

Naja. Ich habe noch im Kopf gehabt, dass ich dem TO hier: 
Beitrag "Re: AVR Studio4: Sourcefile zufügen, was beachten?" geraten habe mit 
-Wall alle Warnungen einzuschalten.

@ TO:
Dazu muss man wissen, dass per Default nicht alle Warnungen aktiviert 
sind. Wenn ich geschrieben habe, dass man keine Warnungen abstellen oder 
ignorieren soll, dann habe ich vorausgesetzt, dass alle Warnungen 
angestellt sind.

von Karl H. (kbuchegg)


Lesenswert?

Bitflüsterer schrieb:
> Ach. Ich glaube ich ahne was. Du meinst den Default-Warn-Level.

Ja, genau.
Ich wollte nur unterstreichen, dass die Sache mit dem Ignorieren oder 
gar Abstellen von Warnungen nicht nur für Anfänger gilt.
So quasi nach dem Muster: Als Anfänger nimmst du alle Warnungen mit. 
Später, wenn du dann besser wirst, lockerst du das Ganze wieder.

Das spielt es so nicht. Auch Profis legen sich die Warning Latte so 
hoch, wie sie nur können. Und das aus gutem Grund.

von Bitflüsterer (Gast)


Lesenswert?

Naja. Um recht zu behalten müsste ich jetzt belegen, dass das für Profis 
nur bedingt gilt. Aber das tust Du ja schon selbst, in dem Du schreibst, 
das Du einige Warnungen durchaus abstellst. Und was anderes habe ich ja 
auch nicht geschrieben.

Der Satz "Für den Anfänger gilt das, meiner Ansicht nach, auf jeden 
Fall!"
bedeutet ja nicht, das es für Profis auf keinen Fall gilt, sondern das 
es für Profis in gewissen Fällen nicht gilt.

von DMC (Gast)


Lesenswert?

Vielen Dank für die professionellen Antworten!!!

DMC schrieb:
> 2. Wie kann man konkret Abhilfe schaffen, damit der Compiler keine
> Warnung diesbezüglich mehr ausgibt?

Das sollte bedeuten:
Wie kann man die Ursache ausräumen, damit der Compiler keine
Warnung diesbezüglich mehr ausgibt?


Im Moment reden reden wir, glaube ich, teilweise etwas aneinander 
vorbei.


Die Funktion
>>uartausgabe(uint64_t ausgabezahl)
befindet sich in der Datei
>>uartzahl.c
(also in einer.c-Datei und nicht in einer .h-Datei).


Kann ich jetzt dem Compiler mitteilen ... (s.u.) ?
1
#ifndef UARTZAHL_C_INCLUDED_
2
#define UARTZAHL_C_INCLUDED_
3
4
void uartausgabe( void );
5
6
#endif




Karl Heinz schrieb:
> Die Abhilfe ist einfach. Es gibt 2 Möglichkeiten.
> * Dreh die Reihenfolge der Funktionen um
>
>void bar()
> {
> }
>
> void foo()
> {
>   bar();
> }
> so dass die Signatur der Funktion bar bekannt ist, noch ehe im Quelltext
> der Aufruf der Funktion erfolgt.

Das ist hier nicht das Mittel der Wahl, weil die genannte Funktion ja in 
einer anderen .c-Datei stehen soll.



Karl Heinz schrieb:
> * Oder aber:
> präsentier dem Compiler einen Prototypen für die Funktion vor der
> Verwendung der Funktion
>
>void bar( void );
>
> void foo()
> {
>   bar();
> }
>
> void bar()
> {
> }

Das mit der Prototypenpräsentation für die Funktion klingt gut!

Schreibt man diese Präsentation
>>void uartausgabe( void );
normalerweise einfach in den "leeren" Raum zwischen Header und 
main-Funktion?

von Horst (Gast)


Lesenswert?

DMC schrieb:
> Schreibt man diese Präsentation
>>>void uartausgabe( void );
> normalerweise einfach in den "leeren" Raum zwischen Header und
> main-Funktion?

Nein. Man schreibt sie in eine H-Datei und inkludiert die H-Datei am 
Anfang der C-Datei.

Jeder der eine Funktion aus einer anderen C-Datei aufrufen will, 
inkludiert die zu dieser C-Datei gehörende, gleichnamige H-Datei. Dort 
stehen alle Prototypen der Funktionen, die sich in in der C-Datei 
befinden.

Nebenbemerkung: Die H-Datei inludiert man auch in die dazugehörige 
C-Datei. Damit kann der Compiler überprüfen, ob der Prototyp auch mit 
der richtigen Funktionsdefinition übereinstimmt und andernfalls davor 
warnen.

von Bitflüsterer (Gast)


Lesenswert?

DMC schrieb:
> Vielen Dank für die professionellen Antworten!!!
>
> DMC schrieb:
>> 2. Wie kann man konkret Abhilfe schaffen, damit der Compiler keine
>> Warnung diesbezüglich mehr ausgibt?
>
> Das sollte bedeuten:
> Wie kann man die Ursache ausräumen, damit der Compiler keine
> Warnung diesbezüglich mehr ausgibt?
>
>
> Im Moment reden reden wir, glaube ich, teilweise etwas aneinander
> vorbei.

Den Eindruck habe ich auch. Denn die Frage ist Dir nun schon auf drei 
verschiedene Arten von mindesten zwei verschiedenen Personen beantwortet 
worden.

Was genau hast Du an: "Schreibe keine impliziten Deklarationen (d.h. 
lasse den Datentyp nicht weg)" nicht verstanden?

> Die Funktion
>>>uartausgabe(uint64_t ausgabezahl)
> befindet sich in der Datei
>>>uartzahl.c
> (also in einer.c-Datei und nicht in einer .h-Datei).
>
>
> Kann ich jetzt dem Compiler mitteilen ... (s.u.) ?
>
>
1
> #ifndef UARTZAHL_C_INCLUDED_
2
> #define UARTZAHL_C_INCLUDED_
3
> 
4
> void uartausgabe( void );
5
> 
6
> #endif
7
>
>

Nein. Wozu willst Du zwei widersprechende Deklarationen im Programmtext 
haben? Das gibt dann Warnings. Das ist aber auch schon erwähnt worden.

>
>
> Karl Heinz schrieb:
>> Die Abhilfe ist einfach. Es gibt 2 Möglichkeiten.
>> * Dreh die Reihenfolge der Funktionen um
>>
>>void bar()
>> {
>> }
>>
>> void foo()
>> {
>>   bar();
>> }
>> so dass die Signatur der Funktion bar bekannt ist, noch ehe im Quelltext
>> der Aufruf der Funktion erfolgt.
>
> Das ist hier nicht das Mittel der Wahl, weil die genannte Funktion ja in
> einer anderen .c-Datei stehen soll.

Das hat Karl Heinz auch nicht gemeint. Er wollte Dir klarmachen, welche 
Rolle die Reihenfolge von Defintionen bzw. Deklarationen in einer 
Datei bzw. in einer C- und einer H-Datei spielen.

> Karl Heinz schrieb:
>> * Oder aber:
>> präsentier dem Compiler einen Prototypen für die Funktion vor der
>> Verwendung der Funktion
>>
>>void bar( void );
>>
>> void foo()
>> {
>>   bar();
>> }
>>
>> void bar()
>> {
>> }
>
> Das mit der Prototypenpräsentation für die Funktion klingt gut!

Schön.

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.