Forum: Compiler & IDEs Hilfe bei qsort


von Michael (Gast)


Lesenswert?

Hallo,

ich benötige Hilfe bei qsort.

typedef struct
{
  u08 nr;
  char name[9];
} TEMP;
TEMP sortlist[100];

die Daten sind z.B. so belegt:

sortlist[0].nr=0;
sortlist[0].name={"zbc"};
sortlist[1].nr=1;
sortlist[1].name={"bcd"};
sortlist[2].nr=2;
sortlist[2].name={"aaa"};
sortlist[3].nr=3;
sortlist[3].name={"xa"};

nach der Sortierung sollte es so aussehen:

sortlist[0].nr=3;
sortlist[0].name={"zbc"};
sortlist[1].nr=1;
sortlist[1].name={"bcd"};
sortlist[2].nr=0;
sortlist[2].name={"aaa"};
sortlist[3].nr=2;
sortlist[3].name={"xa"};

jetzt muß man eine compare funktion schreiben und mit qsort aufrufen.
Ich habe schon einige Versuche hintermir und komme nicht mehr weiter:-(

von Karl H. (kbuchegg)


Lesenswert?

Michael wrote:
>
> nach der Sortierung sollte es so aussehen:
>
> sortlist[0].nr=3;
> sortlist[0].name={"zbc"};
> sortlist[1].nr=1;
> sortlist[1].name={"bcd"};
> sortlist[2].nr=0;
> sortlist[2].name={"aaa"};
> sortlist[3].nr=2;
> sortlist[3].name={"xa"};

So sehr ich auch suche, aber dein Sortierkriterium erschliest
sich mir überhaupt nicht. Das ist weder nach name noch nach nr
sortiert. Wenn überhaupt, könnte man noch die Stringlänge als
absteigend sortiert betrachten. Aber da jetzt plötzlich die
Datensätze die vorher zusammengehörten (0 und zbc) auseinandergerissen
sind, würde ich mal schätzen:
Versuch das noch mal neu zu formulieren.

von Michael (Gast)


Lesenswert?

Es sind 100 strings unsortiert im eeprom abgelegt.
um diese sortiert am Display anzuzeigen muß ich sortieren.
Vor der Ausgabe brauche ich eine Liste die mir die richtige Reihenfolge 
vorgibt um die Daten aus dem EEprom zu lesen.

von Uhu U. (uhu)


Lesenswert?

Gehen wir mal davon aus, daß du nach name sortieren willst - aus deinen 
Postings geht das ja leider nicht klar hervor, dann sieht die 
Vergleichsroutine so aus:

int cmp(char *str1, char *str2) {
   return strcmp(str1, str2);
}

Du könntest also auch strcmp an qsort übergeben... Möglicherweise wirst 
du den Funktionspointer nach int (*)(void *, void *) casten müssen.

von Karl H. (kbuchegg)


Lesenswert?

Michael wrote:
> Vor der Ausgabe brauche ich eine Liste die mir die richtige Reihenfolge
> vorgibt um die Daten aus dem EEprom zu lesen.

Alles klar. Du willst einen Index aufbauen und den Index so
umsortieren, dass die Indexverweise sortiert sind.

Wo ist das Problem?
Du definierst dir ein Array von char Pointern.
In dieses Array schreibst die die EEProm-String-Adressen hinein.

Mittels qsort wird dann dieses Hilfsarray sortiert, wobei die
Compare Funktion diesen Pointer auswertet und den String-
Vergleich (mit den Daten aus dem EEPRom) durchführt.
Compare muss zurück liefern:
   +1   wenn der erste String größer war als der zweite
   -1   wenn der erste String kleiner war als der zweite
    0   wenn die beiden Strings gleich sind.

Nach dem qsort hast du dann ein Array von character Pointern,
die du nur noch der Reihe nach durchgehen musst, um die Strings
in der richtigen Reihenfolge zu bekommen.

Jetzt mal ohne EEPROM, dafür direkt im Speicher:

Du hast eine Array mit den Strings:
  char Strings[4][] = { "abcd",
                        "defg",
                        "xc",
                        "1234",
                      };

dann gibt es weiters dein IndexArray.

char* Index[4];

das befüllst du mal mit Pointern in der richtigen Reihenfolge

  for( i = 0; i < 4; ++i )
    Index[i] = Strings[i];

Für den qsort brauchst du die Compare Funktion. Komplett
sieht das dann so aus:
1
char* Strings[4] = { "abcd",
2
                     "defg",
3
                     "xc",
4
                     "1234",
5
                   };
6
7
char* Index[4];
8
9
int Compare( const void* Arg1, const void* Arg2 )
10
{
11
  char* pString1 = *(char**) Arg1;
12
  char* pString2 = *(char**) Arg2;
13
  
14
  return strcmp( pString1, pString2 );
15
}
16
17
int main()
18
{
19
  int i;
20
  
21
  for( i = 0; i < 4; ++i )
22
    Index[i] = Strings[i];
23
24
  qsort( Index, 4, sizeof *Index, Compare );
25
26
  for( i = 0; i < 4; ++i )
27
    printf( "%s\n", Index[i] );
28
29
  return 0;
30
}

Wenn du nicht mit einem Pointer Index arbeiten möchtest, dann
würde das so aussehen:
1
char* Strings[4] = { "abcd",
2
                     "defg",
3
                     "xc",
4
                     "1234",
5
                   };
6
7
int Index[4];
8
9
int Compare( const void* Arg1, const void* Arg2 )
10
{
11
  int Index1 = * (int*) Arg1;
12
  int Index2 = * (int*) Arg2;
13
  
14
  return strcmp( Strings[Index1], Strings[Index2] );
15
}
16
17
int main()
18
{
19
  int i;
20
  
21
  for( i = 0; i < 4; ++i )
22
    Index[i] = i;
23
24
  qsort( Index, 4, sizeof *Index, Compare );
25
26
  for( i = 0; i < 4; ++i )
27
    printf( "%s\n", Strings[Index[i]] );
28
29
  return 0;
30
}

Entscheidend ist, dass du dir in der Compare Funktion den
übergebenen Pointer wieder auf den richtigen Datentyp
zurückcasten musst. Die Compare Funktion bekommt immer
einen Pointer auf den Datentyp, aus dem das Array besteht.
Besteht das Array aus int, so kriegt die Compare Funktion
jeweils einen Pointer auf int. Besteht dein Array aus double,
so kriegt die Compare Funktion einen Pointer auf double.
Besteht das Array aus char*, so kriegt die Compare Funktion
einen Pointer auf char*. Besteht das Array aus struct irgendwas,
so kriegt die Compare Funktion Pointer auf struct irgendwas.
Der jeweilige Pointer muss wieder in den richtigen Datentyp
zurückgewandelt werden und dann dereferenziert werden um
an die eigentlichen zu sortierenden Daten heranzukommen.
Mit denen machst du dann an Vergleichen was auch immer
notwendig ist, um die Sortierreihenfolge zu bestimmen.

von Michael (Gast)


Lesenswert?

WOW vielen Dank für Deine Ausführung Karl Heinz.
Das werde ich mal durcharbeiten und so versuchen, deine Hilfe hat noch 
immer zum Ziel geführt.
Besten Dank

von Michael (Gast)


Lesenswert?

Irgendwo habe ich doch noch was falsch, das Ergebnis stimmt noch nicht.

So habe ich es jetzt eingefügt:
1
char* Namen[100];
2
3
int Compare( const void* Arg1, const void* Arg2 )
4
{
5
  int Index1 = * (int*) Arg1;
6
  int Index2 = * (int*) Arg2;
7
  
8
  return strcmp( Namen[Index1], Namen[Index2] );
9
}
10
11
12
 //Mit Namen aus dem EEprom laden number = Anzahl der Einträge
13
 for(x=0;x<number;x++)
14
 {
15
  readwp(x+1);
16
  strncpy(Namen[x],WayPoint.name,8);
17
 }
18
19
for( i = 0; i < number; ++i )Index[i] = i;
20
21
  qsort( Index, number, sizeof *Index, Compare );
22
23
24
//Daten sortiert ausgeben
25
readwp(Index[pos]);

von Karl H. (kbuchegg)


Lesenswert?

Michael wrote:
> Irgendwo habe ich doch noch was falsch, das Ergebnis stimmt noch nicht.
>
> So habe ich es jetzt eingefügt:
>
1
> 
2
> char* Namen[100];
3
> 
4
> int Compare( const void* Arg1, const void* Arg2 )
5
> {
6
>   int Index1 = * (int*) Arg1;
7
>   int Index2 = * (int*) Arg2;
8
> 
9
>   return strcmp( Namen[Index1], Namen[Index2] );
10
> }
11
> 
12
> 
13
>  //Mit Namen aus dem EEprom laden number = Anzahl der Einträge
14
>  for(x=0;x<number;x++)
15
>  {
16
>   readwp(x+1);
17
>   strncpy(Namen[x],WayPoint.name,8);
18
>

Aeh. Nein, das geht so nicht. (Mal ganz davon abgesehen,
dass es sich immer um den gleichen Waypoint Namen handelt)

Hinter Namen verbirgt sich ja keine Speicherfläche, in der
du einen String speichern könntest. So wie du das hast, kann
in Namen lediglich ein Pointer abgespeichert sein. Zb. Die
Speicheradresse des Strings im EEPROM (Wink, Wink).

Mit anderen Worten: Kopiere die Strings nicht vom EEPROM
in den SRAM Speicher:
1) Braucht das ziemlich viel Platz im SRAM
2) denke ich, dass das in Summe (*) auch nicht schneller ist,
   als wie wenn du die ganze Vergleichsfunktion auf Daten im
   EEPROM ausführen lässt.
   Dazu kannst du dann aber nicht strcpy benutzen, da strcpy
   die Strings im SRAM erwartet und nicht im EEPROM.
