mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik externe Deklaration in anderem C-File; so richtig ?


Autor: Jungspunt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
habe folgende Frage. Möchte ein Programm schreiben sagen wir "main.c"
. Nun möchte ich ein zweites C-File haben wie "Library.c" worin ich
konstante Werte definieren möchte.
darin deklariere und initialisiere ich nun die Werte wie z.B.

"const int Wert_A = 5;"

im dazugehörigen Header "Library.h" schreibe ich nun

extern const int Wert A;  und binde dieses Headerfile in die "main.c"
per #include ein.

Somit kann ich den "Wert_A" auch in "main.c" benutzen.

Kann das jemand bestätigen oder gegebenenfalls sagen wie es richtig
ist?

mfg

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, das sieht korrekt aus.
Du musst natürlich daran denken, Deine Datei "library.c" zum Projekt
hinzuzufügen - also ins Makefile eintragen oder der Projektverwaltung
Deiner Entwicklungsumgebung hinzufügen, damit die Datei auch vom
Compiler übersetzt und vom Linker zum fertigen Programm gelinkt wird.

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sieht gut aus.

Autor: Stefan Kleinwort (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

Du legst auf diese Art eine Variable an, die den Wert 5 erhält. Wenn
Wert_A benutzt wird, wird auch diese Variable aus dem RAM geladen.

Die andere Möglichkeit ist die Definition als "echte" Konstante: im
Headerfile einfach deklarieren:

#define Wert_A 5

Der Unterschied:
Bei Berechnungen wird nicht die Variable aus dem RAM geholt, sondern
eine Konstante in den Assemblercode eingesetzt. Du sparst Dir den
RAM-Platz der Variable. Zusätzlich kann der Compiler optimieren: so
würde:

  if (Wert_A == 5){
    while(1);
  }

zu

  while(1);

optimiert werden, weil der Compiler weiss, dass
if (Wert_A == 5)
immer wahr ist.

Gruß, Stefan

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@stefan

ich dachte immer, const int ivar=1 wird durch optimierung so behandelt,
als ob da #define ivar 1 steht. nur zusätzlich findet bei const int eine
typprüfung statt.

cu

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@keks:

Eine #define-äquivalente Behandlung geht aber nur, wenn der Compiler
zur Übersetzungszeit des Moduls den Wert der Konstanten kennt. Wird die
aber erst nach der Übersetzungszeit zum Modul gelinkt (und darum geht es
hier ja), dann kann prinzipiell nicht wie bei einem #define verfahren
werden.

Autor: Marcus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
müsste so funzen...
an der stelle, an der der header eingefügt ist, linkt der linker später
die dort eingetragenen sachen hin, und diese, die wiederum im header
verlinkt sind...
da du dies vor dem main(... machst, sind alle im header definierten
sachen also auch global bekannt...

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
habs probiert.

const int ivar=3;

if (ivar==3)
  asm("nop");


wird mit der option -Os auf ein einfaches "nop" reduziert. daher
werde ich const <type> der #define bevorzugen.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@keks:

Wenn beides in einem Sourcefile steht, dann geht das so, aber es geht
nicht, wenn die const-"Variable" aus einem anderen Modul gelinkt
wird.

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@rufus

ich meine, wir schreiben in Library.h nicht
  #define Wert_A 5
sondern
  static const int Wert_A = 5;

das static führt zur internen bindung. damit ist Wert_A nur in den
dateien bekannt, in denen #include "Library.h" steht und der linker
stört sich nicht über mehrfache deklaration. bei nur einer objektdatei
reicht dann natürlich ein const int ... .

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nö, es war die Rede davon, daß in "library.h"

  extern const int Wert_A;

steht.

Zumindest schrieb dies "jungspunt" im ersten Beitrag dieses Threads.

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mit static const int Wert_A=5 wollt ich nur zeigen, dass man nicht
#define braucht, um RAM zu sparen.

Autor: Jungspunt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erstmal danke für die Info,
ich meinte es wirklich so, dass im Header nur extern const Typ XX steht
und die Initialisierung im dazu gehörigen C-File stattfindet.

gruß Jungspunt

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
static const int Wert_A=5;

Das gehört ganz sicher nicht in ein Headerfile. Der GCC "überlagert"
zwar das Vorhandensein bei Mehrfachdefinition, das ist aber GCC-Typisch
und nicht irgendein Standard. Daß hier mehrfach definiert werden kann,
liegt einzig und allein an der Häufigkeit des Einbindens von
"library.h".
Der richtige Weg ist der von "Jungspunt" erläuterte, was aber zu dem
von Rufus geschilderten Problem führen wird.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Patrick, in diesem Falle dürfte die Definition der Variablen in der
Headerdatei zu keinem Problem führen - da sie als static deklariert
ist, ist sie nur lokal (innerhalb einbindender Module) sichtbar und
wird nicht als Symbol für den Linker exportiert.

Das von Dir angesprochene gcc-spezifische Handling von
Mehrfachdefinitionen auf Linkerebene kommt hier also gar nicht zum Zug,
damit es das täte, müsste der "storage class specifier" static
entfernt werden.

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hups, das static hatte ich nicht gesehen.
Na dann ist das ganze ja noch schlimmer, und es handelt sich in
mehreren einbindenden C-Files tatsächlich um jeweils unterschiedliche
Speicherstellen...ich werde das gleich mal prüfen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und wenn Dein Compiler dafuer tatsaechlich eine
(konstante) Variable anlegt (ohne dass Du irgendwo
die Adresse davon nimmst), dann schmeiss ihn gleich weg.

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, hier mal der Code:

static.c:
#include <stdio.h>
#include "static.h"

int
main(void)
{
        printf("%s: location of 'Wert_A' is %p (%d)\n",
        __FUNCTION__, &Wert_A, Wert_A);

        return foo();
}

static2.c
#include <stdio.h>
#include "static.h"

int
foo(void)
{
        printf("%s: location of 'Wert_A' is %p (%d)\n",
        __FUNCTION__, &Wert_A, Wert_A);

        return 0;
}

static.h:
static const int Wert_A = 5;

extern int foo(void);

Ergebnis:

main: location of 'Wert_A' is 00403000 (5)
foo: location of 'Wert_A' is 00403040 (5)

Kompiliert auf Windows2000 mit gcc 3.4.2:
gcc -g -O2 -Wall -o static.exe static.c static2.c

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist klar.
Du weist den Compiler an, die Adresse der Konstanten
auszugeben. Da bleibt dem Compiler nichts anderes uebrig
als 2 Variablen dafuer anzulegen.

Aber das tut man normalerweise nicht. Bei einer Konstanten
interessiert normalerweise nur der Wert. Und in diesem Fall
eliminiert der Compiler die Konstante vollstaendig und
setzt den definierten Wert im Code dort ein wo er gebraucht
wird.

'Constant Folding' ist fuer den Compiler eine der leichtesten
Uebungen die es beim Optimieren gibt.

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja, warum sollte man denn überhaupt eine definition in ein Headerfile
auslagern? Das ergibt keinen Sinn.

Eine der leichtesten Übungen eines Programmierers sollte es doch sein,
sich an gewisse "Gepflogenheiten" zu halten, also macht man es eben
so, daß im Headerfile die Deklaration mit extern versehen wird. Dann
kommt es in keinem Fall zur "Doppeldefinition"...

Jemand anderer Meinung?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Naja, warum sollte man denn überhaupt eine definition in ein
> Headerfile auslagern? Das ergibt keinen Sinn.

Damit garamtiert ist, dass mehere Compilation-Units immer
mit dem gleichen Zahlenwert fuer eine bestimmte Konstante
compiliert werden. Das static macht man, damit diese
Konstante im fertigen EXE keinen Platz verbraucht, sondern
direkt im Code eingesetzt wird.

> Eine der leichtesten Übungen eines Programmierers sollte es
> doch sein, sich an gewisse "Gepflogenheiten" zu halten, also
> macht man es eben so, daß im Headerfile die Deklaration mit
> extern versehen wird. Dann kommt es in keinem Fall
> zur "Doppeldefinition"...

Du hast es immer noch nicht geschnallt. Hier geht es nicht
um Doppeldefinition.
Hier geht es darum, dass die Konstante keinen Speicherplatz
fuer sich selbst verbraucht. Die konstante Variable wird
ueberhaupt nicht im Speicher angelegt, sondern existiert
als Variable nur waehrend des kompilierens. Durch die Konstruktion

static const int Alpha = 5;

ist es dem Compiler moeglich, ueberall im Source Code die
Konstante Alpha durch den Wert 5 zu ersetzen. Dies geht auch
dann, wenn die Definition in einem Header File steht und per
#include eingebunden wird -> Alpha taucht im fertigen EXE
ueberhaupt nicht auf! Ins Header File geb ich das ganze deshalb,
damit alle 7 (um mal eine Zahl zu nennen) *.c Dateien immer
denselben Wert fuer Alpha sehen und ich nur eine Stelle habe, an
der ich den Zahlenwert bei Bedarf aendern muss.

Autor: hans dieter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also noch mal von vorn:
* erst läuft der präprozessor über die source-files
dieser verarbeitet alle zeilen, die mit # beginnen und ersetzt zum
Beispiel alle Namen, die mit
#define NAME (wert)
definiert wurden durch den wert. Der Präprozessor nimmt hier nur
ersetzungen vor, d.h. der compiler sieht bei den obrigen beispiel
wirklich nur
if(5 == 5)
und nichts anderes!!!
Auch werden die #include-Zeilen durch den vom präprozessor
verarbeiteten inhalt der dateien ersetzt
* dann kommt der compiler und macht aus dem code ein object-file mit
machinen-code und externen verweisen
* dann kommt der linker und löst die externen verweise auf.

GENUG GELABERT... definitionen sollten nicht in header-dateien stehen.
dort sollten nur deklarationen (funktions-prototypen, ...) und
präprozessor anweisungen stehen.

also besser #define Wert_A 5

...... kleiner tip am rande: compiler sind von menschen, menschen sind
alles andere als perfekt, also sind auch compiler nicht perfekt.
Das anzeigen der Zwischen-Dateien (auch schon nach dem
präprozessor-lauf) kann so manches problem lösen.

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
input:

::::::::::::::
const2.c
::::::::::::::
#include "const.h"
int fkt1 (void) {
  return (ivar == 3) ? 1 : 0;
}

::::::::::::::
const.c
::::::::::::::
#include "const.h"
void main (void) {
  if (ivar == 3)
    asm ("nop");
  fkt1 ();
}

::::::::::::::
const.h
::::::::::::::
static const int ivar=3;



build:

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
/usr/local/avr/bin/avr-gcc -o const.o -c -mmcu=atmega8 -ffreestanding
-Os -g3 -I . const.c
/usr/local/avr/bin/avr-gcc -o const2.o -c -mmcu=atmega8 -ffreestanding
-Os -g3 -I . const2.c
/usr/local/avr/bin/avr-gcc -o const const.o const2.o
scons: done building targets.



output:

void
main (void)
{
  52:   cf e5           ldi     r28, 0x5F       ; 95
  54:   d2 e0           ldi     r29, 0x02       ; 2
  56:   de bf           out     0x3e, r29       ; 62
  58:   cd bf           out     0x3d, r28       ; 61
  if (ivar == 3)
    asm ("nop");
  5a:   00 00           nop

  fkt1 ();
  5c:   01 d0           rcall   .+2             ; 0x60
  5e:   03 c0           rjmp    .+6             ; 0x66

00000060 <fkt1>:
  60:   81 e0           ldi     r24, 0x01       ; 1
  62:   90 e0           ldi     r25, 0x00       ; 0
  64:   08 95           ret

00000066 <_exit>:
  66:   ff cf           rjmp    .-2             ; 0x66



-> static  const int blabla geht wohl. aus meinem c++-skript:

Mit solchen Deklarationen (const int laenge=1024;) lassen sich viele
#define-Konstanten vermeiden, die bekanntlich problematisch sind, weil
sie sich der Syntaxprüfung des Compilers entziehen. const-Objekte sind
implizit static (also nur innerhalb der Quelle gültig), ... . Das
unterscheidet sie von normalen globalen Variablen, deren
Voreeinstellung extern ist, und macht es problemlos möglich, die
Deklarationen in Header-Dateien unterzubringen.

wie oben zu sehen, geht das auch mit c, wobei bei c die variable als
static deklariert werden muss. aber wo ist da der unterschied, oder
gibt es überhaupt einen?

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> wie oben zu sehen, geht das auch mit c, wobei bei c
> die variable als static deklariert werden muss. aber
> wo ist da der unterschied, oder gibt es überhaupt einen?

O, da gibt es einen Unterschied, und zwar einen großen.

Wäre die Variable nicht static, dann würde für die Variable ein
Linkersymbol erzeugt werden, weil dann der Zugriff auch aus anderen
Modulen möglich wäre.
Daher kann ein ordnungsgemäß arbeitender C-Compiler (genauer: dessen
Linker) ein solches Programm nicht übersetzen (genauer: linken), weil
das betreffende Symbol mehrfach definiert ist.
Ärgerlicherweise lässt gcc so etwas zu, in dem mehrfach definierte
Symbole quasi "überlagert" werden, was den Anfängerfehler der
Variablendefinition in Headerdateien erst ermöglicht.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich nehme an Du meinst den Unterschied zwischen C++ und C?

Wie Du richtig sagst, muss man in C das static angeben.
Ansonsten laeuft man in die Falle der 'One Definition Rule'
(Doppeldefinition).

Da solche Definitionen aber haeufig vorkommen, wurde
das in C++ geaendert. const Definitionen verhalten sich
dort so, also ob sie von Haus aus static waeren.

Ansonsten gibt es keinen Unterschied.
Wenn Du das Ganze also sowohl unter C als auch unter C++
benutzen moechtest, dann gib static immer an. Brauchst
Du es nur fuer C++, dann kannst Du Dir das static sparen,
der Compiler nimmt es fuer Dich an.

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also mein gcc meckert, wenn ich obiges programm mit const int ivar=3;
schreibe:

const2.o(.data+0x0): In function `fkt1': const2.c:7: multiple
definition of `ivar'
const.o(.data+0x0): const.c:7: first defined here

wie "überlagert" der gcc denn nun? ohne das static funktioniert es
nur, wenn ich eine .c datei habe (logen).

nochmal:
Jungspund wollte wissen, ob const int + extern const int funktioniert.
da sind wir uns einig, das funzt. jetzt kommt Stefan und sagt, nimm
#define, das spart RAM. auch hier wieder keine einwände allerseits. nun
sage ich, nimm static const int. das funktioniert genau wie #define,
spart also RAM und gibt mir zusätzlich eine typprüfung.
vielleicht sollten wir die meinungen dreier informatikprofessoren
hierzu einholen. mal sehen, was die bevorzugen würden.

cu

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Deine Analyse unter 'nochmal' ist doch vollstaendig richtig.
Wozu Informatikprofessoren?
Die verstehen das doch selber nicht. Wenn schon, dann
hoechstens Assistenten.

Zum gcc kann ich nichts sagen. Selbst wenn der diese Erweiterung
hat, wuerde ich sie nicht verwenden, sondern gleich richtig
machen. Ist auf lange Sicht immer besser.

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das mit dem "überlagen" war eigentlich ironisch, denn mein gcc scheint
alles richtig zu  machen.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es gibt einen Anwendungsbereich, "extern const int" anstelle "static
const int" zu verwenden, nämlich dann, wenn das so definierte Symbol
zur Konfiguration einer Library verwendet werden soll.

In diesem Falle muss das Programm, das die Library verwendet, dieses
Symbol der Library zur Verfügung stellen, es ist aber nicht
erforderlich, die Library neu zu übersetzen, was ja auch nicht immer
möglich ist, beispielsweise, wenn kein Sourcecode der Library
vorliegt.

Natürlich kann in diesem Falle der Compiler die Optimierungen, wie sie
oben angesprochen wurden, nicht durchführen.

Das gcc-Verhalten sprach Patrick an
(http://www.mikrocontroller.net/forum/read-1-286669...),
welche Compileroption zu verwenden ist, damit das erfolgt, entzieht
sich glücklicherweise? meiner Kenntnis.

Karl Heinz ist in Bezug auf Informatikprofessoren nur Recht zu geben,
die haben von vielem Ahnung, aber nicht von real existierenden
Programmiersprachen. C oder C++ können/kennen die wenigsten davon.
Ältere Semster fahren auf Oberon ab (wer nichts wird, wird Wirth) ...

Autor: keks (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
um die ehre der profs. die können das sicherlich besser als der
durchschnitt unserer forenteilnehmer. und in der tat würde ich einen
assistenten fragen. der weiss es sicherlich und hat die antwort im
kopf.

Autor: Fritz Ganter (fritzg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Das gcc-Verhalten sprach Patrick an
>(http://www.mikrocontroller.net/forum/read-1-286669...),
>welche Compileroption zu verwenden ist, damit das erfolgt, entzieht
>sich glücklicherweise? meiner Kenntnis.

Da steht   static const int Wert_A=5;
das geht natürlich immer im Header, wird halt in jedem C File eine
Variable Wert_A statisch angelegt (in wirklichkeit dann nicht, weil der
Compiler die Konstante in den Code reintut).

Was mit überlagern gemeint ist wäre
int Wert_A;
in mehreren C-Files (vonmiraus über den Header).
Das geht leider wie oben erwähnt. Könnte am -std=gnu99 liegen.
Erschreckend für mich, hab aber noch nix gefunden, in dem man das
abstellen könnte. Müsste aber eine Linkeroption sein, weil der Compiler
da ja nicht draufkommen kann weil er ja immer nur 1 File sieht beim
compilieren.

Autor: Fritz Ganter (fritzg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aja, ich hab mir sowieso angewöhnt Variablen die ich exportieren will so
in das Headerfile zu schreiben (dass dann von jedem C File inkludiert
wird):
#ifndef _FTERM_H
#define _FTERM_H

#ifdef FTERMMAIN
#define EXTERN /**/
#else
#define EXTERN extern
#endif

EXTERN int baudrate, showhex, showmaster, showslave, crcerrors;
/* Function prototypes */

void saveconfig (void);
int quit_program (GtkWidget * widget, GdkEvent * event, gpointer
user_data);

#endif /* _FTERM_H */

Wobei im File das main() enthält steht:

#define FTERMMAIN
#include "fterm.h"

in allen anderen fehlt natürlich das #define FTERMMAIN

Nachteil ist aber, dass man nicht
EXTERN int count=1;
schreiben kann, man muss halt im Hauptprogramm initialisieren.

Dadurch kann nix überladen werden und man hat den Überblick was man
alles exportiert.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.