char array initialisieren

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
starcow
Establishment
Beiträge: 555
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

char array initialisieren

Beitrag von starcow »

Abend zusammen

Ich steh hier grad vor der Situation, dass ich gerne ein static char array initialisieren würde - mit Werten > 127.
Mein Textrenderer ist jetzt in den Grundzügen soweit fertig - und ich würde ihn auch dazu nutzen wollen, einige Rahmenelemente aus Codepage 850 (oder 437) darzustellen.

Für meine hübschen Menüs habe ich also ein static char array angelegt und initialisiere es mit den entsprechenden Werten für die Codepoints der Rahmenelemente.
Die Rahmen lassen sich so mit einem einfachen Texteditor zeichnen, den ich nur auf die entsprechende Codepage umschalten muss. So sehe ich gleich, was ich "zeichne".

Das Problem:
Ich kann die Codepoints später über einen Hexeditor nur als (positive) hexadezimale Zahlen exportieren - was ja soweit eigentlich auch ganz sinnvoll wäre. Nur ist halt das array vom typ char (und nicht unsigned char).
Bei der Initialisierung moniert jetzt der gcc die conversion an, von einer Zahl grösser als 127 zu char (die Codepoints der Rahmenelemente sind natürlich alle > 127).

Code: Alles auswählen

overflow in conversion from 'int' to 'char' changes value from '128' to '-128'
Ich würde allerdings gerne für diverse Funktionen kompatibel zu char bleiben (Beispiel printf).
Notfalls kann ich natürlich einfach händisch auf negative Zahlen umrechnen. Wenn sich das allerdings irgendwie vermeiden lässt, wärs natürlich super!
Vielleicht habt ihr ja eine Idee dazu?

Interessantes Details (wie ich finde)
Bis vor kurzem dachte ich noch, dass folgende Anweisung aufgrund eines overflows UB wäre.

Code: Alles auswählen

char x = 127;
char c = x + x; // OK. Implizit conversion from 'int' to 'char'
Das scheint aber tatsächlich gar nicht der Fall zu sein.
So wie ich das jetzt im C11 Standard gelesen und verstanden habe (genau genug beschreibt es leider keines meiner C Bücher) passiert folgendes:
bei der Addition von x und x, werden die beiden x zunächst zu int promoted, da immer mit (mindestens) int gerechnet wird.
der Ausdruck x + x ist also vom typ int.
Weiter wird jetzt eine implizite conversion vorgenommen vom typ int zu char. Solche conversions unter ganzzahligen Typen sind immer wohl definiert - und es macht dabei auch keinen Unterschied, ob ich diese explizit vornehme (cast) oder nicht.

Selbiges muss dann auch für folgende Anweisung gelten:

Code: Alles auswählen

char x = 127;
x = x + 1; // OK. Implizit conversion from 'int' to 'char'
Die Zahlenkonstante 1 ist vom Typ int, wodurch x (ohnehin) zu int promoted wird.

Und konsequenterweise müsste dann auch folgendes "legal" sein (ich versuche es noch über den Standard zu verifizieren).

Code: Alles auswählen

char x = 127;
x += 1; // OK. Implizit conversion from 'int' to 'char'

Code: Alles auswählen

char x = 127;
x++; // OK. Implizit conversion from 'int' to 'char'
Folgendes aber dann tatsächlich UB, da einen Überlauf beim rechnen auftritt (und nicht bei der Zuweisung).

Code: Alles auswählen

char c = 1 + 2147483647; // UB. int overflow.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8308
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: char array initialisieren

Beitrag von Krishty »

Code: Alles auswählen

char c = `\xFF`;
https://en.cppreference.com/w/cpp/language/character_literal hat geschrieben:If the character literal contains only one numeric escape sequence, and the value specified by the escape sequence is representable by the unsigned version of its type, the character literal has the same value as the specified value (possibly after conversion to the character type).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
starcow
Establishment
Beiträge: 555
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: char array initialisieren

Beitrag von starcow »

Sagenhaft, das ist perfekt!
Herzlichen Danke Krishty! :-)
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
xq
Establishment
Beiträge: 1589
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: char array initialisieren

