Forum: Compiler & IDEs Globale Variable - undefined reference


von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Hallo Leute,

ich sitze hier schon eine Weile dran:
1
//main.c
2
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <string.h>
6
7
#include <sub.h>
8
9
void app_main() {
10
11
  strcpy(string_array, "Hallo");
12
13
  printf("%d\n", zahl );
14
  printf("%s\n", string_array );
15
16
}
1
//sub.h
2
3
#ifndef SUB_H
4
#define SUB_H
5
6
  extern int zahl;
7
  extern char string_array[];
8
9
#endif
1
//sub.c
2
3
#include <sub.h>
4
5
void a_task() {
6
7
  int zahl = 5;
8
  char string_array[100];
9
10
}

Ich bekomme es nicht hin. Wenn ich zahl und string_array aus der 
Funktion
eine Ebene nach oben lege (wo sie dann wohl automatisch extern werden) 
funktioniert es.

Lass ich die Variablen im Funktionsblock bekomme ich: undefined 
reference to ''

von Zombie (Gast)


Lesenswert?

W3ll S. schrieb:
> Lass ich die Variablen im Funktionsblock bekomme ich: undefined
> reference to ''

Logisch, sind ja dann auch lokale Variablen, auf die kannst Du nur 
innerhalb der Funktion zugreifen. Entweder lokal oder global, beides 
geht nicht.

von Peter S. (psavr)


Lesenswert?

>..eine Ebene nach oben lege (wo sie dann wohl automatisch extern werden)
>funktioniert es.

So werden sie global, nicht extern. Variabeln die du innerhalb einer 
Funktion deklarierst sind immer lokal.

von Dirk B. (dirkb2)


Lesenswert?

W3ll S. schrieb:
> wo sie dann wohl automatisch extern werden

Nein.
Das extern kennzeichnet die Deklaration.
Ohne extern ist es eine Definition.

Zombie schrieb:
> Entweder lokal oder global,

Es geht noch static.

von ccc (Gast)


Lesenswert?

