Forum: PC-Programmierung Include in C - first use in this function


von de1m (Gast)


Lesenswert?

Hallo Forum und alle andere ))

ich habe versucht zu googeln, aber so richtig weiß ich nicht, nach was 
ich suchen sollte.

Mein Problem:

Ich habe die eine "main.c" Datei, dort ist die Dateien "config.h" und 
"wifi.h" inkludiert.

In "config.h" habe ich ein struct "wCLIENTCFG"
1
struct wCLIENTCFG {
2
  uint32 configsaved;
3
  uint32 dhcp;
4
  char ssid[32];
5
  char password[64];
6
  char ip[15];
7
  char netmask[15];
8
  char gw[15];
9
  char dns1[15];
10
  char dns2[15];
11
};

So, wenn ich jetzt diesen Struct in "wifi.c" aufrufen will, sagt der 
Compiler, dass dieser Sturct nicht bekannt ist, wenn ich aber in 
"wifi.c" #include "config.h" machen, dann sagt der Compiler, dass der 
Struct dopplet definiert wurde.

Wie kann ich das so machen, dass ich diesen Struct aus "wifi.c" aufrufen 
kann?

Es kann natürlich auch sein, dass ich das ganz falsch mache, bin noch 
nicht richig fit mit programieren in C.

: Verschoben durch Admin
von Jochen (Gast)


Lesenswert?

Du könntest nach den Codeteilen suchen, in denen jeweils die Struktur 
deklariert ist.

Je nach Compiler gibt es eine Option, mit der man die gesamte 
Quellcodedatei sehen kann, nachdem die include-Anweisungen ausgeführt 
worden sind - genauer eigentlich, nachdem die Preprozessor-Anweisungen 
ausgeführt worden sind. Kommt auf den Compiler an.

In dem Ergebnis suchst Du denn einfach ob und wo und wieviele 
Strukturdeklarationen für wCLIENTCFG zu sehen sind.

An sich ist das nicht schwer zu finden.
Schaue Dir genau die Fehlermeldung an. Auf welche Datei und welche 
Codezeile bezieht sie sich? Daraus ist meist das wesentliche Problem zu 
erkennen.

Falls Du möchtest, kannst Du hier auch den kompletten Quellcode und die 
Fehlermeldung posten.

von Dirk B. (dirkb2)


Lesenswert?

Das ist etwas unklar beschrieben, wo du welche Datei einbindest.

Aber suche mal nach Include  Guards

von de1m (Gast)


Lesenswert?

Danke für die schnelle Antworten, aber ich glaube ich das etwas unklar 
beschrieben.

Hier noch mal mit Quelltext.

Meine main.c Datei (Auszug)
In main.c rufe ich diesen Struct auf, damit ich die Konfiguration in 
Flash speicher kann.
1
...
2
#include "config.h"
3
...
4
...
5
...
6
struct wCLIENTCFG clientConf;

Dieser Struct ist in "config.h" deklariert. Nach dem ich die 
Konfiguration gespeichert habe, will ich diese in "wifi.c" wieder aus 
dem Flash lesen, dafür benötige ich wieder den selben Struct, welchen 
ich in "config.h" deklariert haben.

Nun zu meinem Problem, jetzt will ich diesen Struct in "wifi.c" 
deklarieren, um die wifi Konfiguration zu lesen, leider sagt mir der 
Compiler das:
1
user/wifi.c:105:33: error: 'wConfAddr' undeclared (first use in this function)
2
   int readConf = spi_flash_read(wConfAddr*SPI_FLASH_SEC_SIZE, (uint32*)&wifiClientConf, sizeof(struct wCLIENTCFG));
das bedeutet ja, dass er diesen Struct nicht finden kann.