3) Wenn deine Waypoint namen sowieso immer nur 8 Zeichen lang
   sein können, kannst du ja auch die Indexmethode mit int
   (oder unsigned char) anstelle von char Pointern benutzen.
   Du kannst ja aus der Startadresse der Waypoints und einer
   Indexnummer ganz leicht die Speicheradresse (im EEPROM)
   berechnen, an der der Name des Waypoints gespeichert ist.
   Das benutzt du dann in der Compare Funktion um die Adresse
   im EEPROM zu bestimmen an der der erste zu vergleichende
   Namen gespeichert ist. Selbiges für den zweiten Namen.
   Diese beiden werden dann miteinander verglichen. Aber nicht
   mit strcpy, sondern da wirst du dir eine eigene Funktion
   machen müssen, die das kann und ein Ergebnis in Analogie
   zu strcpy liefert.

(*) Mit in Summe meine ich: Den kompletten Vorgang des Kopierens
  aller Strings vom EEPROM in den SRAM. Die Compare Funktion auf
  Daten im EEPROM ist zwar langsamer als wie wenn die Daten im
  SRAM liegen würden. Allerdings wird das dadurch aufgewogen,
  dass die Umkopieraktion ja auch Zeit braucht.

von Michael (Gast)


Lesenswert?

hab ich einigermasen verstanden.
bei readwp() wird ein ganzer Datensatz (struct) gelesen, somit steht in 
Waypoint.name immer ein anderer Namen.
Die Speicheradresse berechnen ist leicht.
Startadresse = FIRSTWP = 0x40
LENGTHOFWP = 0x13

