Forum: PC-Programmierung Struct mit Array - verschiedene Arraygrößen - Wie?


von Wuhu W. (wuhuuu2)


Lesenswert?

Hallo,

ich brauchs für ein Mikrocontroller, aber prizipiell ists ja ne 
allgemeine C-Frage.

Code:
1
#define Buffer_Size = 64
2
3
struct FIFO
4
{
5
 uint8_t data[Buffer_Size];
6
 uint8_t read;
7
 uint8_t write;
8
}
9
10
struct FIFO FIFO_Buffer[2]

Ich habe also einen FIFO Buffer erzeugt. Er funktioniert mit dem Code 
drumrum...

Mein Problem:
- ich fänds gut wenn ich meinen Code zum schreiben und lesen nicht für 
meine 2. Variante ändern muss.
- Ich möchte das mein erzeugtes Array im struct Object verschiedene 
Größen haben kann. Also z.B. FIFO_Buffer[0] hat ein array der größe 32; 
FIFO_Buffer[1] ein Array der größe 64.

Ist das machbar, oder muss ich ein neues struct erzugen und für dieses 
dann den Code neu schreiben/anpassen?

Lg

von Yalu X. (yalu) (Moderator)


Lesenswert?

Du ersetzt in der Struktur data durch einen Zeiger auf einen Puffer.
Dieser Puffer wird dann woanders (statisch oder dynamisch) definiert,
der Zeiger wird mit der Startadresse des Puffers initialisiert.
Zusätzlich erweiterst du die Struktur um ein Element buffer_size.

von Chefkoch (Gast)


Lesenswert?

Es ist nicht nur eine C Frage. Es kommt auch auf den µC an.
Was du bräuchtest ist eine dynamische Speicherverwaltung. Desktop-PC´s, 
Laptops, ähnlich mächtige Systeme haben das oder Betriebsystemverwaltete 
µC´ler KÖNNEN das haben.

Die kleinen 8Bitter die ich kenne, können keinen Speicher dynamisch 
allokieren - mag sein, dass es welche gibt die das können.

Sonst kenne ich vom Anwenden her noch die stm32, die können dynamisch 
allokieren und auch freigeben - ABER wenn man es zu oft macht zerlöchert 
man sich den Heap (Sidekick zu den betriebsverwalteten Systemen, von 
denen manche aufräumen können, kenne mich jedoch nicht weiter aus).

von Wuhu W. (wuhuuu2)


Lesenswert?

Ich bin nich ganz sicher ob ichs verstanden hab..

Meinst du das so??
Und den Pointer auf ein Array oder einen Ringpuffer? .. also n puffer 
erstellen um meinen puffer drauf zeigen zu lassen??
1
#define Buffer_Size = 64
2
3
struct FIFO
4
{
5
 int *data_ptr = &....
6
 uint8_t Buffer_Size;
7
 uint8_t read;
8
 uint8_t write;
9
}
10
11
struct FIFO FIFO_Buffer[2]

Ich glaub ich versteh nich alles. Es wäre cool wenn dus mirs noch anders 
erklären/zeigen(z.B. mit code) könntest.





FYI
Atmega 2560 , mittels AVR-Studio5

: Bearbeitet durch User
von Jay (Gast)


Lesenswert?

1
struct PFUSCH
2
{
3
     uint8_t read;
4
     uint8_t write;
5
     int buffer_size;
6
     uint8_t buffer[1]; // je nach compiler geht auch [0]
7
};
8
9
struct PFUSCH *
10
new_pfusch(int size) {
11
     struct PFUSCH *p = malloc(sizeof(struct PFUSCH) + sizeof(uint8_t) /* ich weiß ... */ * (size - 1));
12
     if(p) {
13
          p->buffer_size = size;
14
     }
15
     return p;
16
}
17
18
struct PFUSCH * p64 = new_pfusch(64);

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wuhu W. schrieb:
> Meinst du das so??

Fast.

> Es wäre cool wenn dus mirs noch anders erklären/zeigen(z.B. mit code)
> könntest.

So könnte bspw. der Code aussehen (mit statischen Puffern, falls du
keine dynamischen Speichert verwenden möchtest):
1
#include <stdint.h>
2
3
// FIFO-Datenstruktur für beliebige Puffergrößen
4
5
struct FIFO
6
{
7
  uint8_t *data_ptr;
8
  uint8_t buffer_size;
9
  uint8_t read;
10
  uint8_t write;
11
};
12
13
// Verschieden große Puffer für drei FIFOs
14
15
uint8_t buffer0[32];
16
uint8_t buffer1[64];
17
uint8_t buffer2[16];
18
19
// Array von drei FIFOs mit Initialisierung der Puffer
20
21
struct FIFO FIFO_Buffer[3] =
22
{
23
  { buffer0, sizeof buffer0, 0, 0 },
24
  { buffer1, sizeof buffer1, 0, 0 },
25
  { buffer2, sizeof buffer2, 0, 0 }
26
};


