Hallo Gemeinde
Kann mir jemand erklären, warum mann bei Variablen Deklarationen in
BeispielCode oft zB "uint8_t" statt "char" lesen kann ?
Weil es in der " stdint.h" so definiert ist ! Ja!
Aber warum weicht diese vom C-Standard ab ?
Danke
Torsten
Also ich benutze uint8_t für Variablen, die Zahlen aufnehmen und char
für welche die Zeichen aufnehmen.
Dem µC bzw. Compiler ist das wohl total egal, aber ich finde es einfach
passender z.B. die Laufvariable i als uint8_t zu deklarieren als als
char...
Weil ein uint8_t unsigned ist.
Und vor allem, weil ein uint8_t auf den ersten Blick deutlich macht, wie
groß der Datentyp ist. Gerade bei plattformübergreifender Entwicklung
ist das wichtig. Nicht überall ist ein int 32 Bit groß.
Torsten B. schrieb:> Kann mir jemand erklären, warum mann bei Variablen Deklarationen in> BeispielCode oft zB "uint8_t" statt "char" lesen kann ?
uint8_t exakt 8 Bits ohne Vorzeichen.
uint_fast8_t mindestens 8 Bits ohne Vorzeichen, laufzeitoptimiert.
char Zeichen mit oder ohne Vorzeichen, 8 oder mehr Bits.
Bei plattformunabhängigem Code ist also uint_fast8_t sinnvoll, da
uint8_t bei 16/32-Bit Prozessoren ineffizient sein kann.
Torsten B. schrieb:> Kann mir jemand erklären, warum mann bei Variablen Deklarationen in> BeispielCode oft zB "uint8_t" statt "char" lesen kann ?
Hab ich noch nie gesehen, zeig mal ein Beispiel.
"uint8_t" hab ich nur statt "unsigned char" gesehen und da spart es
deutlich Schreibarbeit.
Peter II schrieb:> Ronny Spiegel schrieb:>> Weil ein uint8_t unsigned ist.>> was sagt das?
Es stimmt, wenn damit wortwörtlich "ohne Vorzeichen" gemeint ist, nicht
der Typ "unsigned int".
Vervollständigt:
uint8_t exakt 8 Bits ohne Vorzeichen.
uint_fast8_t mindestens 8 Bits ohne Vorzeichen, laufzeitoptimiert.
uint_least8_t mindestens 8 Bits ohne Vorzeichen, platzoptimiert.
char Zeichen mit oder ohne Vorzeichen, 8 oder mehr Bits.
uint_least8_t ergibt Sinn, wenn man eines Tages in Gefahr läufen könnte,
eine Maschine zu erwischen, die keinen 8-Bit Datentyp unterstützt und
folglich uint8_t nicht kennt.
Also das versteh ich nicht. Ein char ist definiert als ein Zeichen
(Byte) welches genau 8 Bit hat. Mir ist noch nie untergekommen dass ein
char signed sein könnte. Vorallem wäre mal interessant: Wer kommt denn
auf die Idee ein signed char zu verwenden? Und vorallem: Wo??
Florian Trück schrieb:> Also das versteh ich nicht. Ein char ist definiert als ein Zeichen> (Byte) welches genau 8 Bit hat. Mir ist noch nie untergekommen dass ein> char signed sein könnte.
Was nicht heißt, das es das nicht gibt.
Du brauchst nur den gcc mit der entsprechenden Option aufrufen und schon
sind deine char alle signed.
Karl Heinz Buchegger schrieb:> Was nicht heißt, das es das nicht gibt.> Du brauchst nur den gcc mit der entsprechenden Option aufrufen und schon> sind deine char alle signed.
Das mag ja sein, aber wozu braucht man ein signed char mit dem
Wertebereich (-127) - (127) ?
Florian Trück schrieb:> Ein char ist definiert als ein Zeichen> (Byte) welches genau 8 Bit hat.
Nein.
> Mir ist noch nie untergekommen dass ein char signed sein könnte.
Das war früher ziemlich verbreitet und ist irgendwo bestimmt noch so.
> Vorallem wäre mal interessant: Wer kommt denn> auf die Idee ein signed char zu verwenden?
Die erste C Implementierung, also die auf einer DEC PDP-11. Deren 8-Bit
Operationen arbeiteten nämlich implizit mit Vorzeichen. Bei
nachfolgenden Implementierungen wurde es lange Zeit ebenso gehandhabt,
auch wenn es ohne Vorzeichen sinnvoller gewesen wäre.
Florian Trück schrieb:> Karl Heinz Buchegger schrieb:>> Was nicht heißt, das es das nicht gibt.>> Du brauchst nur den gcc mit der entsprechenden Option aufrufen und schon>> sind deine char alle signed.>> Das mag ja sein, aber wozu braucht man ein signed char mit dem> Wertebereich (-127) - (127) ?
Es geht nicht darum, ob du oder ob man das braucht. Es geht darum, was
dein Compilerbauer für eine gute Idee auf einer bestimmten Plattform
gehalten hat, bzw. was die CPU an Assemblerbefehlen mitbringt.
Im Ernst. Wo liegt denn das Problem?
uint8_t für 'kleine Zahlen' ohne Vorzeichen.
(Im Volksmund auch Byte genannt)
int8_t für 'kleine Zahlen' mit Vorzeichen.
zb Schleifensteuerungen die auch mal negativ werden können
char für alles was mit Textverarbeitung zu tun hat.
Ist doch ganz einfach. Und zusätzlich hat man auch noch im Programm eine
schöne Trennung, wo man es mit Textverarbeitung und wo man es mit Bytes
im weitesten Sinne zu tun hat. Denn Textverarbeitung impliziert
zusätlich noch, dass Strings eine terminierende \0 enthalten.
Florian Trück schrieb:> Also das versteh ich nicht. Ein char ist definiert als ein Zeichen> (Byte) welches genau 8 Bit hat.
Mindestens 8 Bit. Es könne nauch mehr sein.
Florian Trück schrieb:> Mir ist noch nie untergekommen dass ein> char signed sein könnte. Vorallem wäre mal interessant: Wer kommt denn> auf die Idee ein signed char zu verwenden? Und vorallem: Wo??
Da ASCII nur ein 7-Bit Code ist, reicht das signed da vollkommen aus.
Es ist im C-Standard nicht festgelegt ob char signed oder unsigned ist.
Ich erinnere mich auch an keinen Compiler, der unsigned wäre. (oder es
ist sooo lange her)
Florian Trück schrieb:> Das mag ja sein, aber wozu braucht man ein signed char mit dem> Wertebereich (-127) - (127) ?
Man braucht es genauso dringend, wie einen Knopf an der Backe, aber es
wurde von den C-Erfindern nun einmal so festgelegt.
Es fällt einem auch regelmäßig auf die Nase, z.B. wenn man in einem
Stream Text und Zahlenwerte überträgt bzw. manche Programme können
deshalb keine Zeichen >127 darstellen.
Florian Trück schrieb:> Also das versteh ich nicht. Ein char ist definiert als ein Zeichen> (Byte) welches genau 8 Bit hat. Mir ist noch nie untergekommen dass ein> char signed sein könnte. Vorallem wäre mal interessant: Wer kommt denn> auf die Idee ein signed char zu verwenden? Und vorallem: Wo??
Der Visual Studio Compiler, den ich in der Arbeit verwende(n muss). Ein
char x = 254;
spuckt eine entsprechende Warnung aus, wohingegen
char x = -12;
anstandslos funktioniert.
Mit Typdefinitionen, die explizit angeben, ob ein Typ vorzeichenbehaftet
ist oder nicht, treten solche Sachen gar nicht erst auf.
Hey Ho,
Ich weiß von Atmel Studio 6 das dort ein char default mäßig als unsigned
char betrachtet wird, kann man aber in den Projekteinstellungen ändern.
Ich benutze in meinen Projekten für 1-Byte Variablen immer den char-Typ
und hatte damit noch nie Probleme. Und das bisschen was man da an
Schreibarbeit sparen könnte...naja, ist wohl ansichts sache. ;)
In .Net soll ein char wohl 16-Bit haben.
Ich glaube allerdings das wir uns hier auf C/C++ beziehen. Weiterhin
denke ich das "Platformunabhängigkeit" auf einem µC ziemlich Latte ist,
solange man sicht nicht auf der Himbeere befindet bzw. sonst irgendein
OS benutzt.
In diesem Sinne, Grüße
Ronny Spiegel schrieb:> Der Visual Studio Compiler, den ich in der Arbeit verwende(n muss). Ein>> char x = 254;>> spuckt eine entsprechende Warnung aus, wohingegen>> char x = -12;>> anstandslos funktioniert.>> Mit Typdefinitionen, die explizit angeben, ob ein Typ vorzeichenbehaftet> ist oder nicht, treten solche Sachen gar nicht erst auf.
Auf die Nase fällt man zb dann, wenn der char in einem Vergleich
vorkommt. Denn da spielt es dann eine Rolle
char c = 254;
if( c == 254 )
printf( "juhu" );
auf einem Compiler, bei dem ein char signed ist, wird der if nie
genommen. Und das Rätselraten ist groß, warum nicht.
Mit expliziten uint8_t, int8_t, char (je nach Anwendungszweck) gibt es
diese Probleme erst gar nicht.
Kaj schrieb:> In .Net soll ein char wohl 16-Bit haben.
Nicht nur da. Verwende z.B. mal die Piccolo-Serie von TI!
Da ist das kleinste was Du bekommst eine 16 Bit Variable ;-)
Peter Dannegger schrieb:> Man braucht es genauso dringend, wie einen Knopf an der Backe, aber es> wurde von den C-Erfindern nun einmal so festgelegt.
Es war nicht festgelegt. Von Anfang an war offen, ob char mit oder ohne
Vorzeichen implementiert ist. Es war einfach nur üblich, weil "schon
immer so gemacht".
Kaj schrieb:> denke ich das "Platformunabhängigkeit" auf einem µC ziemlich Latte ist,
Würde ich nicht sagen. Beispiel: Im Code für den Temperatursensor
DS18S20 ist ausschliesslich die Pinwackelei direkt hardwareabhängig, und
das sind sehr wenige Operationen. Folglich kann fast der gesamte Code
wiederverwendet werden.
Schon mal überlegt, warum
char c;
while( (c = getchar()) != EOF )
...
in C überhaupt wie angedacht funktioniert?
wenn c ein unsigned char wäre, funktioniert es nämlich nicht mehr :-)
Es funktioniert so auch nicht wie angedacht, egal ob signed oder nicht.
Der korrekte und überall zu findende Code ist
int c;
while( (c = getchar()) != EOF )
...
und genau dieser Aspekt gehört m.W. zum Grundwissen in C. Dass getchar
bei den üblichen Plattformen tatsächlich 257 mögliche Werte zurück gibt
und solche Zeichen deshalb nicht als char definiert werden dürfen.
Die Perversion dabei ist, dass auf Plattformen, bei denen "char" mit
Vorzeichen definiert und EOF dennoch wie üblich -1 ist, getchar() die
Werte 0..255 zurück gibt, nicht -128..127.
Wirklich verstehen muss man das nicht. Einfach akzeptieren. ;-)
Peter II schrieb:> Florian Trück schrieb:>> Mir ist noch nie untergekommen dass ein>> char signed sein könnte.
Vermutlich sitzt du genau in diesem Augenblick vor einem Rechner bei dem
das genau so ist.
>> im gcc ist es immer signed
Das hat nichts mit gcc zu tun, sondern mit der Platform. x86 und x86_64
haben z.B. üblicherweise signed char, während ARM, MIPS und PowerPC
unsigned char haben. Der Grund für solche Unterschiede sind z.T.
historisch und oftmals Performancefragen. Einige Architekturen müssten
z.B. Konvertierungen durchführen um chars auf eine andere Weise zu
unterstützen.
In C/C++ sind die Eigenschaften der intrinsischen Datentypen aus
Performancegründen von der zugrundeliegenden Architektur bestimmt und
man geht normalerweise davon aus, dass ein kompetenter Programmierer mit
signenness/unsignedness von char, den plattformspezifischen Größen von
short, int, long und all dem anderen Kram umgehen kann.
A. K. schrieb:> Es funktioniert so auch nicht wie angedacht, egal ob signed oder nicht.
Ja, ok.
'funktioniert' ist zu hoch gegriffen.
Aber bei 7-Bit ASCII und signed char kommt man damit durch.
(Nicht das ich das propagieren würde. Gott bewahre.)
Nochn schönes Beispiel für die "signed char" Fallgrube ist auch das
Ummappen der deutschen Umlaute für ein LCD. Wenn man nicht nach unsigned
castet, funktioniert es nicht.
Wenn man aber zur Umgehung all dieser Probleme die Stringpuffer als
unsigned anlegt, meckert einem der AVR-GCC ständig die Hucke voll.
Torsten B. schrieb:> Aber warum weicht diese vom C-Standard ab ?
Zum Rest wurde ja schon alles gesagt, aber man sollte vielleicht noch
erwähnen, dass stdint.h und damit uint8_t natürlich voll und ganz dem
C-Standard entsprechen.
Das ist ja gerade das schöne, dass man endlich mal ohne große
Verrenkungen Datentypen hat, bei denen man sich drauf verlassen kann,
dass sie einen klar bestimmten numerischen Umfang und Platzbedarf haben.
Bei "char" drängt sich einem das noch nicht so auf, wobei
signed-vs.-unsigned auch ein echtes Arschloch sein kann.
Noch viel mehr Spaß hat man mit "int" und "long". Ob das dann 32 oder 64
oder gar nur 16 Bit sind, hängt von CPU, Betriebssystem,
32-/64-Bittigkeit des Betriebssystems, gewähltem Speichermodell
(x86/x86_64/x32) und der Mondphase ab.
Und weil der ursprüngliche C-Standard absichtlich so viele
Unwägbarkeiten enthielt (man dachte das ist ne super Idee: jede
Plattform nimmt was am "besten" für sie ist), und man gelernt hat, dass
der Ansatz nicht so toll funktioniert wie man dachte, gibt es eben
inzwischen die zusätzlichen Datentypen in stdint.h.
Florian Trück schrieb:> Mir ist noch nie untergekommen dass ein char signed sein könnte.
Mir schon oft. Ist auch konsistenter, da alle anderen Integer-Typen ja
auch signed sind, wenn nicht explizit ein 'unsigned' davor steht. Da man
damit aber eh niemals rechnet, sondern nur Zeichen drin abspeichert, ist
es auch völlig wurscht, ob's nun signed oder unsigned ist.
Peter Dannegger schrieb:> Nochn schönes Beispiel für die "signed char" Fallgrube ist auch das> Ummappen der deutschen Umlaute für ein LCD. Wenn man nicht nach unsigned> castet, funktioniert es nicht.>> Wenn man aber zur Umgehung all dieser Probleme die Stringpuffer als> unsigned anlegt, meckert einem der AVR-GCC ständig die Hucke voll.
Deswegen macht man das ja auch nicht so. Alle Stringpuffer sind char.
Und an einer einzigen Stelle, wo man den Wert dann in ein Register
schreibt, macht man die Konvertierung.
Es gibt eben getrennte Typen für "Zeichen" und für "Bytes", und char ist
eben der Typ ausschließlich für Zeichen. Das ist auch keine Eigenheit
von C. Auch bei den meisten anderen Sprachen muß ich eine Konvertierung
durchführen, wenn ich ein Zeichen habe und dieses als Bytewert nutzen
will.
Wenn in C unsigned char und signed char stattdessen unsigned byte und
signed byte heißen würden, char ohne signed und unsigned aber seinen
Namen behalten würde, wäre das wahrscheinlich jedem sofort klar.
Joachim минифлоть schrieb:> Ich schreibe uint8_t, weil ich die Trivialnamen hasse. Man sieht sofort,> dass das 8bit sind. mf
Allerdings gibt es den Fall, daß die Variable zwingend exakt 8 Bit groß
sein muß, gar nicht so oft.
Sam P. schrieb:> Und weil der ursprüngliche C-Standard absichtlich so viele> Unwägbarkeiten enthielt (man dachte das ist ne super Idee: jede> Plattform nimmt was am "besten" für sie ist), und man gelernt hat, dass> der Ansatz nicht so toll funktioniert wie man dachte, gibt es eben> inzwischen die zusätzlichen Datentypen in stdint.h.
Tja, diese Denkweise ist halt C. Besser wäre es gewesen, den bereits
existierenden Wörtern endlich mal ne saubere Bedeutung zu geben, damit
man sich drauf verlassen kann, daß int eben 16 Bit und long 32 Bit ist,
egal auf welcher Maschine. Diese an den Haaren herbeigezogenen
Pseudotypen 'x'int'n'_t sind eklig zu lesen und kein Compiler kennt sie
wirklich. stattdessen werden sie per #define überall wieder vom
Preprozessor auf die althergebrachten Typen char, int, long
zurückübersetzt, bevor der Compiler sie zu lesen bekommt. Wozu also
dieser unleserliche Mumpitz? Wenn überhaupt, wäre das ne Sache, die als
echter Datentyp direlt in den Compiler gehört hätte.
Aber laßt mal, woanders wird mittlerweile der gleiche Murks gemacht.
Siehe 'char' und chr(x) im aktuellen Delphi XE3. Wahrscheinlich haben
dort auch C-Programmierer angefangen...
W.S.
W.S. schrieb:> Tja, diese Denkweise ist halt C. Besser wäre es gewesen, den bereits> existierenden Wörtern endlich mal ne saubere Bedeutung zu geben, damit> man sich drauf verlassen kann, daß int eben 16 Bit und long 32 Bit ist,> egal auf welcher Maschine.
Tja.
Das 'Problem' mit den ISO-Gremien ist, dass sie nichts mehr ändern (oder
nur mit vorgehaltener Waffe), wenn etwas mal im Standard steht.
Die Idee war es halt, dass ein int sowas wie die Leib- und Magengröße
einer CPU sein sollte. Gewisse Mindeststandards werden garantiert. Alles
was darüber hinausgeht, legt der Compilerbauer und/oder die CPU fest.
War nicht unbedingt die stärkste Idee, ermöglicht aber auf der anderen
Seite Programme (die sich nur auf die zugesicherten Mindesteigenschaften
verlassen), vom kleinen 8-Bitter bis hinauf zu einem Supercomputer
unverändert laufen zu lassen und auf jeder Maschine wird der jeweils
schnellste Code dafür erzeugt.
W.S. schrieb:> Tja, diese Denkweise ist halt C. Besser wäre es gewesen, den bereits> existierenden Wörtern endlich mal ne saubere Bedeutung zu geben, damit> man sich drauf verlassen kann, daß int eben 16 Bit und long 32 Bit ist,> egal auf welcher Maschine.
Es gibt auch DSPs mit 24 Bit natürlicher Wortbreite. Darauf wäre C dann
nicht mehr sinnvoll möglich.
> Diese an den Haaren herbeigezogenen Pseudotypen 'x'int'n'_t sind eklig zu> lesen
Ich finde sie eigentlich sehr gut zu lesen, weil der Name schon genau
verrät, was es ist.
> und kein Compiler kennt sie wirklich.
Das ist aber die Schuld der Compiler-Hersteller und nicht die von C.
Dort ist das seit 14 Jahren bereits genormt, und jeder ISO-konforme
Compiler muß das mit dabei haben.
> Wenn überhaupt, wäre das ne Sache, die als echter Datentyp direlt in den> Compiler gehört hätte.
Wozu? Welchen Vorteil hätte das denn?
Rolf Magnus schrieb:> Wozu? Welchen Vorteil hätte das denn?
Ich hatte oben nicht zufällig PL/I genannt. Da wird die Genauigkeit per
Stellenzahl direkt in die Deklaration geschrieben. Wenn man also eine
binäre Variable mit 13 Bits ohne Vorzeichen braucht, dann schreibt man
das einfach da rein:
dcl x fixed bin(13) unsigned;
Was der Compiler intern daraus macht ist dann seine Sache.
Dummerweise waren PL/I Compiler anfangs äusserst unhandliche Kolosse,
weil da noch ein paar mehr gute und schlechte Ideen drin sind. So wie
Unix als Gegenentwurf zu Multics entstand - was in PL/I programmiert war
- entstand C als Gegenentwurf zu PL/I. Als Sprache, die auch auf kleinen
Computern umgänglich war, grad wie Unix ein Betriebssystem für kleine
Computer war.
PS: Ich finde PL/I insgesamt grauslich. Aber es illustriert den Punkt.
Joachim минифлоть schrieb:> Ich schreibe uint8_t, weil ich die Trivialnamen hasse. Man sieht sofort,> dass das 8bit sind. mf
Da setze ich noch eine drauf. Ich benutze bei uC nie die Namen mit "_t",
sondern nur U8, S8, U16 ...
W.S. schrieb:> Tja, diese Denkweise ist halt C. Besser wäre es gewesen, den bereits> existierenden Wörtern endlich mal ne saubere Bedeutung zu geben, damit> man sich drauf verlassen kann, daß int eben 16 Bit und long 32 Bit ist,> egal auf welcher Maschine.
Sie haben eine saubere Bedeutung, nur nicht die, die du gerne hättest.
"int" sagt: "Gib mir eine Ganzzahl, mit der ich sinnvoll auf dieser
Machine arbeiten kann." Weder macht es Sinn, einen 8-Bit-µC zu 32 Bit
ints zu zwingen, noch bringt es irgendeinen Vorteil, Rechner mit vielen
Gigabytes RAM (und entsprechend vielen Datensätzen etc. im Programm) bei
65535/32767 zu deckeln. Die Grundidee hinter "int" ist gut, sie macht
nur nicht das, was viele damit versuchen zu erreichen.
Aber es gibt da diese anderen Datentypen im C-Standard, die ebenfalls
eine saubere Bedeutung haben und so definiert sind wie du es möchtest.
Wie hiessen die nur nochmal?
> Diese an den Haaren herbeigezogenen> Pseudotypen 'x'int'n'_t sind eklig zu lesen und kein Compiler kennt sie> wirklich. stattdessen werden sie per #define überall wieder vom> Preprozessor auf die althergebrachten Typen char, int, long> zurückübersetzt, bevor der Compiler sie zu lesen bekommt.
stdint.h ist integraler Bestandteil der C-Laufzeitumgebung. Es sind
keine Pseudotypen und es findet keine Textersetzung im Präprozessor
statt. Es sind ordentlich per typedef definierte Typen, die genau auf
den Compiler zugeschnitten sind.
Nur weil man eine Headerdatei einbinden muss, ist das noch lange kein
Fremdkörper. Nenne mir eine Sprache, in der du nicht einige Bestandteile
der Standard-Laufzeitumgebung über eine Import-Deklaration einbinden
musst, wenn du sie nutzen willst. Dein offenbar bevorzugtes Delphi macht
es doch nicht anders.
> Wozu also> dieser unleserliche Mumpitz? Wenn überhaupt, wäre das ne Sache, die als> echter Datentyp direlt in den Compiler gehört hätte.
Ich finde ein 16-Bit "int" ziemlich unleserlich. Selbst beim ATMega, wo
16-Bit-int Sinn macht, hab ich mich manchmal ertappt, wie ich ungewollt
einen Overflow produziert habe. Ein int16_t zeight dagegen deutlich und
direkt, was man da gerade deklariert. Es ist standardisiert, jeder
Compiler aus diesem Jahrtausend versteht es, und es bedeutet immer das
gleiche.
Viel schlimmer ist die Pest an Bibliotheken und APIs, die alle noch
nicht begriffen haben, dass es stdint.h gibt und überall funktioniert,
und dann hat man in einem einzigen Programm plötzlich 4 verschiedene
Namen plus "int" für eine vorzeichenbehaftete 32-Bit-Ganzzahl. Das ist
unleserlich...
_t schrieb:> Da setze ich noch eine drauf. Ich benutze bei uC nie die Namen mit "_t",> sondern nur U8, S8, U16 ...
... dankeschön. Hoffentlich muss ich nie deinen Code warten.
Peter II schrieb:> _t schrieb:>> Da setze ich noch eine drauf. Ich benutze bei uC nie die Namen mit "_t",>> sondern nur U8, S8, U16 ...>> wie wie sehen signed aus?_t schrieb:> sondern nur U8, S8, U16 ...
^^
Ein ganzer Sack voll Irrtümer resultiert aus der Unkenntnis der
C-Datentypen.
Das ist allerdings verständlich, da der Hauptdatentyp von "C" ein Witz
ist.
Ich musste mal lernen: Integer ist der "natürliche" Datentyp des
Rechners.
Keine Ahnung ob es zu 4 Bit-Zeiten auch schon jemanden mit Namen Integer
gab.
Anno 8080/86/186 (Z80, 68xx und wie sie alle hießen) waren das 8 Bit.
Später bekamen "Rechners" Zuwachs und das Kind war plötzlich 16 Bit
breit. Das hüpfende Komma aber war - das Baby hieß auch Integer.
Irgendwo war hier die Phantasie in Sachen Namensgebung verschütt
jegangen. Heute streiten sich die Gelehrten, ob dem Baby die 32-er oder
die 64-er Klamotten passen - es geht natürlich um (Trommelwirbel)
Integer.
Etwas aber hat all das Überlebt und das ist der Datentyp char.
Ohne Vornamen oder mit signed davor ist er von -128 über 0 bis 127
spezifiziert. Heißt er aber unsigned, so geht er von 0 bis 255.
Damit das Ganze aber nicht so langweilig wird wurden, z.T. weil man
bestimmte Genauigkeiten benötigte, immer neue Datentypen geschaffen.
Vielleicht aber auch, weil man sich die ganzen Namen nicht mehr merken
konnte.
byte, short, word, longint u.s.w. Zum Teil mit, zum Teil ohne Vornamen.
Damit der Begriff: "Lebenslanges Lernen" auch mit Inhalt gefüllt wird,
sind dann die Hardwarenahen Buben und Mädels dazu übergegangen so
"C"-Krücken wie "uint8_t" oder "uint16_t" in die Welt zu setzen. Keine
Ahnung ob sie das Benötigten oder ob sie glaubten das wir alle von
Alzheimer befallen sind.
Also beteiligen Sie sich alle an dem Wettbewerb: "Wer findet einen
Begriff für den neuen Datentypen mit 127 Bit Breite plus Zusatzzahl".
amateur schrieb:> Ohne Vornamen oder mit signed davor ist er von -128 über 0 bis 127> spezifiziert.
Ohne Vornamen ist nicht spezifiziert ob signed oder unsigned.
Zudem gibt es mittlerweile 3 formal verschiedene char Typen, nämlich
char
signed char
unsigned char
wovon zwar 2 einen identischen Wertebereich haben, aber dennoch als
separate Typen behandelt werden. Was bei Pointern eine Rolle spielt:
char c;
signed char *scp = &c;
unsigned char *ucp = &c;
führt mit -Wall zu
2: warning: pointer targets in initialization differ in signedness
3: warning: pointer targets in initialization differ in signedness
@A.K.
Ich habe in meinem ganzen Leben noch keinen signed char verwendet. Dafür
sind mir meine Fingerkuppen einfach zu schade. Aus diesem Grunde stellt
sich das Problem, für mich, auch nicht.
Natürlich: Wenn ich dem einen Kind den Namen "Hans" gebe und dem Anderen
den Namen "Karl", so muss der Compiler meckern, wenn ich die Behauptung
aufstelle: Hans = Karl;
Für mich gilt aber, bis zum Beweis des Gegenteils: char = signed char;
Egal was ein Compiler dazu zu sagen hat.
amateur schrieb:> Ich musste mal lernen: Integer ist der "natürliche" Datentyp des> Rechners.
Du möchtest also keinen Datentyp haben, der für die Zielmaschine optimal
ist und als allgemeiner Ganzzahl-Datentyp sinnvoll bzw. ausreichend ist?
amateur schrieb:> Anno 8080/86/186 (Z80, 68xx und wie sie alle hießen) waren das 8 Bit.
Nicht für C. Ein "int" ist schon immer wenigstens 16 Bit gewesen, darf
aber beliebig groß werden. 8 Bit "int" wäre dann doch etwas zu nutzlos.
amateur schrieb:> Damit der Begriff: "Lebenslanges Lernen" auch mit Inhalt gefüllt wird,> sind dann die Hardwarenahen Buben und Mädels dazu übergegangen so> "C"-Krücken wie "uint8_t" oder "uint16_t" in die Welt zu setzen. Keine> Ahnung ob sie das Benötigten oder ob sie glaubten das wir alle von> Alzheimer befallen sind.
Du möchtest also nicht in der Lage sein, Präzision bzw. Speicherbedarf
von Daten genau spezifizieren zu können, wenn es nötig ist?
Junge, junge, was du dir wohl für Datentypen wünschst. Leider kannst du
dir für den 8080 nix mehr wünschen, die Geschichte ist geschrieben. Aber
wenn wir dann die Zeitmaschine erfinden, dann kannst du es ja besser
machen. 64-Bit Integer sind auf dem 4004 bestimmt ein Kassenschlager.
Ach was, ich steig da selbst rein und erfinde für C einfach ein Haufen
Typen die klar definiert sind, in offiziellen Standards, die aber keiner
liest. Ich nenn die dann "int", "short" und so weiter, nur um dich zu
ärgern. :)
amateur schrieb:> Ohne Vornamen oder mit signed davor ist er von -128 über 0 bis 127
Ach so, verlass dich auf die "-128" nicht allzusehr. Nirgendwo wird
verlangt, dass negative Zahlen im Zweierkomplement dargestellt werden.
Einerkomplement ist genauso erlaubt wie Zahl+Vorzeichenbit. Das macht
natürlich keiner, weil praktisch jedes nichttriviale C-Programm dann
nicht mehr funktioniert, aber erlaubt wäre es. Und auf irgendwelchen
speziellen µCs/DSPs mag das sogar seinen Sinn haben.
amateur schrieb:> Für mich gilt aber, bis zum Beweis des Gegenteils: char = signed char;> Egal was ein Compiler dazu zu sagen hat.
Lol. Interessante Herangehensweise.
Sam P. schrieb:> Einerkomplement ist genauso erlaubt wie Zahl+Vorzeichenbit. Das macht> natürlich keiner, weil praktisch jedes nichttriviale C-Programm dann> nicht mehr funktioniert, aber erlaubt wäre es.
Warum sollten Programme dann nicht mehr funktionieren?
>Du möchtest also nicht in der Lage sein, Präzision bzw. Speicherbedarf>von Daten genau spezifizieren zu können, wenn es nötig ist?
Es werden ja keine neuen Datentypen geschaffen sondern neue Namen.
Wenn ich mal die Krabbelkommazahlen außen vor lasse, hat sich seit char,
word, int oder longint nichts neues mehr ergeben. Konstrukte wie
uint8_t, uint16_t oder der große Bruder uint32_t sind ja nix als neue
Namen für alte Kollegen.
amateur schrieb:> uint8_t, uint16_t oder der große Bruder uint32_t sind ja nix als neue> Namen für alte Kollegen.
Ja und? Was machen Schlüsselworte besser als Typedefs?
"Ach so, verlass dich auf die "-128" nicht allzusehr. Nirgendwo wird
verlangt, dass negative Zahlen im Zweierkomplement dargestellt werden.
Einerkomplement ist genauso erlaubt wie Zahl+Vorzeichenbit. Das macht
natürlich keiner, weil praktisch jedes nichttriviale C-Programm dann
nicht mehr funktioniert, aber erlaubt wäre es. Und auf irgendwelchen
speziellen µCs/DSPs mag das sogar seinen Sinn haben."
Die Frage ist vor allem wieviel zu Bruch geht wenn man die Annahme dass
der Zahlenwert 0 intern bei jedem numerischen Typ
0x00/0x0000/0x00000000/... entspricht - und damit selbst bei brutalstem
Type Punning einem "false"! - aushebelt.
amateur schrieb:>>Du möchtest also nicht in der Lage sein, Präzision bzw. Speicherbedarf>>von Daten genau spezifizieren zu können, wenn es nötig ist?>> Es werden ja keine neuen Datentypen geschaffen sondern neue Namen.
Natürlich. Was sollte es auch bringen, komplett neue Typen in die
Sprache aufzunehmen, die exakt gleich wie die alten sind, außer daß sie
halt andere Namen haben?
> Wenn ich mal die Krabbelkommazahlen außen vor lasse, hat sich seit char,> word, int oder longint nichts neues mehr ergeben. Konstrukte wie> uint8_t, uint16_t oder der große Bruder uint32_t sind ja nix als neue> Namen für alte Kollegen.
Ein uint32_t ist garantiert 32 Bit groß, ein int nicht.
Sam P. schrieb:> Sie haben eine saubere Bedeutung, nur nicht die, die du gerne hättest.> "int" sagt: "Gib mir eine Ganzzahl, mit der ich sinnvoll auf dieser> Machine arbeiten kann." Weder macht es Sinn, einen 8-Bit-µC zu 32 Bit> ints zu zwingen, noch bringt es irgendeinen Vorteil, Rechner mit vielen> Gigabytes RAM (und entsprechend vielen Datensätzen etc. im Programm) bei> 65535/32767 zu deckeln. Die Grundidee hinter "int" ist gut, sie macht> nur nicht das, was viele damit versuchen zu erreichen.
Du hast ne ziemlich seltsame Denkweise, die ich nicht teile.
Als Programmierer muß man sich auf die verwendeten Datentypen verlassen
können, was in C nicht der Fall ist. Natürlich brauche ich auch auf
einem 8 Bit uC 32 Bit Integerzahlen, gelegentlich auch int64, und auch
auf der größten und dicksten Maschine ist es öfter als du denkst nötig,
Integers bei 16 Bit "zu deckeln", wenn die Daten nämlich in Strukturen
hineinpassen müssen, die von anderswo vorgegeben sind.
Mir ist ein bissel rätselhaft, wie du zu deinen Auffassungen kommst,
deswegen hier ein paar Beispiele, damit du merkst, worum es geht:
Fall 1: Ich will das Ergebnis eines ADC's lesen. z.B. AD7714. Kommt bei
Leuten, die mit Mikrocontrollern zu tun haben vor, sowas. Was kann ich
dazu nehmen? int oder long? Natürlich nehme ich long, weil das am
wahrscheinlichsten die 24 Bit aufnehmen kann, die der IC ausgibt. Auf
manchen Zielsystemen könnte ich auch int nehmen, aber wer garantiert mir
das? Merkst du was? Wohldefinierte Datentypen sind für saubere und
portable Programmierung wichtiger als die Befindlichkeit einer Maschine.
Fall 2: Ich will Daten auf eine SD-Karte schreiben und muß dazu die
SD-Karte ansprechen, also initialisieren, die diversen Parameterblöcke
auslesen, dann ein Filesystem aufsetzen usw. Natürlich richtet sich auch
hier kein SD-Karten-Hersteller nach den Befindlichkeiten des verwendeten
uC und auch kein Filesystem schert sich drum. Ich muß mich also mit
meinen Structs nach den Karten richten und nicht umgekehrt.
Und jetzt tönst du
""int" sagt: "Gib mir eine Ganzzahl, mit der ich sinnvoll..."
Kann man sinnvoll mit so einer verqueren Definition arbeiten? Nee,
natürlich nicht. Man muß leider stattdessen nachschauen, wie int, long
usw. auf dem konkreten Ziel definiert ist und seine Quelle danach
richten, sonst wird nix - insbesondere nix mit Zeigern. Aber damit ist
man eben überhaupt nicht portabel, sondern tanzt auf dem Schlappseil.
soviel zu der angeblich 'sauberen' Bedeutung.
Sam P. schrieb:> stdint.h ist integraler Bestandteil der C-Laufzeitumgebung. Es sind> keine Pseudotypen und es findet keine Textersetzung im Präprozessor> statt.
Ach ja? Lies mal (Auszug aus aktueller Yagarto)
typedef signed char int8_t ;
typedef unsigned char uint8_t ;
Du weißt, daß man mit typedef keine Typen definieren kann, ja?
Mit typedef kann man - salopp gesagt - einem bereits vorhandenen Typ
einen anderen Namen geben. Eben ein Pseudotyp
Es ist 'int8_t' für den Compiler also tatsächlich nix anderes als ein
schnöder 'signed char' - und wenn du ne inkompatible Verwendung im
Quelltext machst, dann lautet die Fehlermeldung etwa so:
"Error 123456: cannot convert signed char to blablabla."
Da wirst du eben nicht finden "...cannot convert int8_t to blablabla".
Was ich gemeint habe wäre, daß genau SOLCHE Zeilen eben nicht mehr
vorkommen dürften, sondern sowas wie 'int8_t' als solches vom Compiler
verstanden wird. Als echtes Schlüsselwort mit einer Bedeutung, die für
ALLE Maschinen gleich ist. Weil man nämlich beim Programmieren
Probleme lösen muß und dazu Datentypen benötigt, die den Problemen
angepaßt sind.
(über den konkreten Namen von sowas läßt sich streiten, 'byte' find ich
deutlich besser als 'uint8_t').
Also, hast du es jetzt gerafft?
W.S.
W.S. schrieb:> Als Programmierer muß man sich auf die verwendeten Datentypen verlassen> können, was in C nicht der Fall ist.
Dann lies das ABI und die Spezifikation der Implementierung!
Beispiel: avr-gcc ABI:
http://gcc.gnu.org/wiki/avr-gcc#ABI
Da steht nämlich drinne, wie groß ein int ist, wie breit ein Zeiger, ob
ein char signed ist oder nicht, wie Bitfelder gelayoutet sind, wie
Funktionsparameter übergeben wwerden, etc.
Und wenn die das mit dem int nicht gefällt, nimm eben in int32_t,
int32_fast_t oder int32_least_t.
Oder nimm Java oder Assembler wenn dir C nicht passt.
> Dann lies das ABI und die Spezifikation der Implementierung!
Der Murks geht dann los, wenn der Code portiert werden soll. Viel Spaß
beim umwursteln und durchsehen aller Variablen auf richtiges Format. :-(
Ich benutze schon lange kein int und co mehr. Totaler Mist.
int-Hasser schrieb:>> Dann lies das ABI und die Spezifikation der Implementierung!>> Der Murks geht dann los, wenn der Code portiert werden soll. Viel Spaß> beim umwursteln und durchsehen aller Variablen auf richtiges Format. :-(>> Ich benutze schon lange kein int und co mehr. Totaler Mist.
Ja was wollt ihr denn?
- int etc. ist euch nicht genehm
- uint32_t etc. auch nicht
- eine andere Spache auch nicht
Hauptsache Rummeckern. Wie die Leute die stänfig übers Wetter nörgeln
anstatt nach auszuwandern, gescheite Klamotten zu kaufen, sich
abzuhärten oder einfach am Kamin sitzen zu bleiben wenn's Wetter nicht
passt.
Und das niemand mehr Doku, Spezifikation, Spezifikaton der
Implementierung, (E)ABI, etc. durchliest -- oder wenigstens nicht bei
jeder sich bietenden Gelegenheit seine Unwissenheitsfrust rausposant --
ist im Zeitalter der 1-Click Mentalität wohl als gegeben hinzunehmen...
> Ja was wollt ihr denn?
Maoam? :)
> uint32_t etc. auch nicht
doch - das ist ok. Da weiß man ja was man hat. Es geht ja grade darum,
dass man als Programmierer etwas haben will, was eindeutig und
unabhängig von der Bitbreite der Zielhardware definiert ist.
so kommt es dazu, dass code wie dieser hier auf einem Cortex korrekt
funktioniert aber nicht auf einem Atmega (weil dort ein int nur 16 bit
hat):
1
voidxy(void)
2
{intbla
3
4
bla=var_32_bit;
5
//...
6
}
Viel Spaß beim Portieren! Hätte man statt int einfach int32_t genommen
wäre das nicht passiert und der code wäre ohne änderung portierbar
geblieben.
W.S. schrieb:> Du hast ne ziemlich seltsame Denkweise, die ich nicht teile.> Als Programmierer muß man sich auf die verwendeten Datentypen verlassen> können, was in C nicht der Fall ist.
Natürlich kann man sich auf "int" verlassen. Nämlich darauf, dass ein
"int" relativ effizient in Berechnungen eingesetzt werden kann und dabei
nicht unnütz klein ist. Das ist auf jedem System so, das einen
gescheiten C-Compiler hat. Selbstverständlich kannst du dich nicht auf
etwas verlassen, was gar nicht versprochen wird. "int" verspricht nicht,
dass es eine feste Größe hat, aber jeder C-Neuling glaubt, das wäre so.
Das ist das (zugegeben unglückliche) Missverständnis.
Und darum ist es gut, dass es "jetzt endlich" (seit ~15 Jahren)
zusätzliche Typen gibt, die eben eine bestimmte Größe versprechen, dabei
aber eventuell langsam sein könnten.
> Natürlich brauche ich auch auf> einem 8 Bit uC 32 Bit Integerzahlen, gelegentlich auch int64, und auch> auf der größten und dicksten Maschine ist es öfter als du denkst nötig,> Integers bei 16 Bit "zu deckeln", wenn die Daten nämlich in Strukturen> hineinpassen müssen, die von anderswo vorgegeben sind.
Da hast du völlig recht. Darum gibt es int16_t/int32_t/int64_t.
short/int/long/long long sind dafür weder gedacht noch geeignet. Guck
dir ein beliebiges C-Projekt an, das in irgendeiner Form von genauen
Größen abhängig ist und auf wenigstens 2 Plattformen läuft (und wenn's
nur Win32 und Win64 sind). Ist die Software nur alt genug, haben die
Programmierer genau das gemacht, was mit stdint.h nun vereinheitlicht
ist. Keiner, der mal etwas real portieren musste, benutzt int&Co, wenn
die genaue Speicherbelegung relevant ist.
> Fall 1: Ich will das Ergebnis eines ADC's lesen. z.B. AD7714. Kommt bei> Leuten, die mit Mikrocontrollern zu tun haben vor, sowas. Was kann ich> dazu nehmen? int oder long? Natürlich nehme ich long, weil das am> wahrscheinlichsten die 24 Bit aufnehmen kann, die der IC ausgibt. Auf> manchen Zielsystemen könnte ich auch int nehmen, aber wer garantiert mir> das? Merkst du was? Wohldefinierte Datentypen sind für saubere und> portable Programmierung wichtiger als die Befindlichkeit einer Maschine.
long? Wenn du Pech hast, hat ein long auch nur 16 Bit. "Am
wahrscheinlichsten"? Seltsame Art zu programmieren. Jemand, der C kann,
nimmt einen int32_t, da hast du deine klare Definition. Du willst klar
definierte Typen, lehnst aber die klar definierten Typen ab.
> Fall 2: Ich will Daten auf eine SD-Karte schreiben und muß dazu die> SD-Karte ansprechen, also initialisieren, die diversen Parameterblöcke> auslesen, dann ein Filesystem aufsetzen usw. Natürlich richtet sich auch> hier kein SD-Karten-Hersteller nach den Befindlichkeiten des verwendeten> uC und auch kein Filesystem schert sich drum. Ich muß mich also mit> meinen Structs nach den Karten richten und nicht umgekehrt.
Uh, structs. Ein weiteres Minenfeld. Grobe Faustregel: Bei structs hast
du keinerlei Garantien, wo die Variablen landen. Nur die Reihenfolge
wird eingehalten. Es gibt Architekturen (z.B. ARMv4), die können auf
keine int32_t zugreifen, die nicht auf durch 4 teilbaren Adressen
liegen. Auf einem solchen System muss eine
1
struct{
2
uint16_tcmd;
3
uint32_taddr;
4
}
8 Bytes lang sein, statt der 6 die man erwarten könnte. Da kann man bei
vielen Compilern per #pragma oder _attribute_ etwas anderes erzwingen,
aber der ARMv4 kann gar nicht anders. Und darum ist C so schwammig, weil
eben die Hardware Vorrang vor der Bequemlichkeit des Programmierers hat.
Das ist nicht das was du hören möchtest oder von einer bequemen
Programmiersprache erwartest, aber beim hardwarenahen Programmieren geht
es eben nicht anders. Hätte man einen Standard verabschiedet, der alle
Plattformen über einen Kamm schert, dann wäre das einzige Resultat, dass
jede abweichende Plattform einen Scheiss auf den Standard gibt und
eigene Regeln erlässt. Das Resultat wäre das gleiche wie jetzt, nur
jetzt ist klar standardisiert, wo ein Compiler auf welche Weise
abweichen darf, und wo nicht.
> Und jetzt tönst du> ""int" sagt: "Gib mir eine Ganzzahl, mit der ich sinnvoll...">> Kann man sinnvoll mit so einer verqueren Definition arbeiten? Nee,> natürlich nicht. Man muß leider stattdessen nachschauen, wie int, long
Da die Definition nicht verquer ist, sondern nur etwas verspricht, was
dir egal ist, kann ich sehr gut mit sowas arbeiten. Ich weiss, worauf
ich mich bei einem int verlassen kann, und worauf nicht. int64_t
verspricht mir etwas anderes, und lässt dafür die Frage nach der
Effizienz offen.
Meine Schleifenindizes sind ints oder unsigned ints, solange ich nicht
aufgrund besonderer Umstände mit besonders großen Zahlen rechnen muss.
Meine Arrays sind per uintXX_t genauer spezifiziert, weil dort der
Speicherbedarf relevant ist und man "int" da nicht vertrauen kann.
> usw. auf dem konkreten Ziel definiert ist und seine Quelle danach> richten, sonst wird nix - insbesondere nix mit Zeigern. Aber damit ist> man eben überhaupt nicht portabel, sondern tanzt auf dem Schlappseil.
Eben. Ich verwende ja auch keinen Schraubenzieher als Meissel. Das geht
ne Weile gut, für einen Quick&Dirty-Job kommt man auch damit durch, aber
irgendwann merkt man, dass ein Meissel andere Versprechen hält als ein
Schraubenzieher.
> soviel zu der angeblich 'sauberen' Bedeutung.
Lies den C-Standard, dann weisst du wie sauber das definiert ist. Du
kannst dir gerne ein anderes C herbeiwünschen, aber weder du noch ich
machen die Regeln, sondern das ISO-Gremium. Und die sagen klipp und
klar, was geht und was nicht geht.
> Sam P. schrieb:>> stdint.h ist integraler Bestandteil der C-Laufzeitumgebung. Es sind>> keine Pseudotypen und es findet keine Textersetzung im Präprozessor>> statt.>> Ach ja? Lies mal (Auszug aus aktueller Yagarto)>> typedef signed char int8_t ;> typedef unsigned char uint8_t ;>> Du weißt, daß man mit typedef keine Typen definieren kann, ja?> Mit typedef kann man - salopp gesagt - einem bereits vorhandenen Typ> einen anderen Namen geben. Eben ein Pseudotyp
Es ist aber kein Präprozessor-Makro, es basiert nicht auf Textersetzung,
es funktioniert wie jedes andere Symbol auch. Damit hält es sich an alle
Regeln der Sprache und ist für den Programmierer ununterscheidbar von
einem vom Compiler vordefinierten Typ. Wie das unter der Haube
realisiert wird, ist doch vollkommen egal.
Schreib dir halt einen Patch für deinen Compiler, der "uint8_t" intern
definiert, sobald er die magische Zeile "#include <stdint.h>" liest. Die
interne Datenstruktur, die du dabei anlegst, wird bis auf den Namen
exakt die von "signed char" sein. Und nun rate mal, was bei einem
typedef passiert. Genau das gleiche.
> Es ist 'int8_t' für den Compiler also tatsächlich nix anderes als ein> schnöder 'signed char' - und wenn du ne inkompatible Verwendung im
Och, "char" könnte ja auch 16 Bit haben. Kein Compilerhersteller wäre so
verwegen, aber erlaubt wäre es. Dann wäre "int8_t" irgendwas anderes als
"char", z.B. ein interner 8-Bit-Datentyp.
> Quelltext machst, dann lautet die Fehlermeldung etwa so:> "Error 123456: cannot convert signed char to blablabla."> Da wirst du eben nicht finden "...cannot convert int8_t to blablabla".
Das liegt an deinem Compiler, nicht am C-Standard. Meiner ist so
hilfreich und nennt sowohl den verwendeten Typennamen, als auch die
zugrundeliegende Definition des Typen. Das ist sogar noch hilfreicher,
weil ich damit zusätzliche Informationen zur impliziten Typumwandlung
bekommen (gleich der nächste Fettnapf-Klassiker für C-Neulinge).
Ich gebe dir recht, dass Compiler-Fehlermeldungen einem das Leben schon
schwer machen können. Ich habe z.B. C++ STL lange gehasst, wegen der
undurchschauberen Fehlermeldungen. Aber das ist nicht die Schuld von
C(++), sondern des Compiler-Herstellers. Wenn das der einzige haltbare
Grund ist, ist das Jammern auf hohem Niveau.
> Was ich gemeint habe wäre, daß genau SOLCHE Zeilen eben nicht mehr> vorkommen dürften, sondern sowas wie 'int8_t' als solches vom Compiler> verstanden wird. Als echtes Schlüsselwort mit einer Bedeutung, die für> ALLE Maschinen gleich ist. Weil man nämlich beim Programmieren
int8_t ist für alle Maschinen gleichbedeutend, solange wir vom
Speicherbedarf reden. Darum gibt es diese Typen doch. Das ist genau das,
was du die ganze Zeit herbeisehnst. Klare Ansage, auf jeder Plattform
vorhanden, gehorcht überall den gleichen Regeln, ist nicht von anderen
Datenypen zu unterscheiden, wo war nochmal das Problem?
Ach ja, Compiler-Fehlermeldungen. Nimm demnächst gcc-4.8, der ist so
dermaßen ausführlich, dass da niemand mehr auch nur irgendwas
missverstehen kann. Der zeigt dir sogar die Quelltext-Zeile mit der
genauen Position des Fehlers. Da wo dann "int8_t" steht. Ich hoffe das
reicht dir. Wird wohl nicht mehr lange dauern, bis der 4.8er offiziell
herausgegeben wird. Die Prereleases funktionieren schon ganz gut.
> Probleme lösen muß und dazu Datentypen benötigt, die den Problemen> angepaßt sind.
Lautet dein Problem "Messwerte mit Zahlenwerten zwischen 0 und 3
Millarden speichern", dann nimm einen uint32_t pro Messwert.
Lautet dein Problem "So viele Messwerte, wie ins RAM passen, in einer
Schleife erfassen und abspeichern", dann ist "unsigned int" die einzige
sinnvolle Wahl für den Schleifenzähler/Array-Index. uint16_t wäre auf
einer 32-Bit-Architektur zu klein, uint32_t wäre auf einem 8-Bitter zu
langsam.
> Also, hast du es jetzt gerafft?
Offensichtlich nicht. Es gibt das was du dir wünschst, wenn auch mit
Namen, die deiner Ästhetik nicht ganz entsprechen. Es ist offiziell, es
ist überall verfügbar, überall gleich definiert und voll in die Sprache
integriert. Willst du jetzt allen Ernstes über Compiler-Fehlermeldungen
schmollen, wo ohenhin jeder Compiler-Hersteller tun und lassen kann was
er will, und deswegen so eine sinnvolle Einrichtung ablehnen?
Rolf Magnus schrieb:> Sam P. schrieb:>> Einerkomplement ist genauso erlaubt wie Zahl+Vorzeichenbit. Das macht>> natürlich keiner, weil praktisch jedes nichttriviale C-Programm dann>> nicht mehr funktioniert, aber erlaubt wäre es.>> Warum sollten Programme dann nicht mehr funktionieren?
Sehr viel Code macht Annahmen über die konkreten Bitmuster, die einer
bestimmten Zahl entsprechen. Heutzutage gibt es bei Integern den
Quasi-Standard "Zweierkomplement", der auch viel Sinn macht. Aber
nirgendwo steht geschrieben, dass das in C unbedingt so sein muss. "~0"
kann entweder "-1" oder "-0" sein. "-0" alleine ist schon kurios (gibts
bei Fliesskomma immer noch). "(-1) << 1" ist -2, -3 oder +2, je nachdem
ob man Zweierkomplement, Einerkomplement oder "positive Zahl plus
Vorzeichenbit" hat. -127 - 1 als int8_t kann -128, +127 oder +0 sein.
Darum ist Überlauf bei vorzeichenbehafteten Zahlen in C auch offiziell
undefiniert.
Das ändert aber nichts an der Realität, dass ganz oft damit gearbeitet
wird. Gerade die Shift-Operatoren werden gerne als angeblich effiziente
Multiplikation/Division verwendet, wo doch jeder Compiler der letzten 10
Jahre ein "* 4" automatisch zu einem "<< 2" umschreibt, dabei aber die
Zahlenformate korrekt berücksichtigt. Für ein ganz perverses Beispiel
von Bitmuster-Hacking google mal nach 0x5f3759df.
Nu is aber gut, das ist ja furchtbar lang. Ich hoffe, die
Ausführlichkeit hat wenigstens zu etwas Erkenntnisgewinn beigetragen.
U8 S8 U16.... ist bei Silabs sehr gebräuchlich und keines wegs schlecht
zu verstehen, habe das auch schon bei anderen gesehen... glaube auch bei
peda :-)
Sam P. schrieb:> long? Wenn du Pech hast, hat ein long auch nur 16 Bit.
Nein. Muss mindestens 32 Bits haben.
> Meine Schleifenindizes sind ints oder unsigned ints, solange ich nicht> aufgrund besonderer Umstände mit besonders großen Zahlen rechnen muss.
Yep. Konsequente Verwendung von [u]intNN_t mit NN <= 16 kann zu recht
ineffizientem Code führen, da mangels entsprechender Operationen bei
RISCs wie ARM immer wieder das Resultat auf NN Bits reduziert werden
muss.
Offiziell sind dafür deshalb die Typen [u]int_fastNN_t vorgesehen, aber
an Stelle von [u]int_fast16_t kann man für lokale Variablen de fakto
ebenso gut [unsigned]int verwenden.
> Och, "char" könnte ja auch 16 Bit haben. Kein Compilerhersteller wäre so> verwegen, aber erlaubt wäre es. Dann wäre "int8_t" irgendwas anderes als> "char", z.B. ein interner 8-Bit-Datentyp.
In diesem Fall würde int8_t wahrscheinlich nicht geben. Diesen Typ gibt
es nur, wenn ein Typ mit exakt 8 Bits überhaupt existiert.
Peter II schrieb:> im gcc ist es immer signed> #include <stdio.h>> int main() {> char c = 0xff;> int i = c;> printf("test:%d\n", i );>> na was kommt raus?
Moin, das heißt nur, das du von C programmieren keine Ahnung hast. Wenn
du dem natürlich sagst, dass er es als signed interpretieren soll...
versuch mal den Umkehrschluss mit printf("test:%u\n", i ); und int. Das
wird dich überraschen.
Ich weiß, das ist Uhralt, aber mich hats grad in den Fingern gejukt^^
Grüße,
@ Sam P.: Super Beitrag. Damit ist wohl so ziemlich alles abgedeckt, was
man zu dem Thema sagen kann. :)
A. K. schrieb:> Yep. Konsequente Verwendung von [u]intNN_t mit NN <= 16 kann zu recht> ineffizientem Code führen, da mangels entsprechender Operationen bei> RISCs wie ARM immer wieder das Resultat auf NN Bits reduziert werden> muss.>> Offiziell sind dafür deshalb die Typen [u]int_fastNN_t vorgesehen, aber> an Stelle von [u]int_fast16_t kann man für lokale Variablen de fakto> ebenso gut [unsigned]int verwenden.
Auf 8-Bit-Mikrocontrollern reichen für viele Dinge wie Schleifen und
Statusvariablen allerdings auch schon 8 Bit. In solchen Fällen ist man
mit uint_fast8_t am besten bedient. Dafür gibt es keine Entsprechung in
den nativen Typen.
Ich bin allerdings auch nicht besonders konsequent bei der Verwendung,
wenn ich nicht schon weiß, dass der Code auf mehreren Plattformen laufen
muss. Werde dank des Threads da mal wieder genauer drauf achten ... :)
Hallo,
habt Ihr bitte einen guten Artikel, aus dem die richtige und sinnvolle
Verwendung der verschiedenen Datentypen hervorgeht?
Von "uint_fast8_t" hatte ich bis dato leidr noch nicht mal was gehört...
aber man lernt ja gerne dazu
Danke
A. K. schrieb:>> Meine Schleifenindizes sind ints oder unsigned ints, solange ich nicht>> aufgrund besonderer Umstände mit besonders großen Zahlen rechnen muss.>> Yep. Konsequente Verwendung von [u]intNN_t mit NN <= 16 kann zu recht> ineffizientem Code führen, da mangels entsprechender Operationen bei> RISCs wie ARM immer wieder das Resultat auf NN Bits reduziert werden> muss.
Das sieht man besonders häufig bei Code, der aus der 16-Bit Ära überlebt
hat — und das ist eher die Regel als die Ausnahme, etwa bei den
Automotives. Der Code ist dann gepflastert mit hausbackenen NIH-Typen*
wie U16, S16, WORD, sint16 oder uint16. Und selbst [u]int16_t ist da
voll exotisch.
Auf einem 32-Bit µC bremst knabbert das natürlich merklich an der
Performance: Der Code wird langsamer und größer.
> Offiziell sind dafür deshalb die Typen [u]int_fastNN_t vorgesehen, aber> an Stelle von [u]int_fast16_t kann man für lokale Variablen de fakto> ebenso gut [unsigned]int verwenden.
Die sind mir in freier Wildbahn allerdings noch nie begegnet, was
mehrere Gründe haben mag: Mangelndes Wissen der Anwender, Paranoide
Coding-Rules, NIH-Attitüden, oder es liegt schlicht und ergreifend kein
C99 vor wegen M$-Compiler etc.
*) NIH = "Not invented here". Alles was nicht auf unserem Mist
gewachsen ist, ist Schrott.
W.S. schrieb:> Du hast ne ziemlich seltsame Denkweise, die ich nicht teile.
Die ist nicht seltsam.
> Mir ist ein bissel rätselhaft, wie du zu deinen Auffassungen kommst,> deswegen hier ein paar Beispiele, damit du merkst, worum es geht:>> Fall 1: Ich will das Ergebnis eines ADC's lesen.> Fall 2: Ich will Daten auf eine SD-Karte schreiben und muß dazu die> SD-Karte ansprechen, also initialisieren, die diversen Parameterblöcke> auslesen, dann ein Filesystem aufsetzen usw.
Beides Lowlevel-Interfaces nach außen. Für die braucht man natürlich
einen passenden Typen. Aber der muß nicht nur die richtige Größe haben,
sondern auch die richtige Byteorder und andere Dinge. Willst du die dem
Compiler auch global vorschreiben, auch wenn die auf dem Zielsystem
höllisch langsam sind, nur damit man beim Ansprechen einer SD-Karte
nicht nachdenken muß? Der Löwenanteil des C-Codes, der auf einem System
läuft besteht in der Regel nicht aus lowlevel-Interfaces. Und dort sind
effiziente Typen wichtiger als welche, die sich an starre Vorgaben von
außen halten, insbesondere, wenn diese Vorgaben bei mehreren Interfaces
auch noch unterschiedlich sein können.
> Kann man sinnvoll mit so einer verqueren Definition arbeiten?
Ja. Konnte ich bisher zumindest immer.
> Es ist 'int8_t' für den Compiler also tatsächlich nix anderes als ein> schnöder 'signed char' - und wenn du ne inkompatible Verwendung im> Quelltext machst, dann lautet die Fehlermeldung etwa so:> "Error 123456: cannot convert signed char to blablabla."> Da wirst du eben nicht finden "...cannot convert int8_t to blablabla".
Ach ja? Bei
1
#include<stdint.h>
2
#include<stdio.h>
3
intmain()
4
{
5
struct{}s;
6
int8_ti;
7
s=i;
8
scanf("%f\n",&i);
9
}
spuckt mein gcc aus:
1
uint.c:7:7: Fehler: unverträgliche Typen bei Zuweisung an Typ »struct <anonym>« von Typ »int8_t«
2
uint.c:8:5: Warnung: Format »%f« erwartet Argumenttyp »float *«, aber Argument 2 hat Typ »int8_t *« [-Wformat]
Die Sache ist also eher ein Problem der Qualität des Compilers, denn es
ist diesem nicht verboten, bei Warnungen den typedef-Namen auszugeben
statt des Namens, auf den der typedef verweist.
> Was ich gemeint habe wäre, daß genau SOLCHE Zeilen eben nicht mehr> vorkommen dürften, sondern sowas wie 'int8_t' als solches vom Compiler> verstanden wird. Als echtes Schlüsselwort mit einer Bedeutung, die für> ALLE Maschinen gleich ist.
Sie ist für alle Maschinen gleich. Es ist halt ein typedef, statt eines
Schlüsselwortes. Einen Nachteil kann ich dabei nicht erkennen.
Nebenbei: Der Unterschied zwischen Schlüsselworten und Typedefs ist
syntaktisch betrachtet nicht sonderlich gross. Typedefs wurden erst
nachträglich in C eingeführt, allerdings schon zu K&R Zeiten, und passen
eigentlich nicht in eine Syntax, in der Deklarationen durch ein
einleitendes Typ-Schlüsselwort wie "int" erkannt werden. Das gibt
Typedef-Namen eine Sonderrolle, da sie keine einfachen Identifier sein
können.
Folglich wird ein Compiler mit schematischem Parser wie Bison bereits in
der lexikalischen Analyse einen Identifier danach untersuchen, ob es ein
Typedef-Name oder ein anderer Name ist, und ein entsprechendes anderes
Token an den Parser reichen. Eingeschränkt dadurch, dass der Name an
einigen wenigen Stellen wieder ein normaler Name ist, beispielsweise
direkt hinter enum/struct/union, was Lexer und Parser enger koppelt, als
Reitern von Sprachprinzipien lieb ist.
C-Gast schrieb:> Hallo,> habt Ihr bitte einen guten Artikel, aus dem die richtige und sinnvolle> Verwendung der verschiedenen Datentypen hervorgeht?> Von "uint_fast8_t" hatte ich bis dato leidr noch nicht mal was gehört...> aber man lernt ja gerne dazu> Danke
Du musst Dir im Prinzip einfach drei Fragen beantworten:
1. Können die zu speichernden Werte negativ sein?
Wenn nein, nimmt man einen vorzeichenlosen Typ (unsigned, uintXX_t).
Wenn ja, einen vorzeichenbehafteten (signed, intXX_t).
2. Wie groß können die Werte maximal werden?
Davon hängt die erforderliche Breite des Typs in Bit ab.
1
Breite | unsigned | signed
2
| min | max | min | max
3
8 Bit | 0 | 255 | -128 | 127
4
16 Bit | 0 | 65.535 | -32.768 | 32.767
5
32 Bit | 0 | 4.294.967.295 | -2.147.483.648 | 2.147.483.647
6
64 Bit | 0 | 2^64 - 1 | -2^63 | 2^63 - 1
3. Muss der Datentyp auf jeder Plattform gleich groß sein?
Wenn ja, dann nimmt man uint8_t, int16_t etc. Damit weiß man genau, wie
viele Bytes die Daten im Speicher belegen. Das ist inbesondere bei
Datenstrukturen wichtig, die man mit anderen Komponenten austauscht.
In den meisten anderen Fällen genügt es aber, wenn man weiß, dass der
Typ mindestens den erforderlichen Wertebereich abdeckt. Es macht aber
nichts, wenn man auch größere Zahlen darin speichern könnte. Das gilt
besonders für lokale Variablen für Berechnungen, Schleifenzähler,
Statusvariablen usw.. Dafür gibt es die Typen (u)int_leastXX_t und
(u)int_fastXX_t. Die garantieren, dass sie mindestens XX Bit breit sind,
können aber auch größer sein.
Der Typ int_fastXX_t hat den Hintergrund, dass es auf
32/64-Bit-Prozessoren in der Regel aufwändiger ist, mit nur 8 oder 16
statt den vollen 32/64 Bit des Registers zu rechnen, da die oberen Bits
immer noch extra ausmaskiert werden müssen. Bei so einer Plattform
bremst man das Programm also unnötig aus, wenn man auf kleinere
Bitbreiten für die Berechnungen besteht. Mit int_fastXX_t erhält man
immer den Typ, der sich am effizientesten verarbeiten lässt, aber
mindestens XX Bits aufnimmt. Ein int_fast8_t wird auf einem
32-Bit-Prozessor also höchstwahrscheinlich 32 Bit breit sein, auf einem
8-Bit-Mikrocontroller dagegen nur 8 Bit.
Die int_leastXX_t sind dagegen afaik eher akademischer Natur. Sie bieten
mindestens XX Bit und sind der kleinstmögliche Typ, der mit dieser
Breite zur Verfügung steht. In aller Regel dürften sie daher genau XX
Bit breit sein, sofern die Plattform nicht über eine ganz exotische
Speicherorganisation verfügt. Bitte korrigieren, falls ich da falsch
liege ...
So, und dann gibt es noch die traditionellen C-Datentypen:
Breite
char mind. 8 Bit
short int mind. 16 Bit
int mind. 16 Bit
long int mind. 32 Bit
long long int mind. 64 Bit
Jeder davon ist auch unsigned möglich.
Der einfache int entspricht normalerweise entweder dem short int oder
long int, jenachdem, was auf der Plattform effizienter zu rechnen ist.
Damit ist ein int praktisch äquivalent zu einem int_fast16_t. Für
int_fast8_t gibt es dagegen keine Entsprechung. Auf einem
Mikrocontroller müsste man (signed) char benutzen, auf größeren
Plattformen dagegen int.
Fabian O. schrieb:> Die int_leastXX_t sind dagegen afaik eher akademischer Natur.
Sie sind dort relevant, wo ein exakter Typ nicht vorliegt. Mangels
entsprechender Maschinenoperationen hatte ich bei einem Compiler für
Transputer in der 32-Bit Fassung keinen 16-Bit Datentyp implementiert.
int16_t wird in einem solchen Fall nicht existieren, int_least16_t
hingegen schon.
Fabian O. schrieb:> Der einfache int entspricht normalerweise entweder dem short int oder> long int, jenachdem, was auf der Plattform effizienter zu rechnen ist.
Nein. Das in Linux übliche 64-Bit Programmiermodell LP64 verwendet 64
Bits für "long", 32 Bits für "int" und 16 Bits für "short". Nur in der
Windows-Variante LLP64 muss für 64 Bits auf "long long" ausgewichen
werden.
A. K. schrieb:> Sie sind dort relevant, wo ein exakter Typ nicht vorliegt.
Dorgh, ich muss aus Versehen auf Absenden statt Vorschau geklickt haben.
Beim Durchlesen ist es mir nämlich auch aufgefallen. Beim Absenden kam
dann die Meldung, ich dürfte nich mehr editieren ...
Hier die richtige Fassung:
Die int_leastXX_t sind dagegen afaik eher ungebräuchlich. Sie bieten
mindestens XX Bit und sind der kleinstmögliche Typ, der mit dieser
Breite zur Verfügung steht. In der Regel sind sie also genau XX Bit
breit. Nur falls die Plattform keine kleineren Typen addressieren kann,
sind sie größer. Das ist beispielsweise bei manchen DSPs der Fall, bei
denen ein char dann 16 Bit breit ist. Man kann die int_leastXX_t also
nutzen, wenn einem wichtig ist, dass möglichst wenig RAM gebraucht wird,
der Code aber auch auf Plattformen läuft, auf denen es z.B. kein int8_t
gibt.
A. K. schrieb:> Fabian O. schrieb:>> Der einfache int entspricht normalerweise entweder dem short int oder>> long int, jenachdem, was auf der Plattform effizienter zu rechnen ist.>> Nein. Das in Linux übliche 64-Bit Programmiermodell LP64 verwendet 64> Bits für "long", 32 Bits für "int" und 16 Bits für "short". Nur in der> Windows-Variante LLP64 muss für 64 Bits auf "long long" ausgewichen> werden.
OK, wieder was gelernt. Habe mir schon fast gedacht, dass es da
Ausnahmen von gibt, daher "normalerweise" ... Dass das bei Linux
Standard ist, war mir aber nicht bewusst. :)
Fabian O. schrieb:> Davon hängt die erforderliche Breite des Typs in Bit ab.> 8 Bit | 0 | 255 | -128 | 127
Der Vollständigkeit halber: Der Standard garantiert nur -127..+127.
Grössere Typen analog. Ist aber eine eher exotische Einschränkung.
Sam P. schrieb:>> Also, hast du es jetzt gerafft?>> Offensichtlich nicht. Es gibt das was du dir wünschst, wenn auch mit> Namen, die deiner Ästhetik nicht ganz entsprechen. Es ist offiziell, es> ist überall verfügbar, überall gleich definiert und voll in die Sprache> integriert...
Es ist ein Manko, ein Geburtsfehler der Sprache C - und keiner hat es
bisher geschafft, damit mal wirklich aufzuräumen. Leider. Immer nur ein
Workaround auf den anderen. Wer in C programmiert, muß damit irgendwie
zurechtkommen. Wie auch immer.
Die Ästhetik finde ich Mist. Aber da all diese Pseudotypen ja ohnehin
nicht zum Sprachumfang gehören, kann sich jeder nach eigenem Gusto U8,
W16, N24, uint16_t und noch viele weitere Hausausdrücke einfallen
lassen. Babylon ist überall.
Aber mal zum Kern der Sache:
Es (s.o.) ist weder voll noch irgendwie in die Sprache integriert,
sondern völlig unintegriert als Hilfs-Headerdatei vorliegend.
Wäre "ES" integriert, dann würde man die von dir genannte Headerdatei
überhaupt nicht benötigen, weil es die betreffenden Schlüsselwörter in
der Sprachdefinition gäbe.
Aber es ist nicht integriert und man benötigt diese
compilerherstellerabhängige Headerdatei, um das Konstrukt (u)int(n)_t in
eine dem Compiler verständliche Folge von Schlüsselwörtern
zurückzuübersetzen. Das war und ist es, was ich dir klarmachen will.
So.
Man kann mit all dem zurechtkommen. Irgendwie. Du, ich und all die
anderen.
Aber man sollte es beim Namen nennen. Mist ist Mist - und es ist eben
kein Gold bloß weil man es selber nicht ändern kann. Ich zumindest
versuche nicht, mir die Dinge schönzureden.
Und nun zurück zur Ausgangsfrage:
Torsten B. schrieb:> Weil es in der " stdint.h" so definiert ist ! Ja!> Aber warum weicht diese vom C-Standard ab ?
Antwort: Weil die entscheidenden Gremien es nicht geschafft haben, die
Sprache C wirklich zu modernisieren und sie stattdessen Workarounds (im
Klartext "Hilfskrücken") wie stdint.h sich ausgedacht haben. Dort darf
dann jeder, der sich berufen fühlt, diese Pseudotypen in geeigneter Form
auf das herunterbrechen, was sein Compiler denn so tatsächlich versteht.
W.S.
W.S. schrieb:> Die Ästhetik finde ich Mist. Aber da all diese Pseudotypen ja ohnehin> nicht zum Sprachumfang gehören, ...
Doch tun sie. Es ist C99. Sie gehören also seit weit über 10 Jahren zum
Standard. Wenn dir das zu wenig ist, kann die niemand helfen.
Es ist auch nicht unüblich, Sprachen zu erweitern: Python, Java,
Fortran, Ada, um nur ein paar wenige zu nennen.
Aber dass es dor nicht darum geht, sondern ums Nörgeln, ist inzwischen
mehr als klar und jeder hat es verstanden.
W.S. schrieb:> Aber mal zum Kern der Sache:> Es (s.o.) ist weder voll noch irgendwie in die Sprache integriert,> sondern völlig unintegriert als Hilfs-Headerdatei vorliegend.>> Wäre "ES" integriert, dann würde man die von dir genannte Headerdatei> überhaupt nicht benötigen, weil es die betreffenden Schlüsselwörter in> der Sprachdefinition gäbe.
Hätte man es direkt integriert (das wäre trivial gewesen), wäre der neue
Standard inkompatibel zum alten. Es gäbe neue Schlüsselwörter und bisher
gültige Programme ließen sich mit aktualisierten Compilern unter
Umständen nicht mehr übersetzen.
Dadurch, dass für die neuen Typen in C99 explizit ein #include
<stdint.h> (und für die booleans ein #include <stdbool.h>) gemacht
werden muss, haben die neu definierten Typen keinen Einfluss auf ältere
Programme.
Und natürlich stehen die Typen in der Sprachdefinition, woher kämen wohl
sonst die Namen stdint.h und stdbool.h?
W.S. schrieb:> Es ist ein Manko, ein Geburtsfehler der Sprache C - und keiner hat es> bisher geschafft, damit mal wirklich aufzuräumen. Leider. Immer nur ein> Workaround auf den anderen.
Es gibt zwei wesentliche Kriterien bei der Erweiterung von
Programmiersprachen:
1. sauber und ästhetisch
2. abwärtskompatibel
Da diese Kriterien teilweise im Widerspruch zueinander stehen, wird
einem von beiden eine höhere Priorität eingeräumt. Ein Beispiel für (1)
ist Python, wo immer wieder einmal aufgeräumt wird, um die Sprache als
Ganzes möglichst rund und aus einem Guß dastehen zu haben. Bei C
hingegen steht ganz klar (2) im Vordergrund, weil von vielen Firmen
C-Code über Jahrzehnte hinweg wiederverwendet wird.
Wird (1) priorisiert, meckern die Leute, dass ihre alten Programme nicht
mehr laufen. Wird hingegen (2) priorisiert, meckern die Leute, dass die
Sprache ihre klinische Reinheit verliert. Wer nur in einem der beiden
Fälle meckert, wählt einfach eine andere Programmiersprache. Wer in
beiden Fällen meckert, der sollte mit dem Programmieren aufhören :)
> Wäre "ES" integriert, dann würde man die von dir genannte Headerdatei> überhaupt nicht benötigen, weil es die betreffenden Schlüsselwörter in> der Sprachdefinition gäbe.
Eine gute Integration von Datentypen bedeutet nicht, dass für sie
Schlüsselwörter reserviert werden. Ich empfinde es sogar eher als
unästhetisch, wenn Datentypen, Systemvariablen, Standardfunktionen u.ä.
als Schlüsselwörter definiert werden, denn für mich ist kein Grund
ersichtlich, warum der Name eines vordefinierten Datentyps ein
Schlüsselwort sein soll, der Name eines benutzerdefinierten Datentyps
aber nicht. Konsequenterweise ist in vielen modernen Programmiersprachen
kein einziger Datentyp als Schlüsselwort definiert¹.
> Aber es ist nicht integriert und man benötigt diese> compilerherstellerabhängige Headerdatei
Ja, und das Tolle an der Sache ist, dass man dieser Headerdatei auch
einfach weglassen kann, wenn sich die darin enthaltenen Typdeklarationen
mit bestehendem Programmcode beißen.
> Aber man sollte es beim Namen nennen. Mist ist Mist - und es ist eben> kein Gold bloß weil man es selber nicht ändern kann. Ich zumindest> versuche nicht, mir die Dinge schönzureden.
Aber noch viel größerer Mist (wenn auch anderer Art) wäre es, wenn eine
Softwarefirma bei jedem neuen C-Standard ihren kompletten Code-Bestand
anpassen müsste.
Ich verstehe auch nicht ganz, warum du immer wieder auf diesen stdint-
Typen herumreitest. Du kannst in den meisten Fällen auch ganz gut ohne
sie leben: Der C-Standard gibt für alle "normalen" Integertypen (also
char, short, int, long und long long, jeweils signed und unsigned) einen
minimalen Wertebereich an. Du wählst also einfach immer denjenigen Typ
aus, dessen garantierter Wertebereich für deine Berechnungen ausreichend
ist. Wie groß der (implementierungsabhängige) tatsächliche Wertebereich
ist, ist meist relativ uninteressant.
Mir fallen spontan nur zwei Fälle ein, wo die tatsächliche Bitgröße der
Integer-Typen von Bedeutung ist:
1. wenn beim Speicherverbrauch des Programms jedes einzelne Byte zählt
2. wenn man aus Effizienzgründen mit überlaufenden Variablenwerten als
Ersatz für die Modulo-2^n-Arithmetik herumtricksen möchte
Aber diese Fälle treten doch eher selten auf.
–––––––––––––
¹) Manchmal wird beim Unit-Typ eine Ausnahme gemacht, was der Ästhetik
auf Grund der Sonderrolle dieses Typs aber keinen Abbruch tut.
W.S. schrieb:> Aber mal zum Kern der Sache:> Es (s.o.) ist weder voll noch irgendwie in die Sprache integriert,> sondern völlig unintegriert als Hilfs-Headerdatei vorliegend.
Wir haben unterschiedliche Ansichten darüber, was zur Sprache gehört und
was "völlig unintegriert ist". Für mich ist alles, was in der
Sprachdefinition steht, Teil der Sprache. Das ist bei stdint.h und den
[u]int*-Typen der Fall, also gehören sie zur Sprache.
Bei C war es schon immer so, daß man versucht hat, den - nennen wir es
mal Sprachkern - möglichst klein zu halten und alles, was da nicht
unbedingt drin sein muß, in die Standardbibliothek zu stecken. So gibt's
z.B. auch die Möglichkeit zur Textausgabe (printf) nur in der Bibliothek
und nicht, wie bei manchen anderen Sprachen, als Schlüsselwort. Stört
dich auch, daß du dafür einen Header einbinden mußt und daß es "weder
voll noch irgendwie in die Sprache integriert" ist? Ist printf deshalb
kein Teil von C?
Du hast es immer noch nicht geschafft, auch nur einen einzigen Vorteil
zu nennen, diese [u]int*-Typen als Schlüselwort statt per Header zu
definieren.
Rolf Magnus schrieb:> Ist printf deshalb> kein Teil von C?
Richtig. 'printf' ist genauso wie alles was in separaten Headerdateien
steht eben kein Bestandteil der Sprache. Da zählt nur genau das, was als
Schlüsselwort definiert ist und nichts, was in irgendwelchen
Headerdateien steht. Das sind alles nur Workarounds, wenn es um
Datentypen geht. Auch die ganzen Funktionen der diversen
Standardbibliotheken gehören nicht zu den Bestandteilen der Sprache,
sondern sind lediglich "üblicherweise vorhandene Funktionen".
'printf' kann, muß aber nicht in den Libraries einer Toolchain enthalten
sein. Nur so als Beispiel.
Andreas B. schrieb:> Hätte man es direkt integriert (das wäre trivial gewesen), wäre der neue> Standard inkompatibel zum alten. Es gäbe neue Schlüsselwörter...
Ja. Eben, siehe "Int64" als Beispiel.
Es wäre ganz gewiß kein Beinbruch gewesen, 'int' als 16 Bit Zahl und
'long' als 32 Bit Zahl festzuschreiben. Das wäre überhaupt nicht
inkompatibel zum Bisherigen gewesen - und ich möchte hier nur mal dran
erinnern, daß der Schritt von K&R zu ANSI ein viel größerer Schnitt in
der Syntax gewesen ist.
Es geht - man muß bloß wollen. Und man hätte es wollen sollen. Das wäre
was wirklich Gutes gewesen.
Nochmal im Klartext: das Festschreiben eines 'int' auf exakt 16 Bit
und 'long' auf exakt 32 Bit wäre absolut KEINE Inkompatibilität zu
bisherigen Programmen gewesen - bis auf solche, bei denen die Autoren
sich ohnehin an nix gehalten haben.
Ich halte es auch nicht für verwerflich, neue Schlüsselwörter
einzuführen, wenn das mit der gebührenden Zurückhaltung erfolgt. Dazu
unser Mod:
Yalu X. schrieb:> Ich empfinde es sogar eher als> unästhetisch, wenn Datentypen, Systemvariablen, Standardfunktionen u.ä.> als Schlüsselwörter definiert werden
Genau DAS ist wieder mal die Übertreibung - hier mal deinerseits.
Systemvariablen und Standardfunktionen gehören nicht zu einer
Sprachdefinition und sollen deshalb eben nicht eigene Schlüsselworte
erhalten. Aber grundlegende Datentypen gehören zu den echten Grundlagen
einer jeden Programmiersprache und damit in den eigentlichen
Sprachumfang mit eigenem Schlüsselwort.
Und da gibt es in C eben nix anderes als char, int und long nebst ihren
unsigned Versionen. Alles Andere ist in C nicht definiert.
Warum in aller Welt wollen die hier versammelten Diskutierer das nicht
einsehen und einfach mal sagen "ja, so isses eben."
Yalu X. schrieb:> Ich verstehe auch nicht ganz, warum du immer wieder auf diesen stdint-> Typen herumreitest.
Um der Klarheit willen. Das isses. Klarheit.
Mit dem Gegebenen leben kann ich schon seit Jahren, ich hab in der
Praxis damit keine Probleme bis auf solche Fälle, wo ich mich mal wieder
durch unleserliche Fremdquellen durchackern muß. Aber ich akzeptiere
Krummheiten eben nicht derart, daß ich mich selber krumm mache, bloß um
sie als 'Geradheiten' ansehen zu können.
Klaro?
W.S.
W.S. schrieb:> daß der Schritt von K&R zu ANSI ein viel größerer Schnitt in> der Syntax gewesen ist.
Es war eine Erweiterung und eine klarere Spezifikation, aber kein
Schnitt. Alte Programme blieben übersetzbar, es sei denn sie hatten das
Pech und verwendeten die neuen Keywords - aber sowas ist trivial zu
fixen.
Einzig jene Programme, die bei der impliziten Typkonvertierung auf
Bewahrung der Vorzeicheneigenschaft (sign preservation) an Stelle des
Wertes (value preservation) gesetzt hatten, die bissen ins Gras. Denn
das findest du nicht so leicht im Code. Aber das war vorher schlicht
nicht festgelegt. Ich hatte im Compiler deshalb vorsorglich beides
ermöglicht.
> Nochmal im Klartext: das Festschreiben eines 'int' auf exakt 16 Bit> und 'long' auf exakt 32 Bit wäre absolut KEINE Inkompatibilität zu> bisherigen Programmen gewesen
Bereits K&R erwähnt Implementierungen mit 32-Bit int (IBM 370, Interdata
8/32) und 36-Bit short+int (Honeywell 6000).
Anno ANSI-C 1989 waren bereits viele C Compiler mit 32-Bit int im
Einsatz.
> - bis auf solche, bei denen die Autoren> sich ohnehin an nix gehalten haben.
Anno K&R gab es keine klare Definition des Wertebereichs von Datentypen,
also auch nichts wogegen man hätte verstossen können. Dafür aber gab es
viele Programme, die faktisch von sizeof(int) == sizeof(char *)
ausgingen. Schon weil das in vielen Fällen der einzige wachsweichen
Spezifikation entsprach, dass "int" der natürlichen Wortbreite
entspräche. Diese einzige Regel hätte int=16bit definitiv verletzt, das
kam also ohnehin nicht in Frage.
Natürlich kann man sich auf "zum Teufel mit denen, die nicht meiner
Ansicht sind" verlegen, aber weit kommt man mit diesem Ansatz aufgrund
der normativen Kraft des Faktischen nicht. Man hatte auf diesem Weg
einen Seitenzweig eröffnet.
Mit dem Ansatz "vor mir die Sintflut" hätte man besser dran getan, die
miserable Deklarationssystax von C gleich ganz über Bord zu werfen und
durch eine saubere zu ersetzen. Und ein paar andere Stellen gleich mit.
> Ich halte es auch nicht für verwerflich, neue Schlüsselwörter> einzuführen, wenn das mit der gebührenden Zurückhaltung erfolgt.
Es war ein fundamentaler syntaktischer Fehler von C, Datentypen
überhaupt erst zu Keywords zu machen.
> Systemvariablen und Standardfunktionen gehören nicht zu einer> Sprachdefinition
Bibliotheken können sehr wohl Bestandteil eines Standards sein. Anno K&R
wurde das de fakto so gesehen, weil das ganze Buch als Standardwerk
betrachtet wurde.
Ab ANSI-C waren bestimmte Includes und ihre Inhalte definitiv Teil der
Standards. Haarspalter könnten nun versucht sein, diesen Standard in 2
Substandards zu differenzieren, einen für Syntax und Semantik und einen
andere für eine mindestens erforderliche Bibliothek, aber das bringt
nicht viel ein.
W.S. schrieb:> Rolf Magnus schrieb:>> Ist printf deshalb kein Teil von C?>> Richtig. 'printf' ist genauso wie alles was in separaten Headerdateien> steht eben kein Bestandteil der Sprache.
Sehr eigenartige Sichtweise. Ich glaube nicht, daß du die mit irgendwem
hier teilst.
> Da zählt nur genau das, was als Schlüsselwort definiert ist und nichts,> was in irgendwelchen Headerdateien steht. Das sind alles nur Workarounds,> wenn es um Datentypen geht.
Ich weiß ehrlich gesagt nicht, was daran "Workaround" sein soll. Die
funktionieren prima und ohne Probleme.
Und es gibt keinerlei sinnvollen Grund, mit neuen Schlüsselwörtern um
sich zu werfen, nur um eine gut funktionierende Lösung zu ersetzen durch
etwas, das nicht besser ist.
> Auch die ganzen Funktionen der diversen Standardbibliotheken gehören> nicht zu den Bestandteilen der Sprache, sondern sind lediglich> "üblicherweise vorhandene Funktionen".>> 'printf' kann, muß aber nicht in den Libraries einer Toolchain enthalten> sein. Nur so als Beispiel.
C definiert zwei Arten von Implementationen, nämlich "freestanding" und
"hosted". Bei ersterer ist sehr vieles optional, bei letzerer nicht.
Dort sind auch printf() und die ganzen Typen aus stdint.h zwingend
vorgeschrieben und müssen bei einem konformen Compiler dabei sein.
Sind sie das nicht, ist es kein ISO-C-Compiler.
> Es wäre ganz gewiß kein Beinbruch gewesen, 'int' als 16 Bit Zahl und> 'long' als 32 Bit Zahl festzuschreiben. Das wäre überhaupt nicht> inkompatibel zum Bisherigen gewesen
Mal abgesehen von Plattformen, die z.B. gar keinen 16-Bit-Typen kennen.
Dort müßte man dann mit viel Aufwand einen solchen emulieren, was
schnarchlahme Progrmame zur Folge hätte. Und ja, solche Plattformen gibt
es.
> Systemvariablen und Standardfunktionen gehören nicht zu einer> Sprachdefinition und sollen deshalb eben nicht eigene Schlüsselworte> erhalten. Aber grundlegende Datentypen gehören zu den echten Grundlagen> einer jeden Programmiersprache und damit in den eigentlichen> Sprachumfang mit eigenem Schlüsselwort.
Die grundlegenden Datentypen sind ja auch da. Und für entsprechende
Anforderungen sind eben noch Alias-Namen mit dabei, die man optional
nutzen kann.
> Und da gibt es in C eben nix anderes als char, int und long nebst ihren> unsigned Versionen. Alles Andere ist in C nicht definiert.
Hast du die Sprachdefinition mal gelesen?
> Warum in aller Welt wollen die hier versammelten Diskutierer das nicht> einsehen und einfach mal sagen "ja, so isses eben."
Weil es Unsinn ist.
W.S. schrieb:> Du hast ne ziemlich seltsame Denkweise, die ich nicht teile.> Als Programmierer muß man sich auf die verwendeten Datentypen verlassen> können, was in C nicht der Fall ist.
Du besitzt eine interessante "Meinung" und fundiertes Halbwissen.
Respekt. Leider wird diese Meinung allerdings nicht von der C-Idee
getragen. C ist immer und wird immer eine möglichst hardwarenahe Sprache
sein.
Solltest Du den von dir propagierten Grad an Abstraktion und
Typensicherheit brauchen musst du ein anderes Sprachmittel wählen.
Vielleicht ist JAVA was für dich.
Der Standard gibt für seine Typen durchaus einen begrenzten Wertebreich
an. Wenn ich Variablen in diesen Wertebereich deklariere kann ich dem
Compiler die Wahl eines möglichst natürlichen Formats für die Maschine
lassen.
Für exakt definierte Typen braucht es allerdings plattformangepasste
Typendefinitionen, wie in den Strukturen für Kommunikationsprotokolle.
Das kannst du getrost deinem Compilerhersteller überlassen, denn der
liefert ja das stdint.h mit.
Und im Grunde kommen diese Strukturen eh nicht ohne deinen Einfluss auf
den Compiler aus (du musst mit #pragma meistens das Padding verhindern).
"Es wäre ganz gewiß kein Beinbruch gewesen, 'int' als 16 Bit Zahl und "
Int steht nunmal für "ein Maschinenwort ala Maison, egal wie gross das
ist solang da 16 Bit reinpassen", und das ist ein sinnvoller Typ, warum
sollte man das anders handhaben?
W.S. schrieb:> Rolf Magnus schrieb:>> Ist printf deshalb kein Teil von C?>> Richtig. 'printf' ist genauso wie alles was in separaten Headerdateien> steht eben kein Bestandteil der Sprache.
Zu einer Sprache gehört alles, was in der Spech-Spezifikation steht
> Da zählt nur genau das, was als Schlüsselwort definiert ist und> nichts, was in irgendwelchen Headerdateien steht. Das sind alles> nur Workarounds, wenn es um Datentypen geht. Auch die ganzen> Funktionen der diversen Standardbibliotheken gehören nicht zu den> Bestandteilen der Sprache, sondern sind lediglich "üblicherweise> vorhandene Funktionen".
Eine Sprache in Sprachkern und Bibliotheken aufzuspalten ist nun
wirklich kein Nachteil von C.
Das gleiche Konzept findet sich bei C++, Fortran, Java, Ada, Python und
zig anderen Sprachen, denn es ist überaus sinnvoll, einen kompakten und
überschaubaren Sprachken zu haben.
Da es dir um dein Hobby C-Bashing geht: C hat wahrlich genug Schwächen
und Kritikpunkte, so daß es wirklich nicht erforderlich ist,
Kritikpunkte an den Haaren herbei zu ziehen. Die Aufteilung in
Sprachkern und Standard-Bibliothek und -Header gehört nämlich nicht zu
den Schwächen, sondern zu den Stärken der Sprache.