von Karl H. (kbuchegg)


Lesenswert?

Michael wrote:
> hab ich einigermasen verstanden.
> bei readwp() wird ein ganzer Datensatz (struct) gelesen, somit steht in
> Waypoint.name immer ein anderer Namen.

Spaetestens jetzt rächen sich die globalen Variablen.
Es wäre besser, wenn readwp ganz einfach eine struct returnieren
würde, anstatt eine globale Variable befüllen. Sowas ist immer
schlecht.

Das ganze mit einem byte Array für die Indizes (Dein Maximum
liegt ja bei 100, und das passt locker in ein Byte).

Dann könntest du in der Compare Funktion schreiben
1
int Compare( const void* Arg1, const void* Arg2 )
2
{
3
  uint8_t Index1 = * (uint8_t*) Arg1;
4
  uint8_t Index2 = * (uint8_t*) Arg2;
5
  
6
  struct Wayp Point1 = readwp( Index1 );
7
  struct Wayp Point2 = readwp( Index2 );
8
  
9
  return strcmp( Point1.name, Point2.Name );
10
}
11
12
int main()
13
{
14
  uint8_t SortIndizes[100];
15
  uint8_t i;
16
  struct Wayp ThePoint;
17
18
  for( i = 0; i < number; ++i )
19
    SortIndizes[i] = i;
20
21
  qsort( SortIndizes, number, sizeof *SortIndizes, Compare );
22
23
  for( i = 0; i < number; ++i ) {
24
    ThePoint = readwp( SortIndizes[i] );    
25
    printf( "%s\n", ThePoint.name );
26
  }
27
}

Merke: Die richtige Wahl dessen, was eine Funktion macht und wie
sie die Ergebnisse präsentiert, kann einen riesen Unterschied
ausmachen, welche Möglichkeiten du in einem Programm hast.
1
struct Wayp readwp( uint8_t Nr )
2
{
3
  struct Wayp tmp;
4
5
  // mach was auch immer zu tun ist, um den Waypoint mit der
6
  // Nummer Nr zu besorgen und die gelesenen Daten in tmp
7
  // zu speichern
8
9
  return tmp;
10
}

von Michael (Gast)


Lesenswert?

1
typedef struct
2
{
3
 float   laenge,
4
         breite;
5
 s16     alti;
6
 char name[9];
7
} WAYPOINT;//19 Bytes
8
9
WAYPOINT WayPoint;
10
11
//die Funktion readwp(u08 pos)
12
//liest die struct WayPoint an pos aus dem EEprom.
13
//Wenn pos=5 wird der 5te WayPoint ausgelesen.
14
15
//Die Comparefunktion sieht so aus: 
16
int Compare( const void* Arg1, const void* Arg2 )
17
{
18
  u08 Index1 = * (u08*) Arg1;
19
  u08 Index2 = * (u08*) Arg2;
20
  
21
  readwp( Index1 );
22
  WAYPOINT Point1 = WayPoint;
23
  readwp( Index2 );
24
  WAYPOINT Point2 = WayPoint;
25
  
26
  return strcmp( Point1.name, Point2.name );
27
}
28
29
u08 i,SortIndizes[100];
30
 
31
 for( i = 0; i < number; ++i )SortIndizes[i] = i;
32
 qsort( SortIndizes, number, sizeof *SortIndizes, Compare );
33
34
//Auslesen der Wegpunkte in sortierter Reihenfolge:
35
for( i = 0; i < number; ++i )
36
{
37
 readwp( SortIndizes[i] );  
38
 printf( "%s\n", WayPoint.name );
39
}

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.