Forum: Compiler & IDEs array aus structs will nicht global :(


von Danny P. (Gast)


Lesenswert?

Hey Zusammen :)

ich will versuchen mich mal an C zu gewöhnen, sitze nun seit 5h hier und 
versuche ein Ringpuffer zum Speichern von empfangenen CAN-Msg´s zu 
stricken...

Dafür wollte ich ein Array aus Struct´s anlegen und mit der Nummer des 
Struct´s dann weiterarbeiten...

innerhalb meiner .c-Datei läuft es wie gewünscht. Auf den Puffer wird 
aber auch aus Programmteilen zugegriffen die halt in anderen .c-Datein 
stehen und da sind meine Variablen angeblich nicht definiert. Aber das 
habe ich doch schon in der "Haupt .c-Datei" gemach tund damit sollten 
sie global sein, oder? selbst alle zusammengesuchten Tips von google und 
hier aus dem Forum brachten mich nicht weiter...

was mache ich falsch?


#include  <avr/io.h>
#include  <stdint.h>
#include  <avr/interrupt.h>
#include  <mcp2515.h>

//  Erzeugen des Datentyps einer CAN-Botschaft
//
typedef struct
{
  uint8_t  IDh;
  uint8_t  IDl;
  uint8_t  EID8;
   uint8_t  EID0;
  uint8_t DLC;
  uint8_t DATEN[8];

} can_msg_aufbau;


//  Erzeugen der Datenfelder für Sende-/Empfangsfächer
//
can_msg_aufbau empfangsfach[8];
can_msg_aufbau sendefach[8];

//  Zähler für Ringspeicher
//
uint8_t  rs_ef = 1;
uint8_t  rs_sf = 1;


thx & greetz
Danny

von Johannes M. (johnny-m)


Lesenswert?

Du musst alle Variablen, die auch in anderen Quelldateien verwendet 
werden sollen, mittels einer "extern"-Deklaration auch in diesen bekannt 
machen.

Der Compiler verarbeitet jede einzelne Quelldatei separat. Wenn Du z.B. 
eine main.c und eine xyz.c hast, die zum selben Projekt gehören, dann 
compiliert der Compiler die main.c und die xyz.c getrennt, d.h. beim 
Compilieren von xyz.c existiert der Code der main.c für ihn überhaupt 
nicht.

Wenn Du eine Variable in main.c hast, die auch in xyz.c bekannt sein 
soll, dann muss diese Variable mit dem vorgestellten Qualifizierer in 
der xyz.c deklariert werden.

Die sinnvollste Lösung (die auch fast immer verwendet wird), ist 
allerdings, zusätzlich zu jeder Source-Datei eine *.h-Datei zu erstellen 
und in dieser alle extern-Deklarationen von Variablen, die auch anderen 
Quelldateien zur Verfügung stehen sollen, durchzuführen. Diese 
*.h-Dateien werden dann in die betreffenden Sourcen eingebunden.

Den Rest hatten wir heute schon:
Beitrag "extern Variable"

von Danny P. (Gast)


Lesenswert?

@Johannes:  vielen Dank für die schnelle Antwort.... aaaaber :)

das mit extern habe ich gelesen und diverse male versucht,
nur wie genau wende ich es in dem 2. .c-file an?

bei einer "normalen" Variablen kein Thema, aber bei nem Array aus 
Struct´s? Ich habe es nicht hinbekommen und auch kein entsprechendes 
Codebeispiel gefunden...



thx & greetz
Danny

von Johannes M. (johnny-m)


Lesenswert?

Das typedef muss natürlich auch in allen Sourcen bekannt sein. Das 
sollte man in einer eigenen *.h unterbringen und ebenfalls in alle 
Sourcen einbinden.

BTW:
> Array aus Struct´s?
Der (Deppen-)Apostroph hat vor dem s nichts zu suchen... Selbst im 
Englischen heißt die Mehrzahl von "struct" einfach nur "structs".

von Danny P. (Gast)


Lesenswert?

mh ich trau mich fast nicht zu fragen, aber könntet ihr mir anhand 
meines Beipieles mal aufschreiben was in welches File müsste, ich dreh 
bald durch... :(

ich habe ein Haupt.c und ein MCP.c aus beiden soll auf den ringpuffer 
zugegriffen werden

ich habe nun alles versucht, mit extern, definieren im .h aber wird 
nichts... vermutlich nur falshc geschrieben oder so... naja... 
c-anfänger halt :)

thx & greetz
Danny

von Stefan B. (stefan) Benutzerseite


Lesenswert?