Beitrag von xq »

Imho wäre hier ein uint8_t besser angebracht als char oder unsigned char, da dein Text Renderer ein von dir definiertes Encoding benutzt statt das der Datei oder das vom System.

Zudem hast du damit eine Garantie auf "unsigned", welche bei char nicht gegeben ist und dir sonst noch ne Menge Gefummel und Probleme einheimsen wird
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
starcow
Establishment
Beiträge: 555
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: char array initialisieren

Beitrag von starcow »

Interessanter Hinweis, danke xq!

Wenn ich genauer darüber nachdenke, fällt mir eigentlich auch kein Szenario ein, wo ein unsigned char String gegenüber einem char String nachteilig sein könnte.
Die conversion zwischen char * und unsigned char * ist ja jeder Zeit explizit erlaubt - also wenn man ein Array per Pointer übergeben will.
Auch die Initialisierung eines Arrays mit einem String Literal funktioniert ja genau so wie mit char.
Allerdings bin ich mir nicht sicher, wie es mit den string Funktionen aus der stdlib ausschaut (printf, strncpy, etc.).
Darf man da einfach mit unsigned ankommen?

Weil ja char nicht signed char entspricht - sprich, char, signed char und unsigned char formal verschieden sind, verwende ich den Datentyp char eigentlich immer nur dann, wenn es auch wirklich etwas mit "Characters" oder "Text" zutun hat.
Ansonsten verwende ich konsequent u8 (unsigned char) oder i8 (signed char).
Eigentlich wäre das ja jetzt so ein Fall - aber vielleicht würde es auch Sinn machen, diese Regel aufzugeben?

btw:
uint8_t ist auf allen mir bekannten Systemen per typedef als unsigned char definiert.
Was sind deine Überlegungen dazu, uint8_t anstatt (direkt) unsigned char zu verwenden?
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
TomasRiker
Beiträge: 88
Registriert: 18.07.2011, 11:45
Echter Name: David Scherfgen
Wohnort: Hildesheim

Re: char array initialisieren

Beitrag von TomasRiker »

@starcow: Diese Frage auf Stack Overflow dürfte hilfreich sein: https://stackoverflow.com/questions/150 ... vice-versa
Benutzeravatar
xq
Establishment
Beiträge: 1589
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: char array initialisieren

Beitrag von xq »

starcow hat geschrieben: 03.11.2024, 20:04 Weil ja char nicht signed char entspricht - sprich, char, signed char und unsigned char formal verschieden sind, verwende ich den Datentyp char eigentlich immer nur dann, wenn es auch wirklich etwas mit "Characters" oder "Text" zutun hat.
Ansonsten verwende ich konsequent u8 (unsigned char) oder i8 (signed char).
Eigentlich wäre das ja jetzt so ein Fall - aber vielleicht würde es auch Sinn machen, diese Regel aufzugeben?
Es wäre sinnvoll, die Regel anzupassen:
Weil ja char nicht signed char entspricht - sprich, char, signed char und unsigned char formal verschieden sind, verwende ich den Datentyp char eigentlich immer nur dann, wenn es auch wirklich etwas mit "Characters" oder "Text" rohen uninterpretierten Daten zutun hat.
Für Text gibt es in C++ mittlerweile spezielle Datentypen (char8_t, char16_t, char32_t) welche was über das Encoding aussagen, aber grundlegend gilt für mich: char sind "uninterpretierte Strings", wobei "String" hier eine Sequenz aus Bytes betrachtet, nicht eine Sequenz aus Text-Symbolen. Das war früher™ mal gleich, aber heutzutage sind Text-Encodings nicht mehr einfach nur Bytes.
starcow hat geschrieben: 03.11.2024, 20:04 btw:
uint8_t ist auf allen mir bekannten Systemen per typedef als unsigned char definiert.
Was sind deine Überlegungen dazu, uint8_t anstatt (direkt) unsigned char zu verwenden?
JA, MACHEN! uint8_t hat definierte Garantien über Vorzeichen und Größe, während unsigned char halt auch uint32_t defineren könnte. Während letzteres glaube ich nicht passiert. Aber grundlegend gilt: Wenn du konkrete Größen willst, nimm die Typen aus stdint.h und stddef.h
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
starcow
Establishment
Beiträge: 555
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: char array initialisieren

