Forum: Compiler & IDEs C- und Header-Files


von Christoph A. (scheams)


Lesenswert?

Hallo!

Ich habe hier ein paar Fragen zum Organisieren, Strukurieren, etc. von 
extra Files (.c, .h).

Derzeit verwende ich als IDE Atmel Studio 7 und würde meine Projekte 
gerne aufteilen. Funktioniert alles gut nur kann ich mich nicht einigen 
wie ich die Files mit extra Ordnern aufteilen will/soll und außerdem wie 
ich struct's für diese Header gestalte.

1. Projektaufteilung
Um das Programm übersichtlich zu halten würde ich gerne Unterornder 
erstellen. Macht das noch jemand und wie teilt ihr diese auf?

Erfundenes Beispiel:
Robotor mit einem Motor mit Encoder und H-Brücke (Geschwindigkeit 
abhängig von Distanz), Distanzsensor, LED, I2C Digital-Widerstand vor 
LED und abhängig von Distanz, serielle Schnittstelle zu PC für 
Informationen.
Nun wie könnte man soetwas aufteilen?
Nach Katogorie, Anwendung, ...?
Kategorie dann z.B. Ordner wie "Aktor" (Motor bzw. Motortreiber, LED), 
"Sensor" (IR-Distanzsensor, Motorencoder), "Kommunikation" (I2C, UART)?
Oder vielleicht nach Anwendung "Motor" (Motortreiber, Motorencoder), 
"LED" (LED, Widerstand), "Distanz" (IR-Distanzsensor) und "Info" (UART).

Oder sonstige Vorschläge? Macht ihr solche Einteilungen überhaupt?

2. Struct's in Header
Wenn private Variablen in einer Struct vorhanden sind, will ich doch 
eigentlich, dass sie außerhalb (der dazugehörigen .c File) nicht 
bearbeitet werden soll. Also könnte ich das mit struct's in der 
spezifischen .c File einbauen. Es müssen aber diese privaten Struct's 
mit den "public" Struct's erstellt werden, was aber ohne malloc/free 
schwierig ist. Eine weitere Möglichket wäre während Compiletime einen 
definierten Array von diesen Struct's erstellen, auch nicht gerade die 
beste Lösung.
Gibt es dazu eine solide Lösung oder doch lieber dabei bleiben, alles in 
eine Struct zu packen und im Main-Code diese Struct dann initialisieren?

Falls diese Themen evtl. auch in C-Style Guides o.Ä. angesprochen 
werden, wäre ich auch dankbar über eine Verlinkung. Ich will eigentlich 
nur eine Richtlinie an die ich mich halten kann.

mfg Scheams

: Verschoben durch User
von Mark B. (markbrandis)


Lesenswert?

Christoph A. schrieb:
> Hallo!
>
> Ich habe hier ein paar Fragen zum Organisieren, Strukurieren, etc. von
> extra Files (.c, .h).
>
> Derzeit verwende ich als IDE Atmel Studio 7 und würde meine Projekte
> gerne aufteilen. Funktioniert alles gut nur kann ich mich nicht einigen
> wie ich die Files mit extra Ordnern aufteilen will/soll und außerdem wie
> ich struct's für diese Header gestalte.
>
> 1. Projektaufteilung
> Um das Programm übersichtlich zu halten würde ich gerne Unterornder
> erstellen. Macht das noch jemand und wie teilt ihr diese auf?

Natürlich. Man kann die Unterteilung zum Beispiel anhand von hinreichend 
großen Subsystemen vornehmnen. Verschiedene Funktionalitäten innerhalb 
des gleichen Subsystems kann man zum Beispiel auf verschiedene Dateien 
innerhalb des gleichen Unterverzeichnisses verteilen.

> 2. Struct's in Header
> Wenn private Variablen in einer Struct vorhanden sind, will ich doch
> eigentlich, dass sie außerhalb (der dazugehörigen .c File) nicht
> bearbeitet werden soll.

C kennt keine privaten Variablen innerhalb von structs.

Du kannst Variablen innerhalb einer C-Datei (aber außerhalb jeder 
Funktion in dieser Datei) als "static" deklarieren. Dann kann auf diese 
Variable nur innerhalb dieser Übersetzungseinheit zugegriffen werden.