header.h
========
1
#include  <avr/io.h>
2
#include  <stdint.h>
3
#include  <avr/interrupt.h>
4
#include  <mcp2515.h>
5
6
//  Erzeugen des Datentyps einer CAN-Botschaft
7
//
8
typedef struct
9
{
10
  uint8_t  IDh;
11
  uint8_t  IDl;
12
  uint8_t  EID8;
13
  uint8_t  EID0;
14
  uint8_t  DLC;
15
  uint8_t  DATEN[8];
16
} can_msg_aufbau;
17
18
//  DEKLARATION der Datenfelder für Sende-/Empfangsfächer
19
//
20
extern can_msg_aufbau empfangsfach[];
21
extern can_msg_aufbau sendefach[];
22
23
//  DEKLARATION der Zähler für Ringspeicher
24
//
25
extern uint8_t rs_ef;
26
extern uint8_t rs_sf;

Haupt.c
=======
1
#include "header.h"
2
3
//  DEFINITION der Datenfelder für Sende-/Empfangsfächer
4
//
5
can_msg_aufbau empfangsfach[8];
6
can_msg_aufbau sendefach[8];
7
8
//  DEFINITION der Zähler für Ringspeicher
9
//
10
uint8_t rs_ef = 1;
11
uint8_t rs_sf = 1;
12
13
/* ... Rest vom Programm ... */

MCP.c
=====
1
#include "header.h"
2
3
/* ... Rest vom Programm ... */

Im Makefile muss dann eine so ähnliche Zeile auftauchen
(SRC kann auch CSRC oder C_SRC o.ä. genannt sein):

SRC = Haupt.c MCP.c

von Karl H. (kbuchegg)


Lesenswert?

Prinzipiell willst du doch Folgendes erreichen

main.c
******
1
can_msg_aufbau empfangsfach[8];
2
can_msg_aufbau sendefach[8];

MCP.C
*****
1
extern can_msg_aufbau empfangsfach[8];
2
extern can_msg_aufbau sendefach[8];


Das sagt erst mal aus:
in main.c existieren die Variablen empfangsfach und sendefach.
In MCP.C wird (wegen dem extern) nur gesagt, dass es 2 Variablen
gibt, names empfangsfach bzw. sendefach. Wie gesagt: diese beiden
existieren irgendwo (naemlich in main.c. Das wissen aber nur wir,
der Compiler weis das erst mal nicht). Dem Compiler ist es beim
compilieren von MCP.C auch erst mal egal wo diese beiden Variablen
liegen, er muss nur wissen
* dass es sie gibt
* dass daher in weiterer Folge in MCP.c ein Variablenname von
  empfangsfach kein Schreibfehler ist, sondern dass du tatsächlich
  eine Variable damit meinst
* und natürlich welchen Datentyp sie haben.
Insbesondere letzteres muss er natürlich wissen, damit er bei
einer Arrayindiziegun aussrechnen kann, wo den das angesprochene
Element innerhalb des Arrays liegt.

Soweit so gut.
Der Compiler muss aber noch mehr wissen.
Wie ein Array aussieht, weiss er auch so. Aber wie der Datentyp
can_msg_aufbau aussieht weiss er nicht. Das muss man ihm daher
beschreiben.

Einschub: Denk immer daran: main.c und MCP.c werden unabhängig
voneinander compiliert! Wenn der Compiler MCP.c kompiliert, dann
sieht er nur dass, was in diesem File drinnen steht. Was in main.c
drinnen steht interessiert zu diesem Zeitpunkt nicht

D.h. aber auch.
Wenn der Compiler das hier compiliert

main.c
******
1
//  Erzeugen des Datentyps einer CAN-Botschaft
2
//
3
typedef struct
4
{
5
  uint8_t  IDh;
6
  uint8_t  IDl;
7
  uint8_t  EID8;
8
   uint8_t  EID0;
9
  uint8_t DLC;
10
  uint8_t DATEN[8];
11
12
} can_msg_aufbau;
13
14
15
//  Erzeugen der Datenfelder für Sende-/Empfangsfächer
16
//
17
can_msg_aufbau empfangsfach[8];
18
can_msg_aufbau sendefach[8];

ist alles in Butter. Da werden 2 Variablen definiert, die beide
vom Datentyp Array von can_msg_aufbau sind. Wie ein can_msg_aufbau
aussieht, ist im vorher mitgeteilt worden-

Wenn der Compiler aber das hier compiliert:

MCP.c
*****
1
extern can_msg_aufbau empfangsfach[8];
2
extern can_msg_aufbau sendefach[8];