Beitrag von starcow »

TomasRiker hat geschrieben: 04.11.2024, 08:33 @starcow: Diese Frage auf Stack Overflow dürfte hilfreich sein: https://stackoverflow.com/questions/150 ... vice-versa
Ich vergesse leider immer zu erwähnen, dass es mir (in erster Linie) um C geht.
Der Thread auf Stack Overflow thematisiert die Sache in C++. Trotzdem danke Tomas!
xq hat geschrieben: 04.11.2024, 08:41 JA, MACHEN! uint8_t hat definierte Garantien über Vorzeichen und Größe, während unsigned char halt auch uint32_t defineren könnte. Während letzteres glaube ich nicht passiert. Aber grundlegend gilt: Wenn du konkrete Größen willst, nimm die Typen aus stdint.h und stddef.h
Ich glaube ich verstehe, was du meinst. sizeof char ist laut Standard immer 1 (Byte), jedoch schreibt der Standard dabei ja nicht vor, aus wie vielen Bits ein Byte zu bestehen hat!

Wie würdest du denn vorgehen, wenn du deinen uint8 buffer an eine Funktion aus der stdlib übergeben willst? Einfach einen (expliziten) cast nach char?
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
TomasRiker
Beiträge: 88
Registriert: 18.07.2011, 11:45
Echter Name: David Scherfgen
Wohnort: Hildesheim

Re: char array initialisieren

Beitrag von TomasRiker »

Es gibt CHAR_BIT, das dir sagt, aus wie vielen Bits ein char besteht. Heutzutage dürfte es kaum (für deine Zwecke) relevante Systeme geben, auf denen das nicht als 8 definiert ist. Du könntest eine static assertion in deinen Code einbauen, die sicherstellt, dass es 8 ist, und dich dann nicht mehr darum kümmern.
Benutzeravatar
xq
Establishment
Beiträge: 1589
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: char array initialisieren

Beitrag von xq »

starcow hat geschrieben: 05.11.2024, 14:08 Wie würdest du denn vorgehen, wenn du deinen uint8 buffer an eine Funktion aus der stdlib übergeben willst? Einfach einen (expliziten) cast nach char?
Jop, das tut dann einfach :) Ist ja auch legal, und 0 bleibt ja in allen Encodings 0. Aber dann bist du auf der typsicheren Seite.

Ich hab mir mittlerweile nen ganzen Strauß eigener Alternativen gebaut zu den Funktionen der stdlib, einfach weil die meisten der Funktionen nur so drei, vier Zeilen Code sind, aber besser implementiert werden können
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
TomasRiker
Beiträge: 88
Registriert: 18.07.2011, 11:45
Echter Name: David Scherfgen
Wohnort: Hildesheim

Re: char array initialisieren

Beitrag von TomasRiker »