Den Funktionen zum Schreiben und Lesen der FIFOs wird jeweils ein Zeiger
(z.B. fifo_ptr) auf die FIFO-Datenstruktur übergeben. Innerhalb dieser
Funktionen greift du auf den Puffer mit fifo_ptr->data_ptr[i] zu. Beim
Hochzählen der read- und write-Indizes werden diese jeweils beim
Erreichen von fifo_ptr->buffer_Size auf 0 zurückgsetzt. Alles in Allem
sind an diesen Funktionen wahrscheinlich nur geringfügige Änderungen
notwendig.

von Vlad T. (vlad_tepesch)


Lesenswert?

Jay schrieb:
> struct PFUSCH

wieso pfusch?
das ist doch ein Standardverfahren für Protokolle mit dynamischen 
Datenlängen.

von A. H. (ah8)


Lesenswert?

Genau dafür wurden in C++ die Templates erfunden:

1
template<int BufferSize> class FIFO
2
{
3
                uint8_t data[BufferSize];
4
                uint8_t head;
5
                uint8_t tail;
6
        public:
7
                uint8_t read();
8
                void    write(uint8_t);
9
                bool    is_empty();
10
                bool    is_full();
11
};
12
13
struct {
14
        FIFO<32>        fifo1;
15
        FIFO<64>        fifo2;
16
} FIFO_Buffer;

read und write sind hier Member Functions innerhalb derer Du auf 
BufferSize wie auf eine Konstante zugreifen kannst. Noch eleganter 
wäre es natürlich, wenn man auch gleich noch den gespeicherten Typ zum 
Parameter macht (und für die Indizes würde ich Pointer verwenden, auch 
wenn Du lieber bei C bleiben solltest):

1
template<class T, int BufferSize> class FIFO
2
{
3
                T       data[BufferSize];
4
                T*      head;
5
                T*      tail;
6
        public:
7
                T       read();
8
                void    write(T);
9
                bool    is_empty();
10
                bool    is_full();
11
};
12
13
struct {
14
        FIFO<uint8_t, 32>       fifo1;
15
        FIFO<uint8_t, 64>       fifo2;
16
} FIFO_Buffer;

von Wuhu W. (wuhuuu2)


Lesenswert?

Vielen Dank!
Funktioniert Perfekt.

Musste die Zuweisungen meines Objects
1
struct FIFO FIFO_Buffer[4]=
2
{
3
  {...},
4
  {...}
5
};
 lediglich in .c file machen, gab sonst compilier Fehler.

Wie kann ich meine Objecte der Struktur FIFO in einem .h file 
deklarieren, wenn sie in .c initialisiert werden?

weil wenn ich ja jetzt schreibe
1
struct FIFO FIFO_Buffer[4];
Dann wird ja wieder ein Object erstellt oder?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wuhu W. schrieb:
> weil wenn ich ja jetzt schreibe
>
> struct FIFO FIFO_Buffer[4];
> Dann wird ja
>
> wieder ein Object erstellt oder?

Im Header-File musst du die Variablen als "extern" deklarieren:
1
extern struct FIFO FIFO_Buffer[4];

von Josef (Gast)


Lesenswert?

A. H. schrieb:
> Genau dafür wurden in C++ die Templates erfunden:

Wirklich?

Wird da nicht für jede Instanz des Templates(Oder wie ist der 
Fachausdruck für ein instanziiertes Template?) Code erzeugt?

von Karl H. (kbuchegg)


Lesenswert?

Josef schrieb:
> A. H. schrieb:
>> Genau dafür wurden in C++ die Templates erfunden:
>
> Wirklich?
>
> Wird da nicht für jede Instanz des Templates(Oder wie ist der
> Fachausdruck für ein instanziiertes Template?) Code erzeugt?

Ja.
Aber der springende Punkt ist, dass du den Code nicht selber schreiben, 
respektive mit anderen Zahlenwerten duplizieren, musst.

