Hallo zusammen, sorry für die Themenbeschreibung; weiß aber nicht wie ich es sonst nennen soll. Ich verwende den GCC und verwende stehts die C99 Standardlibs. So auch stdint.h die Definitionen zu den Datentypen enthält. Es wird unsigned char als uint8_t und signed char als int8_t definiert, soweit so gut. Was ist nun aber mit dem Datentyp char? Denn ein char ist ja bekanntlich kein signed und kein unsigned char. Bei Verwendung der beiden Datentypen sollte auch mit Typecasts gearbeitet werden. Das Problem entsteht dadurch, dass ich die string.h (auch C99 oder?) verwende. Hier möchte ich die Funktion strnlen() verwenden. Diese ist definiert durch "size_t (strnlen,(const char *, size_t));". So jetzt arbeite ich aber nur mit den Typen aus stdint.h, und verwende folglich kein "char" sondern nur uint8_t oder int8_t. Ich kann zwar Parameter mit diesen Typen an strnlen() übergeben, allerdings führt das zu einer Compiler-Warning. Funktionieren tut es natürlich, aber ich finde es nicht schön. Es muss hier doch eine einheitliche Lösung geben. Und wieso definiert die stdint.h eigentlich "char" nicht? Kurz am Rande ich verwende die libc der aktuellsten arm code-sourcery toolchain. Falls sich her jemand auskennt, darf ich um Hilfe und Anmerkungen bitten. Vielleicht verstehe ich es ja auch falsch. Danke und Grüße, Le_Q
Genau dasselbe habe ich auch gerade. Ich übergebe mehrere uint8_t an eine Funktion, die mir einen String ausgiebt. Der CCS warnt mich, dass *char nicht kompatibel mit uint8_t ist. Also habe ich in der Funktion aus "send_string (uint8_t *string)" ein "send_string (unsigned char *string)" gemacht, aber auch hier wird gemeckert...mit einem normalen char ist es OK. Funktionieren tun alle drei Varianten. IAR Embedded beschwert sich auch nicht, der Code Composer schon...
Wenn man char will, nimmt man char (insbesondere char[] bzw. char* für Zeichenketten). Wenn man einen 8-Bit Wert ohne Vorzeichen haben will, nimmt man uint8_t. Wenn man einen 8-Bit Wert mit Vorzeichen haben will, nimmt man int8_t.
Die in stdint.h definierten Typen sind nicht dazu gedacht, die Basic Integer Types zu ersetzen, sondern sie zu ergänzen. Wenn Du einen String oder ein Character haben willst, nimmst Du weiterhin char und nicht [u]int8_t.
Typischerweise hast du jeweils einen genau definierten Übergang zwischen den Typen. Beispielsweise, wenn du Zeichen von einer UART einliest, die dann darstellbarer Text (also char im Sinne von <string.h>) werden sollen, dann hat das UART-Datenregister den Typ uint8_t, danach werden die von dort gelesenen Daten als char weiter interpretiert. Wenn man das sauber machen will, schreibt man einen Typecast:
1 | *uartbufptr++ = (char)UDR; |
Bei der Ausgabe erfolgt das dann in umgekehrter Richtung. "char" steht in keiner Headerdatei, da es ein impliziter Datentyp der Programmiersprache C ist.
Okay, dass klingt einleuchtend. Danke für die Eingebung! :) Aber: Dass ein "char" 8 Bit breit ist, ist mir klar. Aber ist garantiert das dies auf jedem System so ist? Alle definitionen die ich finde, sagen dass ein char min. 8 Bit sein muss und ein Bekannter meinte das er es schonmal mit 16Bit unicode chars zu tun hatte. Wobei mir persönlich diese nur aus Java und nicht aus C bekannt sind. Bei dem int8 kann ich auf jeden Fall von 8 Bit ausgehen... Ich habe Speicherbereiche in denen "irgendwelche Werte" oder aber auch ein String liegen kann. Wird durch eine Struct verwaltet, die enthält auch Infos drüber ob es sich um einen String oder einen anderen Wert handelt (Typ: string/zahlenwert, Lenge: in Byte, Adresse: null-pointer). Wenn ich über diesen Speicherbereich ein Char-Array lege, muss sicher sein das ein Feld wirklich 8 Bit breit ist, unabhängig vom Target für das der Code compiliert wurde.
Le_Q schrieb: > Dass ein "char" 8 Bit breit ist, ist mir klar. Aber ist garantiert das > dies auf jedem System so ist? Nein Das macht aber nichts. Ein char ist so gross, dass ein Zeichen, so wie es bei Textverarbeitung benötigt wird, hineinpasst. > Ich habe Speicherbereiche in denen "irgendwelche Werte" oder aber auch > ein String liegen kann. > Wird durch eine Struct verwaltet Die Idee ist nicht schlecht. Nur möchtest du dafür eine union nehmen. Genau das ist nämlich ihr Einsatzfall. > Wenn ich über diesen Speicherbereich ein Char-Array lege, muss sicher > sein das ein Feld wirklich 8 Bit breit ist, unabhängig vom Target für > das der Code compiliert wurde. Und mit einer Union ist dann auch dieses "Problem" für dich nicht mehr existent.
Ja ich weiß was du meinst, habe auch schon viel mit Unions gemacht, mein Problem ist aber ein anderes. Denn der void-Pointer kann auf einen Bereich zeigen an dem 1GB hinterlegt ist oder auch nur 1 Byte. typedef struct{ uint32_t fieldId; uint32_t fieldSize; FIELDTYPE fieldtype; FUNCTION_PTR fieldFunction; void * fieldAddress; }myStruct; Wobei FIELDTYPE ein Enum ist für den Typ. Und FUNCTION_PTR ein Funktionspointer auf irgendeine Funktion. So kann ich mir z.B. eine Liste erzeugen mit unterschiedlichen Einträgen. Z.B. fieldID = 1; fieldSize = 10; fieldtype = STR; fieldFunction = FPTR_writeField; fieldAddress = (void *) 0x20000000; Ich kann z.B. in dem Funktionspointer eine Funktion hinterlegen die mir jetzt einen String der Länge 10 an die 0x20000000 schreiben kann, oder zum lesen, oder kein String sonder 1GB Daten oder oder oder... Ist ne coole Sache, Funktioniert sogar. ;) Aber ich Adresse die Daten im späteren Code einfach als uint8_t array. Weil ich so einen recht einfachen Byte-weise Zugriff auf die Daten realisieren kann. Also wenn ich euch alle richtig verstehe ist es das Eleganteste vor der Übergabe des uint8_t arrays bzw. pointers an strnlen(), diesen in char zu casten. Das geht gut solange char als 8 bit definiert ist, sobald ein anderes Target mit z.B. 16Bit Chars arbeitet (wenn es das überhaupt gibt? in der C Welt?) funktioniert der Code nicht mehr und ist somit nicht mehr portabel. :-(
Sry ich muss mir mal einen Account machen aufm Forum, sonst kann ich nicht editieren: Nachtrag: Da die Struct selbst statisch ist, bringt eine Union hier nichts. Würden die Strings / Daten direkt in der Struct stehen würde eine Union auf jeden Fall Sinn machen!
Le_Q schrieb: > Weil ich so einen recht > einfachen Byte-weise Zugriff auf die Daten realisieren kann. Nun ja, wenn du VOLLE Kompabilität mit allen real existierenden Systeme willst, geht das schon an dieser Stelle in die Hose. Nicht jede Architektur lässt Byte-weise Zugriffe auf Daten zu. Wenn du wirklich portabel programmieren willst, musst du über sizeof() die tatsächliche Größe von char ermitteln, und in einen Code entsprechen darauf reagieren. Oder du schränkst die Portabilität auf Systeme ein, bei denen char ein Byte groß ist. Viel verlierst du damit nicht. Oliver
Oliver schrieb: > Wenn du wirklich portabel programmieren willst, musst du über sizeof() > die tatsächliche Größe von char ermitteln, und in einen Code entsprechen > darauf reagieren. Das geht auf jeden Fall schief. Den sizeof(char) ist definitionsgemäs eine glatte 1 > Oder du schränkst die Portabilität auf Systeme ein, bei denen char ein > Byte groß ist. Viel verlierst du damit nicht. Exakt.
Le_Q schrieb: > Ist ne coole Sache, Funktioniert sogar. ;) Aber ich Adresse die Daten im > späteren Code einfach als uint8_t array. Weil ich so einen recht > einfachen Byte-weise Zugriff auf die Daten realisieren kann. > Also wenn ich euch alle richtig verstehe ist es das Eleganteste vor der > Übergabe des uint8_t arrays bzw. pointers an strnlen(), diesen in char > zu casten. Das eleganteste ist es, den void Pointer so schnell wie möglich wieder in einen Pointer auf den richtigen Datentyp umzucasten und dann nur noch mit dem umgecastetn Pointer zu arbeiten. Noch eleganter ist nur noch: den void Pointer vermeiden.
Le_Q schrieb: > Was ist nun aber mit dem Datentyp char? Nichts. > Das Problem entsteht dadurch, dass ich die string.h (auch C99 oder?) > verwende. Hier möchte ich die Funktion strnlen() verwenden. Diese ist > definiert durch "size_t (strnlen,(const char *, size_t));". So jetzt > arbeite ich aber nur mit den Typen aus stdint.h, und verwende folglich > kein "char" sondern nur uint8_t oder int8_t. Warum? Woraus bestehen deine Strings? Aus Zeichen ("character") oder aus Integern, die exakt 8 Bit breit sein müssen. (u)int8_t ist letzteres, char ist ersteres. > Es muss hier doch eine einheitliche Lösung geben. > Und wieso definiert die stdint.h eigentlich "char" nicht? Wozu? char ist doch schon definiert. Außer für direkten Zugriff auf I/O-Register oder andere Stellen, wo die exakte Bit-Größe absolut notwendig ist, sollte man übrigens nie (u)int*_t verwenden. Oliver schrieb: > Nun ja, wenn du VOLLE Kompabilität mit allen real existierenden Systeme > willst, geht das schon an dieser Stelle in die Hose. Nicht jede > Architektur lässt Byte-weise Zugriffe auf Daten zu. Jeder konforme C-Compiler unterstützt ganz automatisch byteweise Zugriffe. > Wenn du wirklich portabel programmieren willst, musst du über sizeof() > die tatsächliche Größe von char ermitteln, sizeof(char) ist immer 1. > Oder du schränkst die Portabilität auf Systeme ein, bei denen char ein > Byte groß ist. Viel verlierst du damit nicht. Das stimmt, denn so ist in C das Byte definiert - als genau die Datenmenge, die man in einem char speichern kann. Wenn er allerdings uint8_t statt char verwendet, dann schränkt er sich auf Systeme ein, wo char exakt 8 Bit breit ist, weil nur auf denen ein uint8_t existiert.
Nun gut, wollte jetzt keine Diskussion los treten. Aber ich sehe schon "absolute kompatibilität" lässt sich nicht immer mit "höchster Eleganz" vereinen. :| Und ich glaube was Oliver sagen wollte ist, dass nicht jeder Speicherbereich Byteweise Les- und Schreibbar sein muss. Das gilt es sowieso zu berücksichtigen. @Rolf Magnus: Ja du hast schon recht uint8_t * sollte man in erster Linie für I/O Register etc. verwenden. Da meine Structur aber auch I/O Register adressiert muss ich es so machen. Die Idee ist es eine Lookup Table zu haben die dann R/W Zugriff auf Speicherbereiche (I/O Register, Selbstdefinierte Bereiche...) ermöglicht. Wen dies an CANOpen SDO-Server erinnert hat recht, das Prinzip stammt im Grunde daher. -------------------------------- Danke nochmal für alle Anmerkungen, ich halte mal fest: Void Pointer am besten vermeiden, und wenn dann diesen vor Weiterverarbeitung schnell wieder zurück in den "wirklichen" Datentyp wandeln. Wenn es sich um einen String handelt CHAR verwenden! Char ist nicht von der stdint.h abgedeckt, denn diese ist dazu gedacht die bestehenden basic integer Datentypen zu ergänzen. Die Verwendung von char macht C Code ggf. unportabel, da die Größe von char nicht fest definiert ist. Im ISO/IEC 9899:1999 Seite 33 (C99) steht hierzu: "The three types char, signed char, and unsigned char are collectively called the character types. The implementation shall define char to have the same range, representation, and behavior as either signed char or unsigned char. 35)" Und zur definition der Größe eines Chars findet man weiter: "An object declared as type char is large enough to store any member of the basic execution character set. If a member of the basic execution character set is stored in a char object, its value is guaranteed to be positive. If any other character is stored in a char object, the resulting value is implementation-defined but shall be within the range of values that can be represented in that type." Weiter ist in den Fußnoten zu finden dass die Größe aller Datentypen, sich aus limits.h ableitet. --------------------------- Ich muss dazu sagen, dass diese Probleme sich durch Low-Level / Treiber Programmierung ergeben, auf Applikationsebene glaub ich nicht das es diese Konflikte gibt. Viel Text... ich bin jetzt auf jeden Fall schlauer! Wie immer großes Lob an die Community hier! :)
Ich hab mal in die limits.h reingeschaut, dort ist in der Tat Platformabhänig die größe eines "char" definiert! :) Ich frag mich jetzt nur... wenn ich im code "char" schreibe, ohne irgend einen Header einzubinden, wie stell ich dann sicher das GCC den in der limits.h beschränkten Datentypen und nicht einen "Build-in" Datentypen verwendet? Oder tut er das sobald ich die libc dazu linke?
limits.h hat nichts mit der Bibliothek zu tun. Dieser Header kommt vom Compiler selbst und dokumentiert letztlich nur das, was der Compiler ohnehin bereits so macht und kennt.
Ah okay verstehe. Danke schön. Dann sind wir nur wieder beim alten Problem. Die Größe von char ist somit Compiler abhängig. Üblicherweise entspricht sizeof(char) 1. Das scheint aber nicht in C99 so 100% definiert zu sein. Denn es wird darauf verwiesen das die Größe in limits.h definiert ist.
sizeof(char) ist immer 1 (Byte). Nur wie breit ein byte/char ist, ist Plattform abhängig, d.h. 1 Byte/Char sind nicht immer 8 Bit breit.
Daniel schrieb: > Nur wie breit ein byte/char ist, ist > Plattform abhängig, d.h. 1 Byte/Char sind nicht immer 8 Bit breit. Der genaue Wert ist im Makro CHAR_BIT (ebenfalls aus limits.h) zu finden. Der Wert muss mindestens 8 sein.
Le_Q schrieb: > Die Verwendung von char macht C Code ggf. unportabel, da die Größe von > char nicht fest definiert ist. Eigentlich nicht mehr als die von uint8_t. Es stimmt zwar, daß char auch breiter als 8 Bit sein könnte, aber er ist auch gleichzeitig der kleinstmögliche Datentyp. Wenn also char mehr als 8 Bit breit ist, kann es auf der Plattform ganz einfach keinen uint8_t geben. Portabler wirds dadurch also nicht (und kann's auch nicht werden). Le_Q schrieb: > Dann sind wir nur wieder beim alten Problem. Die Größe von char ist > somit Compiler abhängig. Üblicherweise entspricht sizeof(char) 1. > Das scheint aber nicht in C99 so 100% definiert zu sein. Das steht eigentlich ziemlich explizit unter 6.5.3.4 "The sizeof operator":
1 | 3 When applied to an operand that has type char, unsigned char, or signed char, |
2 | (or a qualified version thereof) the result is 1. |
Le_Q schrieb: > Und ich glaube was Oliver sagen wollte ist, dass nicht jeder > Speicherbereich Byteweise Les- und Schreibbar sein muss. Das gilt es > sowieso zu berücksichtigen. Was ich vor allem sagen wollte, ist, daß du einmal überlegen solltest, welche Folgen es hätte, wenn du deine Software auf Systeme einschränkst, bei denen ein Char 8 Bit breit ist. Vermutlich fast gar nichts. Le_Q schrieb: > Ich muss dazu sagen, dass diese Probleme sich durch Low-Level / Treiber > Programmierung ergeben, auf Applikationsebene glaub ich nicht das es > diese Konflikte gibt. In diesem Fall wohl noch weniger als gar nichts. Oliver
Ich hab mal geschaut, momentan läuft die Applikation Oliver schrieb: > Was ich vor allem sagen wollte, ist, daß du einmal überlegen solltest, > welche Folgen es hätte, wenn du deine Software auf Systeme einschränkst, > bei denen ein Char 8 Bit breit ist. > Vermutlich fast gar nichts. Die Einschränkung ist das die Software dann eben nur auf Machinen/Compilern läuft, die 8 Bit Chars verwenden. Wie schon angemerkt wurde, ist char der kleinste Datentyp und auf einer Maschine auf der ein char 16 Bit ist, ist dies dann der kleinste Datentyp. Folglich entsteht hierdurch eine Inkompatibilität. Es gibt halt nunmal DSPs auf denen ein CHAR mit 16 oder sogar 32 Bit definiert ist. Auf diesen würde der Treiber dann nicht funktioneren den ich schreibe. Aber dies lässt sich wohl nicht vermeiden. ;-)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.