Vertausch diese beiden Zeilen:
1
void a_task() {
2
3
  int zahl = 5;

von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Ahh, ok danke!

Ich dachte echt, in kann Variablen in einem Headerfile 'extern' 
deklarieren, dann in einer Funtion definieren und überall wo ich das 
Headerfile einbinde, drauf zugreifen ...

von Rolf M. (rmagnus)


Lesenswert?

Peter S. schrieb:
>>..eine Ebene nach oben lege (wo sie dann wohl automatisch extern werden)
>>funktioniert es.
>
> So werden sie global, nicht extern.

Doch, extern werden sie dadurch auch.

Dirk B. schrieb:
> Das extern kennzeichnet die Deklaration.

Ja, im Quellcode macht ein vorgestelltes "extern" eine Deklaration 
draus.

> Ohne extern ist es eine Definition.

Dennoch hat die Variable "external linkage". Man kann also von anderen 
Übersetzungseinheiten aus darauf zugreifen. Wenn man das vermeiden 
wollte, müsste sie static gemacht werden.

von Dirk B. (dirkb2)


Lesenswert?

W3ll S. schrieb:
> dann in einer Funtion definieren und überall wo ich das
> Headerfile einbinde, drauf zugreifen

Woher soll der Compiler/Linker denn wissen, welche Funktion gemeint ist?

von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Danke!

von Mike (Gast)


Lesenswert?

>Ich dachte echt, in kann Variablen in einem Headerfile 'extern'
>deklarieren, dann in einer Funtion definieren und überall wo ich das
>Headerfile einbinde, drauf zugreifen ...

Mit 'extern' wird keine Variable deklariert.
=> Damit sagst Du dem Compiler, dass die Variable in einem anderen Modul 
(Global) deklariert wird. Der Compiler soll einfach die angegeben 
Definition verwenden, der Linker kümmert sich dann um die Refernz, bzw. 
meckert wenn er keine Refernz dazu findet.

Der Linker findet die Referenz nicht wenn:
- Die Variable Lokal (=innerhalb einer Funtion) deklariert ist
- Die Variable als 'static' definiert ist (=> Keine Linker Refernz)

von Mike (Gast)


Lesenswert?

..aber statt 'extern' zu nutzen ist es eleganter, globale 
Variabeln-Definitionen via Headerfiles zu includieren.

von Dirk B. (dirkb2)


Lesenswert?

Mike schrieb:
> Mit 'extern' wird keine Variable deklariert.
> => Damit sagst Du dem Compiler, dass die Variable in einem anderen Modul
> (Global) deklariert wird.

Ja was denn nun? Deklariert oder nicht?

Mike schrieb:
> ..aber statt 'extern' zu nutzen ist es eleganter, globale
> Variabeln-Definitionen via Headerfiles zu includieren.

Niemals.


Variablen werden in .c Dateien definiert
Und in Headerdateien - wenn nötig - deklariert.

Die Unterscheidung zwischen Definition und Deklaration macht das extern.

: Bearbeitet durch User
von Uwe W. (Firma: LCD-Solution SAS) (upwettin)


Lesenswert?

Gibt es Literatur über das Thema, die in kurzen Worten alle Fälle (von 
lokalen, globalen und statischen Variablen) abdeckt und in den 
Kommentaren etwas beschreibt?
Quasi 4 Dateien (main.c, main.h, thema.c und thema.h)

Noch eine Frage zum Beispiel ganz oben. Muss in main.c nicht a_task() 
aufgerufen werden?

Gruß UPW

von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Mike schrieb:
> ..aber statt 'extern' zu nutzen ist es eleganter, globale
> Variabeln-Definitionen via Headerfiles zu includieren.

Hallo Mike,

machte mal ein Beispiel bitte?

von zitter_ned_aso (Gast)


Lesenswert?

das geht docht nicht. Man kann nicht die gleiche Variable mehrmals 
defininieren.

von Dirk B. (dirkb2)


Lesenswert?

Jede .c-Datei wird für sich alleine compiliert.
Vorweg läuft noch der Preprozessor. Der ersetzt z.B. die Zeile #include 
"meins.h" durch den Inhalt der Datei meins.h

Der eigentliche Compiler sieht von den Preprozessoranweisungen nichts.
Er weiß nichts von anderen Dateien.
Er übersetzt die .c Dateien in Object-Dateien. Diese werden dann (mit 
den Bibliotheken) vom Linker zu einer ausführbaren Datei zusammen 
gebunden.

Wenn jetzt in einer Headerdatei Variablen definiert werden und dieser 
Header in mehreren .c-Dateien inkludiert werden, dann werden diese 
Variablen mehrmals angelegt.
Das gefällt dem Linker nicht so gut.

Werden die Variablen aber in der .c definiert und in den Headern 
deklariert, gibt es diese Variable nur einmal.

Globale Variablen sollte man so wenig wie möglich nutzen.

Ein globales i (selber gesehen) kann sehr irritierend sein.

von DPA (Gast)


Lesenswert?

W3ll S. schrieb:
> Mike schrieb:
>> ..aber statt 'extern' zu nutzen ist es eleganter, globale
>> Variabeln-Definitionen via Headerfiles zu includieren.
>
> Hallo Mike,
>
> machte mal ein Beispiel bitte?

Man könnte das so machen (ungetestet):

Makefile
1
HEADERS = a.h b.h
2
OBJECTS = vars.gen.o bla.o blup.o
3
DEPS = $(HEADERS)
4
5
all: myfancyprogram
6
7
# Include everything in one file to make sure all headers are only pulled in once
8
vars.gen.c: $(HEADERS)
9
  ( \
10
    echo '#define GENCODE'; \
11
    echo '#include "vars-gen.h"'; \
12
    for header in $^; \
13
      do echo "#include \"$$header\""; \
14
    done; \
15
  ) > $@
16
17
%.o: %.c $(DEPS)
18
  $(CC) -c -o $@ $< $(CFLAGS)
19
20
myfancyprogram: $(OBJECTS)
21
  $(CC) -o $@ $^

vars-gen.h
1
#ifndef VARS_GEN_H
2
#define VARS_GEN_H
3
4
#ifdef GENCODE
5
#define EXTDEF
6
#else
7
#define EXTDEF extern
8
#endif
9
10
#endif

a.h:
1
#ifndef A_H
2
#define A_H
3
4
#include "vars-gen.h"
5
6
EXTDEF int a;
7
8
#endif

b.h:
1
#ifndef B_H
2
#define B_H
3
4
#include "vars-gen.h"
5
6
EXTDEF int b;
7
8
#endif

bla.c:
1
#include "a.h"
2
#include "b.h"
3
...

blup.c:
1
#include "a.h"
2
#include "b.h"
3
...

Würde ich aber nicht empfehlen.

von DPA (Gast)


Lesenswert?

Alternativ könnte man alle Symbole weak machen.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Einen ganz fieser Fallstrick gibt es bei globalen Variablen, wenn man 
sein Projekt nicht nur auf Quellcodeebene schön strukturiert, sondern 
die einzelnen Kompilate zunächst zu Bibliotheken linkt und anschließend 
diese Bibliotheken, ggf. mit einzelnen Objektdateien, zur eigentlichen 
Applikation. Je nach Linkereinstellungen sind globale Variable dann 
nicht außerhalb einer Bibliothek sichtbar, und wenn von einem anderen 
Programmteil auf solch eine globale Variable zugegriffen werden soll, 
wird nämlich daneben gegriffen. Solch ein Problem hatte ich mal vor 
ewiger Zeit(tm) bei einer Applikation, die ich von Ultrix auf Windows NT 
portieren musste. Wenn ich mich recht einnere, fasste der C-Compiler 
unter Ultrix beim Linken gleichnamige globale Variable, die in mehrere 
Bibliotheken definiert wurden, zu einer gemeinsamen zusammen. Microsoft 
C hingegen legte dann pro Bibliothek eine Instanz an, was meines 
Erachtens in den meisten Fällen auch sinnvoll ist, denn gerade bei 
fremden Bibliotheken könnte es ja sonst auch zu unbeabsichtigten 
Übereinstimmungen kommen.

Wenn man Bibbliotheken zur Verwendung durch Dritte erstellt, sollte man 
am Besten auch in einer entsprechenden Linkersteuerdatei explizit die 
Symbole benennen, die nach außen sichtbar sein sollen, und alle anderen 
Symbole verbergen.

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.