Wenn ich in die Datei "wifi.c" das hinzufüge:
1
...
2
#include "config.h"
3
...
und noch mal die Datei kompilieren will, dann bekomme ich diese 
Fehlermeldung:
1
build/app_app.a(wifi.o):(.data+0x8): multiple definition of `wConfAddr'
was für mich bedeutet, dass es jetzt mehrmals definiert wurde.

So, die Frage ist, wie kann diesen Struct "wCLIENTCFG" in "wifi.c" 
verwenden?

von Jochen (Gast)


Lesenswert?

Du drückst Dich tatsächlich unklar aus.

Strukturen z.B. werden nicht "aufgerufen". Das tut man mit Funktionen 
(oder Programmen). Es scheint mir auch so, dass Dir der Unterschied 
zwischen "Definition" und "Deklaration" möglicherweise nicht klar ist.

Jedenfalls bleibt unklar, was denn nun der als Typ verwendete Bezeichner 
"wConfAddr" mit der Struktur namens "wCLIENTCFG" zu tun hat.

Die zweite Meldung ist übrigens eine Meldung vom Linker und nicht vom 
Compiler. Sie deutet darauf hin, und das entspricht auch der 
Fehlermeldung, dass Du eine Variable namens "wConfAddr" mehrfach 
definiert hast. Aber in der Parameterliste taucht der Bezeichner in der 
Position eines Typs auf. Und das ganze hat, wiegesagt, mit der Struktur 
"wCLIENTCFG" nichts zu tun.

Hoffe da hilft Dir ein wenig weiter.

von hp-freund (Gast)


Lesenswert?

An der richtigen Stelle ein "extern" einfügen sollte helfen.

von Jochen (Gast)


Lesenswert?

Das Ganze sieht aus meiner Sicht so aus:

1. Wenn Du config.h nicht includest, fehlt Dir die Deklaration der 
Struktur.
2. Wenn Du config.h includest, dann hast Du eine Variablendefinition 
zuviel. Die von "wConfAddr".

Das Problem entsteht also nicht durch die Strukturdeklaration (das wäre 
allenfalls so, wenn sich zwei Deklarationen widersprechen) sondern durch 
die doppelte Variable.

Versuche das einmal nachzuvollziehen. Das scheint mir wichtig.

Als Lösung gibt es mehrere Möglichkeiten.

Die übliche ist: Die Variablendefinition durch eine 
"guard"-Preprozessor-Definition jeweils mit der Speicherklasse "extern" 
kennzeichnen oder nicht.

Das geht vermutlich im Moment noch über Deine Kenntnisse, aber das kann 
man Dir, wenn Du willst, noch am Beispiel zeigen. Ich bin nur gerade zu 
faul.

von Jochen (Gast)


Lesenswert?

Da ich mich selbst auch falsch ausgedrückt habe, kann ich Dir sagen, 
dass das "nicht so schlimm" ist. :-)

Also, die erste Fehlermeldung sagt etwas von einer fehlenden 
Deklaration und nicht, wie ich schrieb, das selbe wie der 
Linker-Fehler, der von einer doppelten Definition spricht.

Im Ergebnis also: "wConfAddr" ist nicht, wie Du möglicherweise annimmst 
ein Typ sondern eine Variable.
Der Compiler sieht allerdings "wConfAddr" als Typ in der gezeigten 
Zeile, da die Syntax von C an dieser Stelle sagt, dass nach der 
öffnenden Klammer, als erster Teil eines Parameters ein Typ kommen muss 
(mal von anderen Sachen abgesehen).
Da er den Typ aber nicht kennt, denn der ist scheinbar als ein 
Variablenname verwendet, sagt er was von fehlender Deklaration.

von Bernd K. (prof7bit)


Lesenswert?

Variablendefinitionen (und auch andere Definitionen) haben nichts in 
Header-Dateien zu suchen.

Nur Variablendeklarationen (und andere Deklarationen) sollten dort 
auftauchen, sonst nichts!

Bitte denke sorgfältig und lange über die Abläufe beim Kompilieren und 
beim Linken nach, notfalls unter Zuhilfenahme einschlägiger Lehrbücher 
und gezielter Fragestellung in einschlägigen Fachforen, solange bis Du 
zweifelsfrei behaupten kannst begriffen zu haben warum das so 
gefordert wird und was geschehen würde (und warum) sollte man je dagegen 
verstoßen.

von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Variablendefinitionen (und auch andere Definitionen) haben nichts in
> Header-Dateien zu suchen.

Die meisten Typdefinitionen gehören durchaus in Header-Dateien.

von ray (Gast)


Lesenswert?


von Eric B. (beric)


Lesenswert?

de1m schrieb:

> user/wifi.c:105:33: error: 'wConfAddr' undeclared (first use in this
> function)
>    int readConf = spi_flash_read(wConfAddr*SPI_FLASH_SEC_SIZE,
> (uint32*)&wifiClientConf, sizeof(struct wCLIENTCFG));
>
> das bedeutet ja, dass er diesen Struct nicht finden kann.

Nein, das bedeutet, dass die Variable wConfAddr dem Compiler 
unkbekannt ist. Über wCLIENTCFG meckert er hier ja nicht.

> Fehlermeldung:
>
> build/app_app.a(wifi.o):(.data+0x8): multiple definition of `wConfAddr'