von B. S. (bestucki)


Lesenswert?

Christoph A. schrieb:
> 1. Projektaufteilung

Alles was universell verwendbar ist, wandert in den lib Ordner. Dazu 
zählen auch z.B. I2C, UART, FIFOs, sonstige Bibliotheken. Also alles, 
was man unverändert in anderen Projekten verwenden kann.

Den Rest, also alles projektspezifische, teile ich nach gutdünken in 
logische Blöcke auf.


Christoph A. schrieb:
> 2. Struct's in Header

Ich sehe, ausser bei ganz einfachen Dingen, alle Member als privat an 
und bearbeite diese nur über entsprechende Funktionen. Zum Erstellen der 
Strukturen verwende ich ebenfalls eigene Funktionen (ähnlich den 
Konstruktoren in C++). Die Member stehen dann trotzdem als public im 
Header, aber mit genügend Disziplin sollte das nicht mehr stören, da 
alles über die Funktionen erledigt wird.

von Mark B. (markbrandis)


Lesenswert?

Be S. schrieb:
> Christoph A. schrieb:
>> 2. Struct's in Header
>
> Ich sehe, ausser bei ganz einfachen Dingen, alle Member als privat an
> und bearbeite diese nur über entsprechende Funktionen. Zum Erstellen der
> Strukturen verwende ich ebenfalls eigene Funktionen (ähnlich den
> Konstruktoren in C++). Die Member stehen dann trotzdem als public im
> Header, aber mit genügend Disziplin sollte das nicht mehr stören, da
> alles über die Funktionen erledigt wird.

Hm, wozu dann noch die Deklaration im Header?

Alles, was nicht von außerhalb einer C-Datei zugreifbar sein soll, 
braucht auch nicht in der zugehörigen Header-Datei zu stehen.

von B. S. (bestucki)


Lesenswert?

Mark B. schrieb:
> Hm, wozu dann noch die Deklaration im Header?
>
> Alles, was nicht von außerhalb einer C-Datei zugreifbar sein soll,
> braucht auch nicht in der zugehörigen Header-Datei zu stehen.

Ja, das ist klar. Folgendes Beispiel:
1
struct my_struct{
2
  int WertA; /* soll public sein */
3
  int WertB; /* soll private sein */
4
};

Nun kannst du niemanden davon abhalten, auf WertB zuzugreifen, obwohl er 
es nicht sollte. Sinnvoll lässt sich das ohne malloc und void-Zeiger 
nicht trennen. Deswegen mein Vorschlag, einfach alle Member "private" zu 
machen und alle Zugriffe über Funktionen zu erledigen. Man kann zwar 
trotzdem auf die Member zugreifen, aber die eigene Disziplin soll es 
verhindern.

Eine andere Möglichkeit wäre:
1
struct my_struct{
2
  int WertA;
3
  struct my_struct_private{
4
    int WertB;
5
  }Private;
6
};
So wird verdeutlicht, das public und was private ist.

von Christoph A. (scheams)


Lesenswert?

Erstmal danke für die schnellen Antworten :)

Be S. schrieb:
> Alles was universell verwendbar ist, wandert in den lib Ordner. Dazu
> zählen auch z.B. I2C, UART, FIFOs, sonstige Bibliotheken. Also alles,
> was man unverändert in anderen Projekten verwenden kann.

Gefällt mir schon einmal gut! :)

Ihr habt Aufteilung nach Subsysteme bzw. logische Blöcke angesprochen 
aber was ist das für euch? Ich schwanke immer mit meinen Gedanken, dass 
einerseits die Aufteilung z.B. nach Kategorie, andererseits nach 
Anwendung ihre jeweiligen Vorteile haben.
Vielleicht könntet ihr ein Beispiel geben, anhand meines zuvor genanntes 
erfundens Projekt oder allgemein (mit Ordnernamen)? Würde mir sicher 
helfen :)

Be S. schrieb:
> Ich sehe, ausser bei ganz einfachen Dingen, alle Member als privat an
> und bearbeite diese nur über entsprechende Funktionen. Zum Erstellen der
> Strukturen verwende ich ebenfalls eigene Funktionen (ähnlich den
> Konstruktoren in C++). Die Member stehen dann trotzdem als public im
> Header, aber mit genügend Disziplin sollte das nicht mehr stören, da
> alles über die Funktionen erledigt wird.

