www.mikrocontroller.net

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


Autor: Danny P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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"

Autor: Danny P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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".

Autor: Danny P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
header.h
========
#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;

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

//  DEKLARATION der Zähler für Ringspeicher
//
extern uint8_t rs_ef;
extern uint8_t rs_sf;

Haupt.c
=======
#include "header.h"

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

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

/* ... Rest vom Programm ... */

MCP.c
=====
#include "header.h"

/* ... 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

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

Bewertung
0 lesenswert
nicht lesenswert
Prinzipiell willst du doch Folgendes erreichen

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

MCP.C
*****
extern can_msg_aufbau empfangsfach[8];
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
******
//  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];


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
*****
extern can_msg_aufbau empfangsfach[8];
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
    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
*****
extern can_msg_aufbau empfangsfach[8];
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
*********
typedef struct
{
  uint8_t  IDh;
  uint8_t  IDl;
  uint8_t  EID8;
   uint8_t  EID0;
  uint8_t DLC;
  uint8_t DATEN[8];

} can_msg_aufbau;

main.c
******
#include "structs.h"    // dadurch wird can_msg_aufbau eingeführt
                        // was wiederuum notwendig ist, damit
                        // der Datentyp für ...  

can_msg_aufbau empfangsfach[8];
can_msg_aufbau sendefach[8];

                        // bekannt ist. Der Compiler kann daher im
                        // Speicher eine entsprechende Anzahl Bytes
                        // für die beiden Variablen reservieren

MCP.c
*****
#include "structs.h"    // dadurch wird can_msg_aufbau eingeführt
                        // was wiederuum notwendig ist, damit
                        // der Datentyp für ...  

extern can_msg_aufbau empfangsfach[8];
extern can_msg_aufbau sendefach[8];

                        // bekannt ist. Dadurch weiss der Compiler
                        // wie die Datentypen aussehen und wie der
                        // Speicher, der an anderer Stelle reserviert
                        // wurde, organisiert ist.

Autor: Danny P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;)

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.