Mal angenommen, dass man auf einer Plattform arbeitet, wo ein char 16 Bits hat. Was wäre denn dann sizeof(uint8_t)? Da sizeof(char) immer 1 ist, müsste dann sizeof(uint8_t/ eigentlich 1/2, also 0.5 sein, aber das geht nicht. Heißt das, dass auf solchen Plattformen uint8_t gar nicht existiert? Dann wäre das Problem ja eigentlich gar keins. Höchstens wenn ein char 2 oder 4 Bits hätte, denn dann wäre sizeof(uint8_t) = 4 bzw. 2. Aber das würde ja bedeuten, dass man nur 4 oder 16 Werte hätte, was nicht einmal für das Alphabet ausreicht. (CHAR_BITS muss ≥ 8 sein!)
Zuletzt geändert von TomasRiker am 06.11.2024, 04:36, insgesamt 2-mal geändert.
Benutzeravatar
xq
Establishment
Beiträge: 1589
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: char array initialisieren

Beitrag von xq »

TomasRiker hat geschrieben: 05.11.2024, 20:57 Mal angenommen, dass man auf einer Plattform arbeitet, wo ein char 16 Bits hat. Was wäre denn dann sizeof(uint8_t)? Da sizeof(char) immer 1 ist, müsste dann sizeof(uint8_t/ eigentlich 1/2, also 0.5 sein, aber das geht nicht. Heißt das, dass auf solchen Plattformen uint8_t gar nicht existiert? Dann wäre das Problem ja eigentlich gar keins. Höchstens wenn ein char 2 oder 4 Bits hätte, denn dann wäre sizeof(uint8_t) = 4 bzw. 2. Aber das würde ja bedeuten, dass man nur 4 oder 16 Werte hätte, was nicht einmal für das Alphabet ausreicht.
Grundlegend wäre dann sizeof(uint8_t) dann auch 1, aber der legale Wertebereich wäre trotzdem 0..255 und nicht 0..65535. Oder der Datentyp wäre nicht verfügbar
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
Krishty
Establishment
Beiträge: 8308
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: char array initialisieren

Beitrag von Krishty »

Höh?! uint8_t ist auf solchen Plattformen schlicht nicht verfügbar und der Code kompiliert nicht.
https://en.cppreference.com/w/c/types/integer hat geschrieben:(provided only if the implementation directly supports the type)
Lesen, nicht mutmaßen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
TomasRiker
Beiträge: 88
Registriert: 18.07.2011, 11:45
Echter Name: David Scherfgen
Wohnort: Hildesheim

Re: char array initialisieren

Beitrag von TomasRiker »

Das heißt: Wenn man uint8_t im Code benutzt (und den Datentyp nicht eigenhändig definiert), dann kompiliert der Code eh nur auf Plattformen, bei denen ein char genau 8 Bits hat?
Benutzeravatar
Krishty
Establishment
Beiträge: 8308
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: char array initialisieren

Beitrag von Krishty »

TomasRiker hat geschrieben: Gestern, 04:38 Das heißt: Wenn man uint8_t im Code benutzt (und den Datentyp nicht eigenhändig definiert), dann kompiliert der Code eh nur auf Plattformen, bei denen ein char genau 8 Bits hat?
… so verstehe ich das.

Und int8_t übrigens auch nur auf Plattformen, auf denen signed char Zweierkomplement benutzt, denn Zweiterkomplement ist eine weitere Garantie der int_*-typedefs.

Auf anderen Plattformen könntest du andere typedefs bekommen, wie etwa uint9_t, falls das die native Bitbreite ist.
The implementation may define typedef names intN_t, int_fastN_t, int_leastN_t, uintN_t, uint_fastN_t, and uint_leastN_t when N is not 8, 16, 32 or 64. Typedef names of the form intN_t may only be defined if the implementation supports an integer type of that width with no padding. Thus, uint24_t denotes an unsigned integer type with a width of exactly 24 bits.
Falls du auf hypothetischen 19-Bit Plattformen mit 8-Bit-Daten rechnen möchtest, kannst du uint_least8_t benutzen, das dann auf einen unsigned-Typ mit Größe 19 Bits und sizeof == 1 verweisen würde. Natürlich ist das Gespeicherte nicht binär kompatibel aber für die Verarbeitung reicht es meist.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
antisteo
Establishment
Beiträge: 923
Registriert: 15.10.2010, 09:26
Wohnort: Dresdem

Re: char array initialisieren

Beitrag von antisteo »

Krishty hat geschrieben: Gestern, 08:55 Falls du auf hypothetischen 19-Bit Plattformen mit 8-Bit-Daten rechnen möchtest, kannst du uint_least8_t benutzen, das dann auf einen unsigned-Typ mit Größe 19 Bits und sizeof == 1 verweisen würde. Natürlich ist das Gespeicherte nicht binär kompatibel aber für die Verarbeitung reicht es meist.
Der Witz: Es gibt solche Plattformen wirklich. Gerade FPGAs haben manchmal aufgrund des LUT-Layouts einen Performance- oder Layoutgrößen-Vorteil, wenn man einen 19-Bit-Microcontroller statt 32 oder 16 Bit implementiert.
http://fedoraproject.org/ <-- freies Betriebssystem
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
Antworten