D.h. im Main-Code erstellst du dann eine struct und übernimmst/-gibst 
diese dann immer in die Funktionen? Für die Initialisierung ebenfalls 
(wenn ich das richtig verstanden habe).
Was ist wenn ich eine struct für z.B. einen PID Regler habe, wo dann 
recht viele Initialisierungswerte benötigt werden (mit div. Extras upto 
15). Diese als Funktionsparameter zu übergeben ist vielleicht nicht die 
"feinste" Art. Initialisierst du diese vielleicht dann doch direkt in 
der struct Initialisierung?

mfg Scheams

: Bearbeitet durch User
von B. S. (bestucki)


Lesenswert?

Christoph A. schrieb:
> D.h. im Main-Code erstellst du dann eine struct und übernimmst/-gibst
> diese dann immer in die Funktionen? Für die Initialisierung ebenfalls
> (wenn ich das richtig verstanden habe).

Richtig. Hier ein Beispiel:
1
struct my_struct{
2
  int WertA;
3
  int WertB; /* geht den Anwender nix an */
4
};
5
6
7
struct my_struct my_struct_init(int WertA){
8
  return (struct my_struct){WertA, 42};
9
}
10
11
12
int my_struct_get_a(struct my_struct * Struct){
13
  return Struct->WertA;
14
}
15
16
17
void my_struct_do_something(struct my_struct * Struct){
18
  Struct->WertA *= 2;
19
  Struct->WertB++;
20
}
21
22
23
int main(void){
24
  struct my_struct Alt = {123, 42};
25
  struct my_struct Neu = my_struct_init(123);
26
  
27
  Alt.WertA *= 2;
28
  Alt.WertB++;
29
  my_struct_do_something(&Neu);
30
  
31
  int X = Alt.WertA;
32
  int Y = my_struct_get_a(&Neu);
33
  
34
  return 0;
35
}


Sieht auf den ersten Blick umständlich und aufgebläht aus. Hat aber den 
Vorteil, dass du Funktionalität kapselst und so Änderungen einfacher 
durchführen kannst. Du kannst das gesamte Modul über den Haufen werfen 
und neu programmieren, solange sich das Modul gleich verhält wie vorher, 
musst du nichts anderes anpassen. Bei direktem Zugriff auf Member muss 
sich nur ein Datentyp in der Struktur ändern und du musst im schlimmsten 
an zig Stellen in deinem Programm Anpassungen oder Prüfungen vornehmen.

Das war dann auch der Punkt, wo ich auch für kleine uCs auf C++ 
umgestiegen bin, da die Sprache dieses Vorgehen stark vereinfacht. 
Bringt aber viele, eher sehr viele, neue und andere Dinge hinzu, die das 
Leben nicht unbedingt vereinfachen.


EDIT: Nicht falsch verstehen, ich mache das nicht immer so, nur 
meistens. Für eine Struktur, die nur Werte halten muss (z.B. 
RGB-Farben), treibe ich diesen Aufwand nicht.

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


Lesenswert?

Be S. schrieb:
> Alles was universell verwendbar ist, wandert in den lib Ordner. Dazu
> zählen auch z.B. I2C, UART,

Ach..
Das möchte ich sehen.

Nach meiner Erfahrung sind gerade Peripherie-Treiber eben nicht 
universell verwendbar, dank unterschiedlicher Interruptvektoren und 
anderer Kleinigkeiten. Das Einzige, was da universell ist bzw. bei MIR 
so universell wie möglich gestaltet wird, ist die Schnittstelle des 
Treibers zu den Anwendungsschichten, als kurz: das Headerfile. Und eben 
diese Schnittstelle sorgt dafür, daß die höheren Programmebenen 
weitestgehend unabhängig sind von dem konkreten Controller, so daß man 
zum Portieren sich fast nur die Peripherie-treiber vorknöpfen muß.

Ansonsten gönne ich mir für jedes Projekt ein völlig separates 
Directory, damit eventuell nötige Anpaßarbeiten nicht auf andere 
Projekte durchschlagen.

W.S.

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.