Dann hat er ein Problem. Zwar kennt er danach die beiden Variablen.
Er hat auch erkannt das es sich dabei um Arrays handelt.
Aber er weis nichts über den Datentyp can_msg_aufbau. Das würde er
gerne tun und auch tun muessen, denn er muss ja schliesslich
prüfen, ob sowas hier
1
    empfangsfach[2].EID8
überhaupt gültig ist. Dazu muss er aber wissen ob den eine
can_msg_aufbau überhaupt ein Feld namens EID8 besitzt und an
welcher Stelle in der Struktur diese steht.
Nur, wenn der Compiler nur das hier hat (und denk dran, was in
main.c steht interessiert nicht)
MCP.C
*****
1
extern can_msg_aufbau empfangsfach[8];
2
extern can_msg_aufbau sendefach[8];
dann kann er das schlicht und ergreifend nicht. Also muss man
ihm das beibringen

MCP.c
*****
//  Erzeugen des Datentyps einer CAN-Botschaft
//
typedef struct
{
  uint8_t  IDh;
  uint8_t  IDl;
  uint8_t  EID8;
   uint8_t  EID0;
  uint8_t DLC;
  uint8_t DATEN[8];

} can_msg_aufbau;


//  Erzeugen der Datenfelder für Sende-/Empfangsfächer
//
extern can_msg_aufbau empfangsfach[8];
extern can_msg_aufbau sendefach[8];
[/C]

Jetzt kann der Compiler alles notwendig aus MCP.c (und nur von
dort) ablesen um mit den beiden Variablen klarzukommen.

Jetzt kommt das grosse 'Aber'!
Natürlich ist sowas Fehleranfällig. Du musst die Struktur can_msg_aufbau
2-mal beschreiben: Einmal in main.c und einmal in MCP.c.
Das die beiden Beschreibungen exakt übereinstimmen sollten, brauche
ich wohl nicht extra zu erwähnen. Das diese Übereinstimmung von
niemandem (ausser dir) kontrolliert wird, brauche ich auch nicht
extra zu erwähnen. Wer solls auch machen? Wenn der Compiler main.c
compiliert kümmert ihn nur das was in main.c steht. Wenn der Compiler
MCP.c compiliert kümmert ihn nur das was in MCP.c steht.

Der Ausweg lautet: Ein Include File muss her.
Die Strukturdefinition wird dorthin ausgelagert. Dann kann ich
dieses Include File sowohl in main.c includieren als auch in MCP.c
includieren. Beim compilieren ist dadurch die Strukturdefinition
sowohl in main.c sichtbar, als auch in MCP.c. Und als Programmierer
hast du den Vorteil, dass du sie nur einmal schreiben musst. Und
da es sie in Textform nur einmal gibt, muessen daher die Definitionen
in main.c und MCP.c per Definition übereinstimmen.

structs.h
*********
1
typedef struct
2
{
3
  uint8_t  IDh;
4
  uint8_t  IDl;
5
  uint8_t  EID8;
6
   uint8_t  EID0;
7
  uint8_t DLC;
8
  uint8_t DATEN[8];
9
10
} can_msg_aufbau;

main.c
******
1
#include "structs.h"    // dadurch wird can_msg_aufbau eingeführt
2
                        // was wiederuum notwendig ist, damit
3
                        // der Datentyp für ...  
4
5
can_msg_aufbau empfangsfach[8];
6
can_msg_aufbau sendefach[8];
7
8
                        // bekannt ist. Der Compiler kann daher im
9
                        // Speicher eine entsprechende Anzahl Bytes
10
                        // für die beiden Variablen reservieren

MCP.c
*****
1
#include "structs.h"    // dadurch wird can_msg_aufbau eingeführt
2
                        // was wiederuum notwendig ist, damit
3
                        // der Datentyp für ...  
4
5
extern can_msg_aufbau empfangsfach[8];
6
extern can_msg_aufbau sendefach[8];
7
8
                        // bekannt ist. Dadurch weiss der Compiler
9
                        // wie die Datentypen aussehen und wie der
10
                        // Speicher, der an anderer Stelle reserviert
11
                        // wurde, organisiert ist.

von Danny P. (Gast)


Lesenswert?

@Stefan & Karl Heinz: Vielen Dank für die raschen Antworten und klasse 
Erklärungen!

Ich bin zwar der Meinung das so mal getestet zu haben, aber dabei hab 
ich mich wohl an anderer Stelle verzettelt... nun klappts aber!

:)

thx & greetz
Danny
(der nun beruhigt schlafen kann ;)

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.