PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] String Array



oberallgeier
19.03.2013, 23:51
Wie kann ich verschiedene (gleich lange) Strings in einem Array so ablegen, dass ich sie mit einem Pointer, z.B. txtptr {1..4} anwählen kann. Derzeit verwende ich z.B. einzelne Strings der Art:


unsigned char ML1[] EEMEM = "Taster gedrückt!"; // Meldetext
die ich natürlich mit dem expliziten Namen ML1 oder so aufrufen muss.

suche etwas wie dies
??? EEMEM = " auf "
??? EEMEM = " ab "
??? EEMEM = " zu "
??? EEMEM = "mist "

mit dem jeweils gleichen Namen, aber einem wählbaren Pointer : emma[2]. Entsprechende Felder mit Zahlen bekomme ich ja hin:


uint16_t ESeroff[] EEMEM = { 0, 0,0,0, 0,0,0, 0,0,-12, 19, 0, 0 } ;

Seit Tagen suche ich immer wieder nach ner Lösung mit Strings statt Zahlen, finde in keinem Tutorial und nicht im K&R etwas hilfreiches. Etliche Experimente sind fehl geschlagen.

Danke für Hilfe

Valen
20.03.2013, 00:36
http://stackoverflow.com/questions/1088622/how-do-i-create-an-array-of-strings-in-c

Klebwax
20.03.2013, 02:03
Ich glaube deine Frage zu verstehen und versuche daher mal eine Antwort.

Es gibt keinen Datentyp string in C. In anderen Sprachen schon. Als String wird in C ein Array of Chars bezeichnet. Was du also brauchst ist ein Array of Arrays of Chars, anders gesagt ein zweidimensionales Array of Chars, etwa so:

char Texte[5][10];

Das sind 5 Texte mit einer jeweiligen Länge von maximal 9 Zeichen plus der Endekennung durch '\0'.

Wie geht man damit um? Hier ein Beispiel:


char texte[][10] = {"eins", "zwei", "drei"};

int main(int argc, char** argv) {

printf("%s\n", texte[1]);

strcpy(texte[1], "neu");

printf("%s\n", texte[1]);
}


Dazu ein paar Bemerkungen: Die erste Dimension des zweidimensionalen Arrays kann man sich vom Compiler auszählen lassen, daher ist beim Initializer die erste eckige Klammer leer. Die zweite muß man selber einsetzen. Sie muß so groß sein, daß der längste Text (plus '\0') reinpasst.

So wie bei char Text[10], Text die Adresse des Anfangs des Arrays ist, die man z.B. mit printf("%s", Text) ausgibt, ist texte[1] die Adresse des zweiten Char-Arrays oder Strings.

Ich bin sicher nicht der große Lehrbuchschreiber, hoffe aber doch daß ich das verständlich formuliert habe und daß es dein Problem trifft.

Achso, die Ausgabe des obigen Programms ist:

zwei
neu

MfG Klebwax

oberallgeier
20.03.2013, 10:24
... hoffe .. verständlich formuliert habe und daß es dein Problem trifft ...Danke Klebwax; immerhin bist Du auf dem besten Weg zum großen Lehrbuchschreiber *kompliment* - es funktioniert wirklich gut. Danke für die schnelle Hilfe und diese wirklich saubere, verständliche Beschreibung - komplett mit Anwendungsbeispiel. Ich hatte (schon mal als grundlegenden Fehler, trotz K&R-Nachhilfe) beim zweidimensionalen Array schon mal die Koordinaten verwechselt :-/ - aber dank Deiner Ausführung klappte es praktisch schon auf Anhieb. Vielen Dank, hat mir wieder mal STUNDEN gespart.
Anmerkung: die zitierten Codezeilen stammen aus verschiedenen Modulen, ich hoffe, dass die Lösung/Problematik trotzdem klar dargestellt wird.


