Forum: Mikrocontroller und Digitale Elektronik Mehrdimensionale Felder mit verschiedenen Zeilenlängen?


von Torsten B. (torty)


Lesenswert?

Hallo Gemeinde

Kann ich in C ein mehrdimensionales Feld definieren, in dem zB die 
Zeilen verschiedene Längen haben ?

Beispiel

Feld(x,y)

xxxxxxxxxxxxxxxxxx
xxxxxxxxx
xxxx
xxxxxxxxxxxxxx
xxxxxx

Habe die Aufgabe im Augenblick mit einer Struktur gelöst, wo das ja kein 
Problem ist.

Um den Quellcode klein zu halten, würde ich die Funktion, die die Zeilen 
mit Daten füllen, aber gerne nacheinander mit einer Schleife abarbeiten.

Das geht bei einer Struktur doch nicht, oder ???


Klar könnte ich ein xy-Feld bestimmen, in dem alle Zeilen die maximal 
vorkommende Länge haben, aber das finde ich stumpf.


Danke
Torsten

von Ralf G. (ralg)


Lesenswert?

Torsten B. schrieb:
> Klar könnte ich ein xy-Feld bestimmen, in dem alle Zeilen die maximal
> vorkommende Länge haben, aber das finde ich stumpf.
Bei einem Feld geht's nicht anders.

Torsten B. schrieb:
> Um den Quellcode klein zu halten, würde ich die Funktion, die die Zeilen
> mit Daten füllen, aber gerne nacheinander mit einer Schleife abarbeiten.
Die Länge jedes einzelnen Teiles müsstest du ja trotzdem irgenwo 
herholen.

Für kurzen Code (braucht aber mehr RAM) könnte man vielleicht sowas 
machen:
Zusätzlich in deine Struktur ein Feld von Pointern auf die 'Zeilen', 
evtl. kombiniert mit der Zeilenlänge. Dann kannst du das in einer 
Schleife abarbeiten (entweder über Index, oder NULL-Pointer als 
Endekennzeichen).

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Ka wozu du das brauchst, aber machs doch so:
Definier dir die Zeilenarrays und pack Zeiger auf diese in das 
Spaltenarray.
1
int zeile1[5];
2
int zeile2[10];
3
int* spalte[2] = {zeile1, zeile2};
4
5
//Zugriff:
6
int blah = spalte[0][0];

Musst dann nur höllisch aufpassen, dasse nicht bei nem Zeilenarray 
daneben greifst.

von Udo S. (urschmitt)


Lesenswert?

Man müsste mal mehr sehen um was es eigentlich geht. Wenn es eine 
Struktur ist liegt es eigentlich nahe, daß die Daten unterschiedlicher 
Art sind. Dann macht es keinen Sinn in einer Schleife 
umzukopieren/befüllen.
Wenn alles Strings sind, ist es vieleicht einfacher ein Array von 
Stringpointern zu benutzen und nicht zu kopieren sondern nur die Zeiger 
zu setzen.

Torsten B. schrieb:
> Um den Quellcode klein zu halten,
Ist nicht immer die beste Lösung. Meist geht das einher mit Tricks die 
man nach einem Jahr selbst nicht mehr versteht.

von Ralf G. (ralg)


Lesenswert?

Udo Schmitt schrieb:
> Man müsste mal mehr sehen um was es eigentlich geht.

Das wird er sicher gleich verraten. In der vorliegenden Form sehe ich da 
auch keinen Sinn. Aber vielleicht werden die Daten von irgendwoher erst 
empfangen.

Udo Schmitt schrieb:
> Torsten B. schrieb:
>> Um den Quellcode klein zu halten,
> Ist nicht immer die beste Lösung. Meist geht das einher mit Tricks die
> man nach einem Jahr selbst nicht mehr versteht.

sowas dann:
Martin Wende schrieb:
> Musst dann nur höllisch aufpassen, dasse nicht bei nem Zeilenarray
> daneben greifst.

von Torsten B. (torty)


Angehängte Dateien:

Lesenswert?

:-)
Ich bastel mal wieder an einem NMEA Parser, der ohne strsep & Co 
auskommt, da ich damit immer Probleme hatte. Dachte ich zumindest ...
Probleme in der Hauptanwendung sind jetzt auch mit diesem nicht weg, 
aber jetzt weiß ich wenigstens, dass es nicht daran gelegen hat ;-)

Die Anwendung ist:
GPS Daten vom Modul empfangen und über RF12 zu einem anderen uC senden.
Übeltäter ist aber die Empfangsfuktion.