Ich vermute, dass du in config.h die Variabele wConfAddr deklarierst 
und definierst. Das heisst es wird Speicherplatz für diese Variabele 
reserviert.

Da du config.h aber auch in main.c inkludierst, wird beim Compilieren 
von main.c nochmal Speicherplatz reserviert und der Linker, der main.o 
und wifi.o, zusammenschweissen will, beswchwert sich über die doppelte 
Speicherzuweisung.

Die Lösung besteht darin, in config.h die Variabele nur zu definieren 
(also ohne Speicherzuweisung):
1
extern wCLIENTCFG wConfAddr;

Und dann in entweder main.c oder wifi.c wConfAddr zu deklarieren 
(also Speicher zuzuweisen)
1
wCLIENTCFG wConfAddr;

Eine sauberere Lösung wäre aber wenn wifi.c Funktionen bereitstellen 
würde, die es main.c erlaubt die Konfiguration ins Flash zu speichern 
und daraus zu lesen, ohne dass main.c sich um die Adresse kümmern muss.


wifi.h
1
...
2
bool_t WifiSaveConfig();
3
bool_t WifiReadConfig();
4
...

wifi.c
1
bool_t WifiSaveConfig()
2
{
3
   ...benutze hier wConfAddr und speichere die Wifi Config..
4
5
  Liefere TRUE zurück wenn Speicherung OK, sonst FALSE
6
}
7
bool_t WifiReadConfig()
8
{
9
   ...hier auch ...
10
}

main.c
1
...hier KEIN #include "config.h"
2
3
  if(WiFiSaveConfig())
4
  {
5
    printf("Wifi Config saved OK\n");
6
  }
7
...

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

de1m schrieb:
> Mein Problem:
>
> Ich habe die eine "main.c" Datei, dort ist die Dateien "config.h" und
> "wifi.h" inkludiert.
>
> In "config.h" habe ich ein struct "wCLIENTCFG"

Ich habe den ziemlich bestimmten Eindruck, daß du Typdefinitionen und 
Variablendeklarationen nicht unterscheiden kannst. Ich hätte dir zwecks 
Erlernen dieses wesentlichen Unterschiedes zunächst das Programmieren in 
Pascal empfohlen. Nachträglich tut das ebenso gut, also programmiere mal 
ne Weile in Pascal.

Zu deinem Thema, also:

Erstens, wähle die Bezeichner deiner kreierten Typen sinnvoll. Ich würde 
dir als Bezeichner deines struct's TClientCfg vorschlagen. Damit du (als 
Eselsbrücke) am führenden T (wie "Typ") sehen kannst, daß hier eine 
Typdefinition vorliegt. Wenn du sowas konsequent überall durchziehst, 
dann machst du dir damit das Leben und das Lesen leichter.

Zweitens:
In eine .h Datei gehört nur das hinein, was man von dem Modul, der damit 
gemeint ist, wirklich wissen MUSS. Und nicht mehr.

Drittens:
Klammere jeden Inhalt einer .h inetwa dieser Form

/* header zu config.c */
#ifndef CONFIGINCLUDED
#define CONFIGINCLUDED

struct TClientCfg
{ dword configsaved;   // dein Kommentar dazu
  dword dhcp;          // dito
  char  ssid[32];      // ebenso
  char  password[64];  // usw.
  char  ip[15];
  char  netmask[15];
  char  gw[15];
  char  dns1[15];
  char  dns2[15];
};

extern struct Ottokar;

#endif
  /* ende von config.h */

Also so etwa. Und in config.c wird natürlich config.h inkludiert UND du 
vereinbarst dort deine tatsächliche Variable:

#include "config.h"
struct TClientCfg Ottokar;

So. Prinzip: Typangabe Variablenname;

Jetzt kannst du deine config.h überall einbinden wo du willst und von 
dort aus auch auf Ottokar zugreifen.

Hast du das Prinzip dahinter jetzt verstanden?

W.S.

von de1m (Gast)


Lesenswert?

Ja, es stimmt, ich habe noch nicht viel Ahnung von C, habe einen kleinen 
Projekt letztes Jahr gemacht, ansonsten habe ich damit nicht viel zu 
tun.

Aber danke für die Hilfe, ich habe jetzt alles was zum Speicher Lesen 
gehört in eine eigene Datei verschoben, damit klappt es mit Kompilieren, 
bleibt aber nach dem Lesen hängen. Dazu habe ich einen anderen Topic 
aufgemacht.

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.