// - - - - - - - - - -
char SVdef [][6]
EEMEM = { " ", "Ko+/-", "Kor/l", "KoNir", "KoNil",
"ADeur", "ADeul", " ", "ADe o", "Au rl", "Au +-" } ; // - - - - - - - - - -
void uart0_eep_string( char data [6] ) ; // EEPROM-String an uart0
// - - - - - - - - - -
// ================================================== ============================ =
// == Ausgabe Text aus EEPROM an uart0
// ================================================== ============================ =
void uart0_eep_string( char data [6] )
{ //
char c;
while(1) // String *data an uart0
{ //
c = eeprom_read_byte(data); //
if ( c == 0 ) return; //
uputchar0 ( c ); // Ein Zeichen an uart0
data++; //
} // Ende while(1)
} // Ende void uart0_eep_string(const unsigned char *data)
// ================================================== ============================ =
// ================================================== ============================ =
// - - - - - - - - - -
// ... im Main/
uart0_eep_string(SVdef [5]);
//...

bringt ins Terminalfenster
............ADeur

Und das trotz Warnungen. Da ich Warnungen selbst in minimaler Anzahl hasse bitte ich um weitere Hilfe/Aufklärung.

../KoCo_inf15.c: In function 'uart0_eep_string':
../KoCo_inf15.c:89: warning: pointer targets in passing
...... argument 1 of '__eerd_byte_m1284p' differ in signedness
Build succeeded with 1 Warnings...

function 'uart0_eep_string':
../KoCo_inf15.c:89:
c = eeprom_read_byte(data); //


Das krieg ich nicht gebacken. Hoffentlich habe ich trotz der Knappheit alles Wesentliche zum Fehler dargestellt.

Danke im Voraus für die Hilfe.

Valen
20.03.2013, 10:38
Was ist das Datentyp von data in den Funktion eeprom_read_byte(data)? Schau uns bitte mal den Funktion Header davon.

Data ist ein char. Aber laut den Warnung könnte eeprom_read_byte ein signed char erwarten.

oberallgeier
20.03.2013, 11:38
Was ist das Datenyp von data in den Funktion eeprom_read_byte(data) ...Danke Valen, Du hast Recht - trotzdem . . .