Letzten Endes gibt es nur 2 Möglichkeiten:
entweder man dupliziert oder man gestaltet es so, dass der dynamische 
Teil nicht Teil der Struktur ist. Will man Elemente in einem Array 
zusammenfassen, dann müssen alle Elemente die gleiche Größe haben. Da 
führt kein Weg daran vorbei.

von Josef (Gast)


Lesenswert?

Karl Heinz schrieb:
> Letzten Endes gibt es nur 2 Möglichkeiten:
> entweder man dupliziert oder man gestaltet es so, dass der dynamische
> Teil nicht Teil der Struktur ist. Will man Elemente in einem Array
> zusammenfassen, dann müssen alle Elemente die gleiche Größe haben. Da
> führt kein Weg daran vorbei.

Schon klar.

Was ich bemängelt habe war dass Templates für diesen Zweck gedacht sind. 
Für jede im Projekt verwendete Puffergröße extra Code zu generieren 
(egal ob mir das der Compiler abnimmt) ist auf einem Mikrocontroller 
wohl nicht der beste Weg (wobei "beste" zu definieren wäre).

von A. H. (ah8)


Lesenswert?

Josef schrieb:
> Was ich bemängelt habe war dass Templates für diesen Zweck gedacht sind.
> Für jede im Projekt verwendete Puffergröße extra Code zu generieren
> (egal ob mir das der Compiler abnimmt) ist auf einem Mikrocontroller
> wohl nicht der beste Weg (wobei "beste" zu definieren wäre).

Einverstanden, eine Aussage wie: auch dafür wurden Templates einst 
erfunden wäre sicherlich richtiger gewesen, schon deshalb, weil Typen 
als Template-Parameter damals eine mindestens ebenso große Rolle 
gespielt haben dürften wie numerische.

Das Templates zwangsläufig zu schlechterem Code führen müssen – wobei 
ich „schlechter“ hier mal als gleichbedeutend mit „länger“ verstehe – 
würde ich so aber nicht unterschreiben. Denn durch das Einbringen von 
zusätzlichen Informationen ins das Typsystem eröffnen sich dem Compiler 
ja auch neue Optimierungsmöglichkeiten. Welcher Effekt hier stärker zum 
Tragen kommt hängt natürlich vom Einzelfall ab, hier konkret von der 
Anzahl der verschiedenen Puffergrößen. Sollten dass nur zwei oder drei 
sein, wobei auf jeden Puffer vielleicht nur an einer Stelle geschrieben 
und an nur einer anderen gelesen wird, eine inline-Funktion also ohnehin 
die bessere Variante wäre, dann könnte ich mir durchaus vorstellen, dass 
die Template-Version sogar den kürzeren Code liefert, zumindest aber 
keinen unakzeptabel längeren. Sie kann dann die Puffergröße als 
Konstante in den Code compilieren, statt auf eine Variable zugreifen zu 
müssen. Natürlich wird sie mit zunehmender Anzahl an Puffergrößen immer 
schlechter abschneiden.

Ich hatte vor einigen Jahren mal ein Projekt mit einem nicht 
unerheblichen Anteil an Grafikprogrammierung. Dafür war ich auf der 
Suche nach einer Vektor-Klasse mit einer festen Anzahl Komponenten. Die 
kann man grundsätzlich entweder als Array mit Schleifen oder als 
Struktur bzw. in diesem Fall als rekursives Template mit entsprechend 
generierten Anweisungssequenzen implementieren. Natürlich machen 
Schleifen bei Vektoren für zwei- oder dreidimensionale Räume wenig Sinn 
und so verwundert es nicht, dass die Template-Variante trotz mehrfacher 
Anweisungen fast immer kürzeren Code geriert hat. (Allerdings kann es 
sein, dass ein guter Compiler kurze Schleifen auch heraus optimiert und 
durch Sequenzen ersetzt.) Erst ab vier Dimensionen zeigten sich messbare 
Unterschiede in der Codegröße. Bezüglich der Laufzeit war die 
Template-Variante natürlich in jedem Fall überlegen.

Es gibt übrigens auch Techniken, Stichwort: partielle Spezialisierung, 
mit denen kann man z.B. für Vektoren festlegen, dass ein-, zwei- und 
dreidimensionale sequenziell, alle anderen iterativ implementiert 
werden. Die iterative Variante lässt sich dann auch als generische 
Version implementieren. Der für die jeweilige Spezialisierung 
generierter Code ist dann nur noch ein inline Wrapper, der die 
generische Funktion typsicher mit den richtigen Parametern aufruft. Das 
dürfte dann auch auf einem μC keine Wünsche mehr offen lassen.

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.