Hab den neuen Parser mal angehängt.
Hier sieht man, dass ich sehr oft das gleiche mache. Eben bis auf die 
Angabe der Strukturfelder.
Wollte eben eine Struktur verwenden, da man die sehr schön (bei einer 
späteren LCD Ausgabe angeben kann.
2D char Array würde hier auch gehen.

Der Code ist zum Zerreißen freigegeben... gehts besser ?

Grüße
Torsten

von Udo S. (urschmitt)


Lesenswert?

Ist so doch viel besser. Überlege dir, statt Bla.Longitude hättest du 
Bla[4]
Super selbsterklärend oder?

Was mir an deiner Funktion ParsGPGGA() auffällt, wenn du statt void ein 
int zurückgibst könntest du damit dem Aufrufer Fehlerfreiheit oder nicht 
signalisieren. Was machst du z.B. wenn ein Datensatz nicht vollständig 
ist?

Hier
while (*GPGGA_Ptr != ',')
solltest du zusätzlich überall prüfen ob du nicht schon am Stringende 
bist (Erkennen eines unvollständigen Datensatzes oder eines fehlerhaften 
wo ein Komma fehlt) Auch in dem Fall einen Fehler returnen.
Um es noch sicherer zu machen solltest du auch alle Laufvariablen i 
überprüfen dass sie (bei einem fehlenden Komma) nicht über das jeweilige 
Ende des Strings in der Struktur rausschreiben.
Dein main ist etwas strange, aber ist wohl nur zum debuggen so.

von Uli T. (avaron)


Lesenswert?

Was ebenfalls funktioniert ist, du legst eine Struktur mit dem attribut 
PACKED (unterschiedlich von Compiler zu Compiler) über die gesamte 
Datenstruktur... Dann müsstest Du nur zwischendrin Füllbytes für die 
Kommatas haben....

Also wenn Deine Daten so im Speicher stehen:

char data[12] = "AAAAA,BB,CCC"

dann

struct sData {
   char A[5];
   char delimiter1;
   char B[2];
   char delimiter2;
   char C[3];
} PACKED




Später:

sData my_test;

my_test = data; entweder im gleichen Speicher auslesen oder
 eben mit z.B. memcpy kopieren....

von Uli T. (avaron)


Lesenswert?


von Udo S. (urschmitt)


Lesenswert?

Man könnte auch beim Parsen alle Kommas einfach durch 0 Bytes ersetzen 
und nur die Zeiger auf die entsprechenden Speicherstellen in das Struct 
schreiben.
Aber Vorsicht, wenn man den Speicher dann mit einem neuen Datensatz 
überschreibt ist das Struct vor dem erneuten parsen nicht mehr gültig.

von Karl H. (kbuchegg)


Lesenswert?

Torsten B. schrieb:

> Der Code ist zum Zerreißen freigegeben... gehts besser ?

Nehmen wir mal an, du bleibst bei dieser Umkopiererei in die Struktur, 
was ja grundsätzlich nicht schlecht ist.

Natürlich gehts dann noch besser.
Anstatt im Prinzip immer gleichen Code zu duplizieren, steckt man diesen 
Code in eine Funktion, wählt entsprechend nützliche Argumente und schon 
.... schrumpft dein Code wieder zusammen und ist genauso kurz.

C gibt dir einen Satz Werkzeuge an die Hand. Benutzen musst du sie aber 
schon selber
1
void ParsGPGGA(char *GPGGA_Ptr, struct _gpgga *GPGGA)
2
{
3
  char FeldNr = 0;
4
5
  if (*GPGGA_Ptr =='$')
6
  {
7
    ExtractString( GPGGA->Name,       sizeof(GPGGA->Name),       &GPGGA_Ptr );
8
    ExtractString( GPGGA->UTC,        sizeof(GPGGA->UTC),        &GPGGA_Ptr );
9
    ExtractString( GPGGA->Latitude,   sizeof(GPGGA->Latitude),   &GPGGA_Ptr );
10
    ExtractString( GPGGA->N_S,        sizeof(GPGGA->N_S),        &GPGGA_Ptr );
11
    ExtractString( GPGGA->Longitude,  sizeof(GPGGA->Longitude),  &GPGGA_Ptr );
12
    ExtractString( GPGGA->E_W,        sizeof(GPGGA->E_W),        &GPGGA_Ptr );
13
    ExtractString( GPGGA->Quality,    sizeof(GPGGA->Quality),    &GPGGA_Ptr );
14
    ExtractString( GPGGA->NumbSat,    sizeof(GPGGA->NumbSat),    &GPGGA_Ptr );
15
    ExtractString( GPGGA->HorPrec,    sizeof(GPGGA->HorPrec),    &GPGGA_Ptr );
16
    ExtractString( GPGGA->HightNN,    sizeof(GPGGA->HightNN),    &GPGGA_Ptr );
17
    ExtractString( GPGGA->Reference1, sizeof(GPGGA->Reference1), &GPGGA_Ptr );
18
    ExtractString( GPGGA->HightGeo,   sizeof(GPGGA->HightGeo),   &GPGGA_Ptr );
19
    ExtractString( GPGGA->Reference2, sizeof(GPGGA->Reference2), &GPGGA_Ptr );
20
    ExtractString( GPGGA->Empty1,     sizeof(GPGGA->Empty1),     &GPGGA_Ptr );
21
    ExtractString( GPGGA->Empty2,     sizeof(GPGGA->Empty2),     &GPGGA_Ptr  );
22
  }
23
  else 
24
  {
25
  //wenn String nicht mit "$" beginnt, Fehlerkennung eintragen und tschüss
26
    strcpy( GPGGA->Name, "?" );
27
  }
28
}

Jetzt schreibst du dir noch die Funktion ExtractString, die den nächsten 
Teilstring extrahiert und auf das angegebene Ziel umkopiert und bist 
fertig. Und das ohne das dir der Code in der Größe explodiert.

(und natürlich baust du in der Funktion dann auch die Fehlerbehandlung 
und Sicherungen gegen Array-Overflows mit ein. Dazu kriegt die Funktion 
ja die Arraygröße mitübergeben, damit sie das tun kann)
1
void ExtractString( char* Ziel, int LenZiel, char** Quelle)
2
{
3
  ..... dein Code hier
4
}

Anstatt deine Zeit in Code-Duplikate zu stecken und darauf zu achten, 
dass du alle notwendigen Stellen auch wirklich überall korrekt ersetzt 
hast, hättest du deine Zeit besser mal darin investiert, zu überlegen, 
wie man diesen algorithmisch immer gleichen Teil in einer eigenen 
Funktion abhandeln kann.



Und: eine Programmiersprache ist nur ein Werkzeug. Das muss man aber 
kennen, um es effizient benutzen zu können!
Kauf dir ein C-Buch und arbeite es durch.
Sowas
1
   (*GPGGA).Name[i] = ....
ist doch schauderhaft! Es ist zwar nicht falsch, aber es ist nicht die 
Schreibweise, die man in C wählt. Extra dafür wurde ein eigenes Symbol 
eingeführt, damit man eben nicht diese schauderhafte und schlecht zu 
tippende Notation benutzen muss

    (*x).y     ist identisch zu      x->y

von Torsten B. (torty)


Lesenswert?

Vielen Dank für die Wertvollen Hinweise.
Ja ich habe noch viel zu lernen, aber leider nur die Zeit und die Muse 
mich in solchen Hobby-Progrämmchen mit dem Erlernen der Sprache zu 
beschäftigen.

Bin immer für konstruktive Kritik zu haben.
Von heute an werder ich wohl einen Kniff mehr im Petto haben und 
GPGGA->Empty2 schreiben.

Danke
Torsten

von Karl H. (kbuchegg)


Lesenswert?

Torsten B. schrieb:
> Vielen Dank für die Wertvollen Hinweise.
> Ja ich habe noch viel zu lernen, aber leider nur die Zeit und die Muse
> mich in solchen Hobby-Progrämmchen mit dem Erlernen der Sprache zu
> beschäftigen.

Du würdest dir viel Zeit sparen, wenn du nicht anlassbezogen Lernen 
würdest, sondern zumindest die Grundlagenkapitel (das sind ungefähr die 
ersten 30 bis 40% der Kapitel) in einer vernünftigen Literatur 
durcharbeiten würdest.
Das kostte dich jetzt zwar ein paar Wochen, aber diese Zeit holst du ab 
dann locker wieder rein.

von Uli T. (avaron)


Lesenswert?

Ich würde hier in diesem speziellen Fall wirklich lieber eine packed 
struct drüberlegen, anstelle es in funktionen zu parsen... ist halt 
einfach schneller...
Und da sich heir das Format ja niemals ändern wird ist da auch nix gegen 
zu sagen..

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.