1) die Funktion ist die Bibliotheksfunktion aus der avr-libc vom WinAVR-2010011 (klick) (http://C:\Programme\WinAVR-20100110\doc\avr-libc\avr-libc-user-manual\group__avr__eeprom.html). Diese Funktion ist dort gelistet mit :


uint8_t (https://www.roboternetz.de/community/group__avr__stdint.html#gba7bc1797add20fe3efdf37ce d1182c5)

eeprom_read_byte (https://www.roboternetz.de/community/group__avr__eeprom.html#g2d4ee8b92a592c764785fb5e4 af5662b) (const uint8_t (https://www.roboternetz.de/community/group__avr__stdint.html#gba7bc1797add20fe3efdf37ce d1182c5) *__p) __ATTR_PURE__




ABER wenn ich in meiner Routine "void uart0_eep_string( char data [6] )" das "c" als uint8_t c definiere - gibts trotzdem die Warnung

../KoCo_inf15.c:90: warning: pointer targets in
...... passing argument 1 of '__eerd_byte_m1284p' differ in signedness

Leider :-/

- - - Aktualisiert - - -

Trotz Allem: es läuft genau wie ich will - und ich kann endlich mit dem Befehl "D" (= Display all Data) eine Liste der Daten für meine zehn Servos bekommen >> mit Angabe/Kürzel<< des jeweiligen Servos:


Servodaten
Servo | Aktuell Min ~Max Offset
1 Ko+/- 3600 1600 5600 0
2 Kor/l 3600 1600 5600 0
3 KoNir 3850 3000 4700 0

4 KoNil 3200 2300 4100 0
5 ADeur 3600 1600 5600 0
6 ADeul 3600 1600 5600 0

7 3600 1600 5600 0
8 ADe o 3600 1600 5600 0
9 Au rl 3600 2800 4400 -12
10 Au +- 3600 2800 4400 19

Valen
20.03.2013, 11:45
Dein "avr-libc vom WinAVR-2010011 (klick)" link fehlt weil das auf deine Festplatte steht. Aber nicht in Program Files in meiner.

oberallgeier
20.03.2013, 11:50
Dein "avr-libc vom WinAVR-2010011 (klick)" link fehlt weil das auf deine Festplatte ...Aua - ein typischer Anfängerfehler.

http://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html

markusj
20.03.2013, 11:58
Das Problem ist nicht "c" sondern dein Array, das aus chars besteht. Und chars sind signed, die EEPROM-Funktion will aber einen Pointer auf unsigned chars (uint8_t) haben.

Die statische 2D-Array-Variante verschwendet übrigens Platz, da wirklich X*Y Bytes alloziert werden selbst wenn X-1 Strings nur halb so groß sind.

mfG
Markus

oberallgeier
20.03.2013, 13:27
Das Problem ... Array ... aus chars ... EEPROM-Funktion will ... Pointer auf (uint8_t) ...Danke für die Hilfe und dass Du da drüber schaust. Über die Deklaration uint8_t war ich schon gestolpert, als ich mir vorhin die Erklärungen zur Funktion im user manual angesehen hatte. Egal - auch ein uint8_t c; hatte nicht geholfen, es kommt dieselbe Warnung.


... Die statische 2D-Array-Variante verschwendet übrigens Platz ...Danke. Im Moment hab ich ja noch ewig Platz im m1284; Compilermeldung nach dem Build: "EEPROM: 390 bytes (9.5% Full)". Genau diese Platzfrage ist ja auch einer der Gründe - neben den 20 MHz und anderen Karten zum selben Projekt mit demselben Controller - warum ich den m1284 gewählt hatte. Immerhin meldet der Compiler auch noch "Program: 12190 bytes (9.3% Full) und Data: 2034 bytes (12.4% Full)". Also total viel Platz nach oben. Wie gesagt - war bei mir so ne Art Standardisierung die hier sogar gute Vorteile zeigt. Und die paar Euro mehr krieg ich grad noch hin ; - )

markusj
20.03.2013, 13:51
Danke für die Hilfe und dass Du da drüber schaust. Über die Deklaration uint8_t war ich schon gestolpert, als ich mir vorhin die Erklärungen zur Funktion im user manual angesehen hatte. Egal - auch ein uint8_t c; hatte nicht geholfen, es kommt dieselbe Warnung.

Nicht c ist das Problem. Dein Array! Statt "char SVdef [][6]" wünscht sich der GCC "uint8_t SVdef [][6]". Tatsächlich kannst du an dieser Stelle den Pointer von int8_t* auf uint8_t* umcasten, weil du im Endeffekt nur die rohen Daten aus dem EEPROM liest und der Typ hier in jeglicher Hinsicht keine Rolle spielt.

mfG
Markus

oberallgeier
20.03.2013, 14:41
Nicht c ist das Problem. Dein Array! ... wünscht sich der GCC "uint8_t SVdef [][6]". ...Au au - danke für die Geduld. Noch ein paar Reste angepasst (Funktionsprototyp etc) - und das Kompilat wird ohne Fehler und Warnungen abgeliefert.

Danke allen, die mir geholfen haben

oberallgeier
29.08.2013, 17:21
Bitte helft mir wieder einmal. Mein rumblättern und scrollen hat nix geholfen.


Nicht c ist das Problem. Dein Array! ...Gut, das mit dem EEPROM läuft problemlos - nur so EWIG langsam, bei meinen mittlerweile längeren Texten im EEPROM wirkt sich das fühlbar aus beim Umschalten von einem aufs andere Menue. Da ich aber im Flash Platz satt habe, wurde kurzerhand ein Array im Flash erzeugt - und schon kommt wieder mal ne Warnung.

Mal ein paar Codeschnippsel (hoffentlich die ausreichend und richtigen).

Im Definitionsheader:
typedef unsigned char u8;
typedef signed char s8;

im LCD-Header:
void lcd_string(char *data);

in meinem Projekt-Header:
...
s8 MNUnm2 [][ 17 ] // Namen der einzelnen Menues STETS 16 char
= { " menu 0 ",
" menu 1 ", " UART Verbindng ", // 1 2
"Messung mit ADC3", " rtLED 0/1 ", // 3 4
...

im Code:
LCR; lcd_string ( MNUnm2 [ 3 ] );

Und dann kommt die Warnung:

../SeTe_r5n20.c: In function 'menu_3':
../SeTe_r5n20.c:126: warning: pointer targets in passing argument 1 of 'lcd_string' differ in signedness

Die Folge ist: ich steh im Wald und verstehe nur Bahnhof.

Wsk8
29.08.2013, 17:57
Ich bin jetzt kein C-Experte, aber für den Compiler gilt: char != signed char != unsigned char

Entweder Lcd-header + source ändern:


void lcd_string(signed char const *data);


oder:


lcd_string((char*)MNUnm2[3]);


EDIT:
Und mit "signed" arbeiten ist mir eig neu. Entweder "char" oder "unsigned char". Entferne am besten signed. Ein char ist schon ein signed char. Das unsigned verwendet man dann um den Bereich von -128 bis 127 auf 0 bis 255 zu verschieben.


mfg

sternst
29.08.2013, 18:29
Ein char ist schon ein signed char.Nein, ist es nicht. Du hast doch selbst richtig geschrieben:

char != signed char != unsigned char
Klar muss sich letzten Endes ein "char" entweder genauso verhalten wie "signed char" oder "unsigned char". Aber was von beiden wird vom Compiler festgelegt.

Warum jemand Strings allerdings explizit als "signed" haben möchte, erschließt sich mir auch nicht.

- - - Aktualisiert - - -


Da ich aber im Flash Platz satt habe, wurde kurzerhand ein Array im Flash erzeugt - und schon kommt wieder mal ne Warnung.

Mal ein paar Codeschnippsel ...Von "im Flash" kann ich da aber nichts sehen.

Wsk8
29.08.2013, 18:40
Nein, ist es nicht. Du hast doch selbst richtig geschrieben:

char != signed char != unsigned char


Klar muss sich letzten Endes ein "char" entweder genauso verhalten wie "signed char" oder "unsigned char". Aber was von beiden wird vom Compiler festgelegt.
Für den Compiler sind es natürlich 3 unterschiedliche, aber beim gcc ist das verhalten von signed char = char. Darauf will ich hinaus.

mfg

sternst
29.08.2013, 19:22
Für den Compiler sind es natürlich 3 unterschiedliche, aber beim gcc ist das verhalten von signed char = char.Beim gcc kann das Verhalten über eine Option festgelegt werden, und viele Makefiles enthalten ein "-funsigned-char". Es ist schlicht ziemlich unklug im Code von "char = signed char" auszugehen. Wenn man unbedingt ein signed char möchte/braucht, dann schreibt man halt besser auch explizit "signed char".

oberallgeier
29.08.2013, 19:28
... Von "im Flash" kann ich da aber nichts sehen.Ich dachte (hoffte, glaubte) diese Codesequenz

...
in meinem Projekt-Header:
...
s8 MNUnm2 [][ 17 ] // Namen der einzelnen Menues STETS 16 char
= { " menu 0 ",
" menu 1 ", " UART Verbindng ", // 1 2
"Messung mit ADC3", " rtLED 0/1 ", // 3 4
...würde diese Texte im Flash ablegen ! ? ! ?

Na jedenfalls hatte Wsk8´s "lcd_string((char*)MNUnm2[3]);" meine Probleme beseitigt. Besser gesagt bekomme ich das Compilat ohne Warnungen. Ach so, hatte ich oben vergessen, die gewünschten Ausgaben erschienen trotz der Warnungen, der Compiler besitzt offensichtlich mehr Toleranz als ich C-Kenntnisse.


... Wenn man unbedingt ein signed char möchte/braucht, dann ... explizit "signed char".

Im Definitionsheader:
typedef unsigned char u8;
typedef signed char s8;Meine Fehler kommen einfach daher, dass ich solche Feinheiten nur ungefähr kenne und öfters mal die Konsequenzen nicht kenne/sehe/beachte. Deshalb erstmal und wieder mal vielen Dank allen für die Hilfe.

Wsk8
29.08.2013, 19:41
Beim gcc kann das Verhalten über eine Option festgelegt werden, und viele Makefiles enthalten ein "-funsigned-char". Es ist schlicht ziemlich unklug im Code von "char = signed char" auszugehen. Wenn man unbedingt ein signed char möchte/braucht, dann schreibt man halt besser auch explizit "signed char".
Naja, ich will hier jetzt nicht über irgendwelche Standards etc streiten.

Ich für meinen Teil benutzte "char" für strings und "unsigned char" wenn es wirklich Vorzeichenlos sein soll. Bei Zählern tendiere ich sowieso zu uint8_t. Damit komme ich immer ganz gut zurecht ;)


Ach so, hatte ich oben vergessen, die gewünschten Ausgaben erschienen trotz der Warnungen, der Compiler besitzt offensichtlich mehr Toleranz als ich C-Kenntnisse.
Im Vergleich zu einem C++-Compiler ist der C-Compiler auch wirklich recht umsichtig. Diese Warnung soll auch keinen Fehler verdeutlich, sondern dir eher aufzeigen, dass hier evtl. etwas schief gehen könnte, da du z.b. einen negativen Wert an eine Funktion übergeben könntest, die nur positive Werte annimmt. Hier gibts dann einen Fehler.


würde diese Texte im Flash ablegen ! ? ! ?
http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

unsigned char mydata[11][10] PROGMEM (http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga75acaba9e781937468d091 1423bc0c35) = .....
Da ist auch beschrieben, wie man einen String-table ablegt.

mfg

sternst
29.08.2013, 19:53
würde diese Texte im Flash ablegen ! ? ! ?Nein, sie liegen im RAM (gut, du wirst sie natürlich auch im Flash wiederfinden, denn von irgendwo her müssen sie ja ins RAM kommen).


Na jedenfalls hatte Wsk8´s "lcd_string((char*)MNUnm2[3]);" meine Probleme beseitigt.Ja, fragt sich nur weiterhin, warum MNUnm2 überhaupt ein s8-Array sein soll, und nicht einfach ein char-Array.

- - - Aktualisiert - - -


Ich für meinen Teil benutzte "char" für strings und "unsigned char" wenn es wirklich Vorzeichenlos sein soll. Bei Zählern tendiere ich sowieso zu uint8_t. Damit komme ich immer ganz gut zurechtDas ist ja auch unstrittig. Aber wenn man mal einen kleinen vorzeichenbehafteten Typ braucht (z.B. in einer Berechnung), dann sollte man halt tunlichst ein "signed char" oder ein "int8_t" nehmen, und niemals nur ein "char".

oberallgeier
30.08.2013, 19:48
Nein, sie liegen im RAM (... natürlich auch im Flash wiederfinden ...) ...Wenn ich zurückdenke, war mir das im Hinterkopf schon klar - denn ich hab ja diese Änderung gemacht, weil ich für das 2x16-LCD doppelt lange Strings im EEPROM hatte als vorher für das 2x8. Das Lesen dieser doppelt langen Strings aus dem EEPROM war so langsam, dass mir die Trägheit der Menuschaltung schon sehr unangenehm auffiel. Daher auch meine Programmänderung und diese Texte ins RAM(mit Umweg über Flash).


... lcd_string((char*)MNUnm2[3]); ...Eure Mühe hat ja für mich etwas Gutes: ich hatte im Kernighan/Ritchie die fast vier Seiten zum Thema "Umwandlung bzw. Umwandlungsoperation" nicht richtig oder eher gar nicht gelesen, das war mir zu undurchsichtig. Und da verstand ich auch selten Sinn, Vorgehen und Anwendung, wenn es ums Umwandeln (basic german : casten) ging. Jetzt habe ich diese Punkte nachgearbeitet und wieder etwas mehr Einblick in C.