Móin,
kann mir jemand sagen was die Unterschiede sind und wann man welche
szenarien verwendet:
1
//a: warum ist der Stern am Datentyp und wann macht man das so
2
voidmachwas(uint8*buffer){
3
}
4
5
//b:
6
voidmachwas(uint8*buffer){
7
}
8
9
//c:
10
voidmachwas(char*buffer){
11
}
12
13
//d:
14
voidmachwas(char*buffer){
15
}
16
17
// und warum gibt es so eine Schreibweise?
18
// ein array (container) für bytes? oder strings?
19
//e:
20
voidmachwas(uint8buffer[]){
21
}
22
23
//ein array (container) für reinen String?
24
//oder ist das eine Stringliste,
25
// mich irretiert einmal schreibt man einen reinen String ohne
26
// array und einmal mit, wie ist es denn richtig?
27
//f:
28
voidmachwas(charbuffer[]){
29
}
Ist es nicht so, dass unsigned char und uint8 das gleiche sind
was sind dann reine char datentypen, reichen die von 0 bis 255
oder nur 0 bis 127 oder hat das gar nichts damit zu tun.
Ich stolpere meistens wenn es um Bytes und Strings geht,
mein Base ist:
eine Zahl ist ein Integer
mehrer Zahlen sind eine Integer Folge
entweder in Reihe 'string' oder parallel 'array'
ein Buchstabe ist ein char
mehrere Buchstaben ein char Folge
entweder in Reihe 'string' oder parallel 'array'
Ein Buchstabe ist ein idx von einem string
Ein Byte kann alles sein, ein Buchstabe, eine Zahl, ein IDX.
Wo liegt mein Denkfehler, wie kann ich das wieder gerade biegen, sodass
ich weiss, wann ich char und wann int / uint.. nehmen muss.
Danke!
> Ist es nicht so, dass unsigned char und uint8 das gleiche sind> was sind dann reine char datentypen, reichen die von 0 bis 255> oder nur 0 bis 127 oder hat das gar nichts damit zu tun.
Es ist nicht definiert ob char mit oder ohne Vorzeichen ist. Somit kann
es von 0 ... 255 ODER von -128 ... 127 gehen. Wie kommst du auf 0 ...
127? Ein char ist immer 8Bit groß.
> eine Zahl ist ein Integer
Nein, es gibt auch Fließkommazahlen (float, double)
> mehrer Zahlen sind eine Integer Folge
Missverständlich... mehrere Zahlen können auch eine lange Zahl oder die
Repräsentation eines "Strings" (was es so in C nicht gibt) als
Ascii-Code sein.
> Wo liegt mein Denkfehler, wie kann ich das wieder gerade biegen, sodass> ich weiss, wann ich char und wann int / uint.. nehmen muss.
Im Zweifel musst du entscheiden welche Werte deine Variable annehmen
kann. Generell ist ein char auch nur eine Zahl (nähmlich der
Ascii-Code). int-Typen sind in der Regel größer als char.
Welche Lernmaterialien nutzt du?
Gruß
Dennis
Dennis S. schrieb:> Es ist nicht definiert ob char mit oder ohne Vorzeichen ist.
Beim Atmel Studio kann man einstellen, ob char signed oder unsigned sein
soll...
Ingo Less schrieb:> Dennis S. schrieb:>> Es ist nicht definiert ob char mit oder ohne Vorzeichen ist.> Beim Atmel Studio kann man einstellen, ob char signed oder unsigned sein> soll...
Das kann man bei jedem vernünftigen Compiler einstellen (z.B. gcc mit
-funsigned-char). Trotzdem ist es schlechter Stil char anstatt unsigned
char oder besser uint8_t zu benutzen.
Varianten a, b und e sind exakt gleichwertig. Genauso c, d und f. Bei
dem Stern ist es Geschmackssache ob man es an den Typ oder den
Variablennamen schreibt. Und bei der Schreibweise mit [] könnte man auch
erst meinen es wäre was anderes als als Funktionsparameter ist das
identisch mit der Pointer Versionen. Bleibt also noch der Unterschied
zwischen char und uint8_t. Dabei ist char der Typ den man für Strings
benutzt weil char für Zeichen gedacht ist. uint8_t ist ein
vorzeichenlose Zahl im Bereich 0 bis 255. Kann man nehmen wenn man mit
keinen Zahlen arbeitet. Wird teilweise auch für binäre Daten benutzt
(wieder Geschmackssache).
Da gebe ich dir recht. Ich verwende auch immer uint8_t und co. weil ich
auf 8 und 32 Bittern arbeite und man sich gern bei unsigned/signed int n
Fehler ins Haus holt.
Ömm... in C ist ein string de facto ein character array. Da gibt's nix
mit Reihe oder "parallel".
Wenn Du da hast
1
char meinname[] = "ducttape";
ist das nichts anderes als ein Array vom Typ char (also eine vorgegebene
Anzahl an hintereinander liegenden (weil Array) ein Byte großen (weil
char) Elementen. Dass die zufällig einen String ergeben den Du lesen
kannst interessiert C dabei eher wenig. Für C ist das auch nix anderes
als 8 Zahlen hintereinander. Die halt zufällig, wenn man durch sie den
ASCII-Table interpretiert, einen String ergeben.
Anders gesagt, Du kannst auch folgendes machen:
1
char meinname[9]; //Wenn man nicht initialisiert, muss man C sagen wie groß.
2
meinname[0] = 100;
3
meinname[1] = 117;
4
meinname[2] = 99;
5
meinname[3] = 116;
6
meinname[4] = 116;
7
meinname[5] = 97;
8
meinname[6] = 112;
9
meinname[7] = 101;
10
meinname[8] = 0;
11
printf ("%s", &meinname);
Deswegen muss man C auch sagen welchen Typ eine Variable hat. Für C ist
alles eigentlich nur 'ne Zahl. Auch wenn wir darin sonderbare Zeichen
lesen wollen.
Übrigens: Das Array muss 9 Zeichen groß sein auch wenn nur 8 rein
sollen, weil ein "string" in C ein extra Zeichen braucht für die
berühmte terminierende 0. Die so nebenbei öfter als einem lieb ist für
lustige Speicherüberläufe und sonstige Laufzeigfehler sorgt,
insbesondere wenn sie nicht da ist.
Zum Thema Stern: Ob Du schreibst
1
uint* variable;
oder
1
uint *variable;
ist egal. Beides gleich gültig und am ehesten davon abhängig an welche
Coding convention Du Dich zu halten hast weil Dein Chef das so will. :)
TriHexagon schrieb:> Trotzdem ist es schlechter Stil char anstatt unsigned> char oder besser uint8_t zu benutzen.
Von welchem Fall redest du jetzt? Für Funktionen die mit Strings/Zeichen
arbeiten ist char genau richtig.
Dennis S. schrieb:>> Ist es nicht so, dass unsigned char und uint8 das gleiche sind>> was sind dann reine char datentypen, reichen die von 0 bis 255>> oder nur 0 bis 127 oder hat das gar nichts damit zu tun.> Es ist nicht definiert ob char mit oder ohne Vorzeichen ist. Somit kann> es von 0 ... 255 ODER von -128 ... 127 gehen. Wie kommst du auf 0 ...> 127? Ein char ist immer 8Bit groß.
Ok, ich hab mir das ausgedacht, da es unsigned char und char gibt,
dachte ich mir dass unsigned dann 0..255 sein können und char eben der
reine Tastaursatz 0..127 oder so .
>>> eine Zahl ist ein Integer> Nein, es gibt auch Fließkommazahlen (float, double)
ok..
>>> mehrer Zahlen sind eine Integer Folge> Missverständlich... mehrere Zahlen können auch eine lange Zahl oder die> Repräsentation eines "Strings" (was es so in C nicht gibt) als> Ascii-Code sein.
ok . sorry für meine unverständliche Formulierung, int, double, long ..
etc
ok kurze und lange Zahlen, mit denen man rechnet.
Was ist eigentliche eine Datei, ein Datenbuffer?
Ein Array von Bytes?
Ein Array von Chars's?
EIn Array von Integer?
Wobei ja die Fragen nicht richtig sind, ein Byte ist ein Integer 0..255
bzw das Zeichen.
Wenn der Buffer/Datei 256 Bytes lang ist, können das ja nur ein array of
bytes sein ?
>>> Wo liegt mein Denkfehler, wie kann ich das wieder gerade biegen, sodass>> ich weiss, wann ich char und wann int / uint.. nehmen muss.> Im Zweifel musst du entscheiden welche Werte deine Variable annehmen> kann. Generell ist ein char auch nur eine Zahl (nähmlich der> Ascii-Code). int-Typen sind in der Regel größer als char.>
Ich hatte irgenwo mal gelesen, dass bei char der compiler \0 als
Schnittmakierung verwendet, und man eine Stelle mehr einpanen muss,
daher hab ich charimmer der 'String' Verarbeitung zugeordnet:
letztendlich will ich byte's verarbeiten 0..255 ob das eine Zahl ist,
oder ein Hexwert oder Bin Wert ist mir erstmal egal, aber jedesmal wenn
ich anfange, stolpere ich über die Wahl, wenn ich einen MessageText
daraus ziehen will..:
was wäre richtiger?
1
2
3
charbuffer[8]="abcdefg\0";
4
..
5
uint8_tbuffer[8]="abcdefgh";
Und das Sternchen gibt mir dann den Rest,einmal rechts dran beim
Datentyp und einmal links dran an der Variablen, und manchmal steht es
einfach nur so da:
1
voidmachwas('typedefstructure:datensatz'*);
> Welche Lernmaterialien nutzt du?
'Karl Heinz'
'Karl Heinz'
'Karl Heinz'
mikrocontroller.net ´
J. Wolf, beide C und C++ Bücher, kleines Handbuch und dickes.
>> Gruß> Dennis
Danke!
Sebastian V. O. schrieb:> TriHexagon schrieb:>> Trotzdem ist es schlechter Stil char anstatt unsigned>> char oder besser uint8_t zu benutzen.>> Von welchem Fall redest du jetzt? Für Funktionen die mit Strings/Zeichen> arbeiten ist char genau richtig.
Bei Strings und Zeichen char, ansonsten uint8_t. Da hast du allerdings
recht das hätte ich unterscheiden müssen.
Heinz L. schrieb:> Zum Thema Stern: Ob Du schreibst uint* variable;> oder uint *variable;> ist egal. Beides gleich gültig und am ehesten davon abhängig an welche> Coding convention Du Dich zu halten hast weil Dein Chef das so will. :)
Ich habe damals gelernt, dass man typischerweise uint *variable;
schreiben sollte. Warum?
Übersichtlicher, wenn man diesen Fall betrachtet:
uint8_t* var1, var2; (var1 pointer, var2 nicht aber schlecht zu lesen)
bzw.
uint8_t *var1, var2; (var1 pointer, var2 nicht, eindeutiger)
c schüler schrieb:> Ok, ich hab mir das ausgedacht, da es unsigned char und char gibt,
Es gibt auch signed char :-)
am besten gehst du so vor
char für alles was mit Texten zu tun hat. Also
Stringverarbeitung
signed char kleiner Integer, mit Vorzeichen
unsigned char kleiner Integer, aber ohne Vorzeichen
insbesondere letzteres (unsigned char) ist der Datentyp der Wahl, wenn
man es mit Bytes zu tun hat.
> Was ist eigentliche eine Datei
Den Datentyp 'Datei' gibt es in C nicht.
> ein Datenbuffer?> Ein Array von Bytes?> Ein Array von Chars's?> EIn Array von Integer?>
Das kommt drauf an, als was du es ansehen willst.
Grundsätzlich ist der einfachste Datentyp immer das Byte.
Aber es kann natürlich sein, dass eine Abfolge von Bytes eine spezielle
Bedeutung hat. Wie zum Beispiel dann, wenn die Bytes als Inhalt ASCII
Codes haben und nach dem letzten Nutzbyte ein Byte mit dem Wert 0 kommt.
Dann wird aus der allgemeinen Bytefolge ein String.
c schüler schrieb:> Ok, ich hab mir das ausgedacht, da es unsigned char und char gibt,> dachte ich mir dass unsigned dann 0..255 sein können und char eben der> reine Tastaursatz 0..127 oder so .
Für alle anderen Ganzzahligen Typen hättest du recht gehabt. Also wenn
du nur short, int, long oder sowas schreibst ist es immer signed also
mit Vorzeichen. Wenn man unsigned will muss man das dazu schreiben. Bei
char wird es aber nicht festgelegt obt es signed oder unsigned ist. Wenn
man explizit Zahlen zwischen -128 bis 127 speichern will nimmt man
signed char oder besser int8_t. Für unsigned eben unsigned char oder
auch wieder besser uint8_t. Den Typ char nimmt man ausschließlich für
Zeichen oder Strings (Array von Zeichen).
Guest schrieb:> Ich habe damals gelernt, dass man typischerweise uint *variable;> schreiben sollte. Warum?
Das ist auf eine Idee der Designer von C zurückzuführen, die die
Definition einer Variablen wie ihre Verwendung gestalten wollten
in
1
int*a,b;
ist a eine Pointer Variable, b jedoch nicht.
D.h. an dieser Stelle bindet sich der * an den Variablennamen und nicht
an den Namen des Datentyps.
Wenn b auch ein Pointer sein soll, dann schreibt sich das als
1
int*a,*b;
(Und ob du da jetzt Leerzeichen machst oder nicht, ist dem C Compiler
wurscht)
mit
1
int*a,b;
ist das jetzt leicht fehlzulesen, weil uns die Nähe des * zum Dtaentyp
suggeriert, dass er dort dazugehört. Tut er aber nicht.
TriHexagon schrieb:> Sebastian V. O. schrieb:>> TriHexagon schrieb:>>> Trotzdem ist es schlechter Stil char anstatt unsigned>>> char oder besser uint8_t zu benutzen.>>>> Von welchem Fall redest du jetzt? Für Funktionen die mit Strings/Zeichen>> arbeiten ist char genau richtig.>> Bei Strings und Zeichen char, ansonsten uint8_t. Da hast du allerdings> recht das hätte ich unterscheiden müssen.
Dann geht es nicht nur mir so.
Es gibt also doch 'Missverständnise' untereinander.
@all
Danke für die vielen Antworten, ich war zu schnell bei Denis zu
antworten; viele Sachen sind schon beantwortet, danke danke! Jetzt kann
ich wieder weiter kauen.
Es ist sehr schwer wenn man von einer anderen Programiersprache kommt,
wo es zu viele Datentypen gibt, die aber genau genommen auch nur ein
Byte sind; mir persönlich sind uint8_t am liebsten, egal ob
Stringverarbeitung oder Rechnen. Bei char zucke ich zusammen und mein
Gehirn fängt an zu rattern, was ich jetzt falsch machen könnte.
Sternchen ist jetzt klarer.
Danke nochmals an alle hier, dass ihr euch so eine Mühe gemacht habt,
und dazu noch bei dem Wetter.
Karl Heinz schrieb:> Guest schrieb:>>> Ich habe damals gelernt, dass man typischerweise uint *variable;>> schreiben sollte. Warum?>>> Das ist auf eine Idee der Designer von C zurückzuführen, die die> Definition einer Variablen wie ihre Verwendung gestalten wollten>> in>
1
>
2
>int*a,b;
3
>
>> ist a eine Pointer Variable, b jedoch nicht.> D.h. an dieser Stelle bindet sich der * an den Variablennamen und nicht> an den Namen des Datentyps.>> Wenn b auch ein Pointer sein soll, dann schreibt sich das als>
1
>int*a,*b;
2
>
>> (Und ob du da jetzt Leerzeichen machst oder nicht, ist dem C Compiler> wurscht)>> mit>
1
>int*a,b;
2
>
> ist das jetzt leicht fehlzulesen, weil uns die Nähe des * zum Dtaentyp> suggeriert, dass er dort dazugehört. Tut er aber nicht.
Da ist es wieder ;-)
Das Sternchen einmal da udn einmal hier ;-)
ALso reiner Schreibstil Karl Heinz?
Wenn ich also schreibe
c schüler schrieb:> mir persönlich sind uint8_t am liebsten, egal ob> Stringverarbeitung oder Rechnen
Wenn du für alles uint8_t nimmst funktioniert sowas aber nicht:
1
uint8_tx[]="test";
2
printf(x);
Zumindest nicht ohne extra Cast.
c schüler schrieb:> Wenn ich also schreibe>> int* a,b,c,d,e,f;>> // ist das gleiche wie>> int *a,*b,*c,*d,*e,*f;
Nein das ist nicht das gleiche! Dem Compiler ist es egal wo man die
Leerzeichen setzt. Aber für den Leser der sich nicht so genau mit den
Regeln auskennt könnte meinen bei int* a,b,c,d,e,f; wären alles Pointer.
Tatsächlich ist hier nur a ein Pointer.
c schüler schrieb:> Ich hatte irgenwo mal gelesen, dass bei char der compiler \0 als> Schnittmakierung verwendet, und man eine Stelle mehr einpanen muss,> daher hab ich charimmer der 'String' Verarbeitung zugeordnet:
Das ist auch ok so.
> letztendlich will ich byte's verarbeiten 0..255 ob das eine Zahl ist,> oder ein Hexwert oder Bin Wert ist mir erstmal egal
Pst. In einem Computer ist alles ausnahmslos eine Zahl.
Du als Programmierer entscheidest, was da daraus machst.
>, aber jedesmal wenn> ich anfange, stolpere ich über die Wahl, wenn ich einen MessageText> daraus ziehen will..:
Text ... dann ist die Sache klar. Text ist char
> was wäre richtiger?>
1
>
2
>charbuffer[8]="abcdefg\0";
3
>
Grunsätzlich fast richtig. Aber wozu das \0 Zeichen da am Ende?
Wenn du ein Stringliteral schreibst, also etwas in " Anführungszeichen,
dann hängt der Compiler IMMER das abschliessende \0 Zeichen automatisch
mit drann! D.h der String "abcdefg\0" hat die Länge 9(!) und nicht 8. Es
ist schön (und nicht verboten), wenn du selbst da ein \0 in den String
reinschreibst. Das hindert den Compiler aber nicht daran, selbst dann
auch noch eines hinten anzuhängen, so wie es die Regel fordert, dass er
das bei einem Stringliteral tun muss.
> uint8_t buffer[8] = "abcdefgh";
UNd auch dieses Array ist zu klein.
Zähl die Zeichen. VOn a bis zum h sind es 8 sichtbare Zeichen. Dann noch
das obligatorische \0 vom Compiler hinten drann und du hast 9 Zeichen.
OK. Der Fairness halber sei gesagt, dass es hier noch eine
Ausnahmeregelung gibt. Nämlich dann, wenn dein Stringliteral ohne das
abschliessende \0 Zeichen exakt in das angegebene Array passt, dann wird
das abschliessende \0 auch ausgelassen. Aber das ist eine Ausnahme.
Ansonsten ist das obligatorische \0 vom Compiler immer mit dabei.
Aber:
Wozu die Zeichen zählen? Überlass das doch dem Compiler!
1
charbuffer[]="abcdefgh";
Der Compiler sieht sich die Initialisierung an, bestimmt daraus wie
gross das Array sein muss und dann macht er es genau so gross.
Wenn die Idee an dieser Stelle war, ein Array zu machen, das als
Übersetzungstabelle fungiert, dann könnte man das auch genau so als
Übersetzungstabelle schreiben.
1
charbuffer[]={'a','b','c','d','e','f','g','h'};
jetzt ist alles klar: Es ist klar, dass dieses Array nicht als String
aufgefasst werden soll, denn sonst hättest du es ja mit einem
Stringliteral initialisiert. Das hier, und das ist für einen
Programmierer ablesbar, ist eindeutig einfach nur als Array von
Charactern gedacht. Mit irgendeinem Index geht man in das Array rein und
aus dem Array kriegt man 1 Character raus. Mehr wollte der
Originalprogrammierer nicht und mehr steckt auch nicht in diesem Code.
Man kann viele Dinge auf viele verschiedene Art und Weisen schreiben.
Manchmal bewirkt es sogar bis runter zur Bitebene genau das gleiche. Der
Unterschied besteht nur darin, was die unterschiedlichen Schreibweisen
einem menschlichem Programmierer vermitteln - was sie ihm erzählen was
wohl die Absicht des Schreibers war.
Sebastian V. O. schrieb:> c schüler schrieb:>> mir persönlich sind uint8_t am liebsten, egal ob>> Stringverarbeitung oder Rechnen>> Wenn du für alles uint8_t nimmst funktioniert sowas aber nicht:>
1
uint8_tx[]="test";
2
>printf(x);
> Zumindest nicht ohne extra Cast.
EXACT!
Sebastian du bringst es auf den Punkt,
genau an dieser Stelle habe ich dann meine Hänger,
und fange den angefangenen schönen code zu verwurschteln an.
Ich bin dann mehr Zeit damit beschäftigt, zu verstehen, warum mir der
compiler den fehlenden TYpe Cast meldet, dann häng ich einen aus
verschiedenen casten kopierten Code ein, einmal mit einem Stern am
Datentyp oder andersmal mit einem am Variablennamen damit es weitergeht.
Das bringt mich dann in rage und fange an die Bücher auf und ab zu
wälzen um das zu verstehen, was ich falsch mache.
>> c schüler schrieb:>> Wenn ich also schreibe>>>> int* a,b,c,d,e,f;>>>> // ist das gleiche wie>>>> int *a,*b,*c,*d,*e,*f;>> Nein das ist nicht das gleiche!
okay, jetzt ist das Sternchen in dem Fall klar, danke!
c schüler schrieb:> Móin,>> kann mir jemand sagen was die Unterschiede sind und wann man welche> szenarien verwendet:>> //a: warum ist der Stern am Datentyp und wann macht man das so> void machwas (uint8* buffer) {> }
Der Standard-Datentyp heißt uint8_t, nicht uint8.
Zur Platzierung des *: Persönliche Vorliebe des Programmierers, macht an
dieser Stelle aber keinen Unterschied. Die Schreibweise wird von manchen
Lehrbüchern als "schöner" bezeichnet, hat aber in anderem Zusammenhang
den Nachteil etwas Verwirrung zu stiften:
1
uint8_t*p,q;
Sind mitnichten zwei Pointer p und q, sondern ein Pointer p, und eine
uint8_t variable q. Bei sauberer Schreibweise ist das etwas
offensichtlicher:
1
uint8_t*p,q;
> // und warum gibt es so eine Schreibweise?> // ein array (container) für bytes? oder strings?> //e:> void machwas (uint8 buffer[] ) {> }
Syntaktischer Zucker. Ob eine Funktion als
1
voidmachwas(uint8_tbuffer[]){}
oder
1
voidmachwas(uint8_t*buffer){}
definiert wird macht absolut keinen Unterschied.
Bei beiden Versionen erwartet die Funktion einen Pointer!
Es ist so, dass in C an ein paar Stellen der Namen eines Arrays
automatisch in einen Pointer auf das Array umgewandelt wird. Der Aufruf
einer Funktion ist so eine Stelle. Man kann eine Funktion nicht mit
einem Array aufrufen, auch wenn es so aussieht. Es ist in Wahrheit immer
ein Pointer der in der Funktion ankommt.
Die Variante
1
voidmachwas(uint8_tbuffer[]){}
verbirgt das nur, ändert aber nichts daran, dass buffer nicht als Array,
sondern eben als Pointer auf ein Array übergeben wird. Vielleicht hätten
die Erfinder von C diese alternative Schreibweise weglassen sollen.
>> //ein array (container) für reinen String?> //oder ist das eine Stringliste,> // mich irretiert einmal schreibt man einen reinen String ohne> // array und einmal mit, wie ist es denn richtig?
Ein char-Array ist Speicherplatz (typischerweise für einen String,
besonders in älteren Programmen auch oft Speicherplatz für Daten, als es
noch kein uint8_t gab). Ein Pointer ist ein Verweis auf einen
Speicherplatz.
Das ist so etwa der Unterschied zwischen "ein Schüler sein" und der
Nachbar zeigt auf dich und sagt "da hihten, dass ist ein Schüler".
In C ist die Bezeichnung String eine Bezeichnung für zwei Dinge:
1) Für ein char-Array das die nummerischen Codes der einzelnen
Buchstaben eines Strings enthält, abgeschlossen durch ein zusätzliche
0-Byte (ASCII-Code NUL) im Array.
2) Ein String-Literal, also
1
"dies ist ein String"
Eigentlich ist die Bezeichnung eines String-Literals, als String nicht
ganz richtig, aber üblich.
Im Folgenden geht es ziemlich heftig durcheinander:
> Ich stolpere meistens wenn es um Bytes und Strings geht,> mein Base ist:>> eine Zahl ist ein Integer
Es gibt auch andere Zahlen.
> mehrer Zahlen sind eine Integer Folge
Mehrere Zahlen sind erst einmal nur mehrere Zahlen. Mehrere Zahlen in
ein Array geschrieben, sind Inhalt des Arrays.
> entweder in Reihe 'string' oder parallel 'array'
Nein, siehe oben. In C ist ein String ein mit Buchstabencodes
initialisiertes Array (oder ein String-Literal).
> ein Buchstabe ist ein char
Die in C übliche Art und Weise den nummerischen Code eines Buchstabens
zu speichern ist der char-Datentyp. Vereinfacht nennt man ein char
manchmal einen Buchstaben oder einen Buchstaben ein char.
> mehrere Buchstaben ein char Folge
Mehrere Buchstaben sind erst einmal mehrere Buchstaben. Speichert man
die numerischen Codes der jeweiligen Buchstaben in ein char-Array, und
speichert man hinter den Buchstabencodes im Array noch den ASCII-Code
NUL, hat man einen String (in einem char-Array).
> entweder in Reihe 'string' oder parallel 'array'
Quatsch.
> Ein Buchstabe ist ein idx von einem string
Nein ein Index ist ein Verweis auf eine Stelle in einem Array. An der
Position des Index im Array befinden sich Daten. Bei einem char-Array
befindet sich an der durch den Index bezeichneten Position zum Beispiel
der numerische Code eines Buchstabens.
> Ein Byte kann alles sein, ein Buchstabe, eine Zahl, ein IDX.
Ein Byte ist eine Informationseinheit, typischerweise auf modernen
Systemen aus 8 Bit zusammengesetzt (ja, es gab auch Maschinen mit andere
Byte-Längen). Ein Byte kann in C in jedem Datentyp gespeichert werden,
der mindestens die Anzahl Bits eines Bytes speichern kann. Früher war
das klassischerweise der Datentyp char. Heute verwendet man für die
üblichen 8 Bit Bytes lieber uint8_t.
Die Bedeutung eines Bytes hängt von seiner Interpretation ab. Ich kann
ein Byte als Zahl, als nummerischen Code eines Buchstabens, oder was
auch immer interpretieren. Heute handhabt man das so, dass wenn man ein
Byte in einem char-Datentyp abspeichert man die Interpretation als
Zahlencode eines Buchstabens möchte. Speichert man ein Byte in einem
uint8_t Datentyp ab sagt man erst mal gar nichts darüber wie man das
Byte interpretieren will, allerdings erlaubt ein uint8_t eine einfache
Interpretation eines Bytes als Zahl zwischen 0 ... 255.
c schüler schrieb:>> Wenn du für alles uint8_t nimmst funktioniert sowas aber nicht:>>
1
uint8_tx[]="test";
2
>>printf(x);
>> Zumindest nicht ohne extra Cast.>> EXACT!> Sebastian du bringst es auf den Punkt,> genau an dieser Stelle habe ich dann meine Hänger,
wo hast du da einen Hänger?
Dieses x hier ist eindeutig dazu gedacht einen Text aufzunehmen. Die
Intialisierung zeigt das mehr als deutlich an, indem dort ein
Stringliteral steht.
Also ist der dafür zuständige Datentyp ein plain vanilla char.
Stoooooop!
Momenterle bitte! Bitte nicht einfach Sterne irgendwo dranpappen und
weglassen damit irgendwie halt der Code compiliert! Da kommt sonst sehr
schnell GANZ was anderes raus als man will!
Wenn zwei Typen nicht implizit gecastet werden können kann man auch
explizit casten. Aber einfach Sternchen hinmalen oder weglassen geht
SICHER schief!
>> Grunsätzlich fast richtig. Aber wozu das \0 Zeichen da am Ende?
okay, verstanden, hatte das mal gelesen, dass man bei String ein \0
ranhängen sollte, damit, wenn der buffer[8] mit "ab" belegt wird,
nicht weitere zeichen aus speicheradressen wiedergibt.
> Wenn du ein Stringliteral schreibst, also etwas in " Anführungszeichen,> dann hängt der Compiler IMMER das abschliessende \0 Zeichen automatisch> mit drann!
das hab ich so noch nicht gewusst, danke Karl Heinz.
ok.
> reinschreibst. Das hindert den Compiler aber nicht daran, selbst dann> auch noch eines hinten anzuhängen, so wie es die Regel fordert, dass er> das bei einem Stringliteral tun muss.
Karl Heinz, kannst du mir bitte dieses Regelwerk posten, es wäre schön,
wenn man so ein Regelwerk am Tisch hat, den \0 Schnittmarke hatte ich
auch nur bedingt in Erfahrung gebracht, als ich einen String ausgewertet
habe, und mich wunderte, warum 0x00 oder \0 nicht vorkam, obwohl ich es
explizit versand hatte ( UART ) .
>>>> uint8_t buffer[8] = "abcdefgh";>> UNd auch dieses Array ist zu klein.
jetzt rattert es wieder ;-)
man sollte das \0 also generell einplanen mit einem mehrzeichen?
> Zähl die Zeichen. VOn a bis zum h sind es 8 sichtbare Zeichen. Dann noch> das obligatorische \0 vom Compiler hinten drann und du hast 9 Zeichen.
bekomme ich das zu sehen, wenn ich neun bytes lese?
>> OK. Der Fairness halber sei gesagt, dass es hier noch eine> Ausnahmeregelung gibt.
solche Sachen werden in den Standards beschrieben, richtig?
wie kann man sich da auf dem Laufenden halten?
> Aber:> Wozu die Zeichen zählen? Überlass das doch dem Compiler!>
1
>charbuffer[]="abcdefgh";
2
>
> Der Compiler sieht sich die Initialisierung an, bestimmt daraus wie> gross das Array sein muss und dann macht er es genau so gross.
was macht das Programm dann wenn es mal ein grösserer String wird,
kümmert es sich selber um den Speicherbedarf onThefly?
>> Wenn die Idee an dieser Stelle war, ein Array zu machen, das als> Übersetzungstabelle fungiert, dann könnte man das auch genau so als> Übersetzungstabelle schreiben.>
1
>charbuffer[]={'a','b','c','d','e','f','g','h'};
2
>
EXACT!
genau diese Stellen haben ich dann, und springe dann
1
chararray[0]=string[0];
2
chararray[1]=string[1];
3
chararray[2]=string[2];
4
..
weil ich versucht habe, ein array einfach einem anderen array zuzuweisen
1
chararray=stringarray;
es aber nicht geht.
>> jetzt ist alles klar: Es ist klar, dass dieses Array nicht als String> aufgefasst werden soll, denn sonst hättest du es ja mit einem> Stringliteral initialisiert. Das hier, und das ist für einen> Programmierer ablesbar, ist eindeutig einfach nur als Array von> Charactern gedacht. Mit irgendeinem Index geht man in das Array rein und> aus dem Array kriegt man 1 Character raus. Mehr wollte der> Originalprogrammierer nicht und mehr steckt auch nicht in diesem Code.
exact, genau so mach ich das bisher.
und Ecke dann wenn ich einen Text daraus ziehen will.
Ich bekomme das schon irgendwie dann hin, aber ich muss
nachschlagen, nachlesen, es will einfach nicht als 'logik'
in meinen Kopf ;-)
danke!!
Heinz L. schrieb:> Stoooooop!>> Momenterle bitte! Bitte nicht einfach Sterne irgendwo dranpappen und> weglassen damit irgendwie halt der Code compiliert! Da kommt sonst sehr> schnell GANZ was anderes raus als man will!>> Wenn zwei Typen nicht implizit gecastet werden können kann man auch> explizit casten. Aber einfach Sternchen hinmalen oder weglassen geht> SICHER schief!
Nach 3-4 Stunden lesen und recherchieren, was die Compiler Meldung
mitteilen will, probiere ich es just in Time aus ;-);
am nächsten Tag ist das dann klar wie Sternenhimmel;
am übernächsten Tag gleiche Art, andere Namen, und es geht von vorne
los.
Ich will das verstehen, warum weshalb wieso, c ist super , wenn ich c
von Anfang an verwendet hätte, wäre einiges einfacher; vieles läuft
jetzt dank der Hilfen immer hier, und lesen in Büchern, aber manche
Sachen bringen mir Kopfzerbrechen warum das jetzt so ist wie es ist und
sein muss.
Hernach ist es dann wieder klar.
..
Danke euch für Eure Mühen, '1000 thanks button drück'
danke Jay und Karl Heinz, viel Stoff - werde mich einlesen!
Viele Sachen wieder dabei, die mich weiterbringen!
c schüler schrieb:> solche Sachen werden in den Standards beschrieben, richtig?> wie kann man sich da auf dem Laufenden halten?
Ja es gibt einen C-Standard, den man auch im Internet findet. Zum lernen
ist das allerdings nichts. Das ist eher was als Nachschlagwert für
Experten und Compilerentwickler. Sinnvoller wäre eher ein gutes Buch.
Dort werden solche Grundlagen wie der richtige Umgang mit Strings
erklärt.
c schüler schrieb:>> char buffer[] = "abcdefgh";>> > Der Compiler sieht sich die Initialisierung an, bestimmt daraus wie>> gross das Array sein muss und dann macht er es genau so gross.>> was macht das Programm dann wenn es mal ein grösserer String wird,> kümmert es sich selber um den Speicherbedarf onThefly?
Der String kann nicht vergrößert werden. Der Compiler reserviert einfach
soviel Platz wie es für die Zeichen in den Anführungszeichen benötigt +
ein extra Zeichen für die abschließende \0. Wenn man nachträglich noch
etwas anhängen möchte braucht man ein neues Array mit mehr Platz.
c schüler schrieb:> man sollte das \0 also generell einplanen mit einem mehrzeichen?
Wenn es um Text geht ja. Die vielen Funktionen welche mit Strings
arbeiten gehen davon aus, dass der String bei einem \0 Zeichen zuende
ist. Dies hat den Vorteil, dass man sich nicht speichern muss wie lang
der Text ist sondern man gibt einfach so lange Zeichen aus bis ein \0
kommt. Natürlich könnte es noch weiter gehen aber das kann die Funktion
ja nicht wissen. Dafür gibt es auch Funktionen aber denen muss man dann
die Größe mitgeben.
Zum Thema der \0: Grundsätzlich hast Du recht. Wenn Du Strings "zu Fuß"
zuweist, also jeden Buchstaben einzeln (siehe auch mein Beispiel weiter
oben), dann musst Du das machen. Wenn Du einem Character Array einen
String "direkt" zuweist, also mit sowas wie
1
char mystring[]="whatever";
Macht das der Compiler für Dich, weil ihm klar ist dass Du da mit einem
String hantierst. Das Array muss allerdings in jedem Fall groß genug
sein dass die 0 reinpasst. Sprich, wenn Du 8 Zeichen im String hast,
muss das Array Platz für 9, also Deine 8 und die 0 von C, haben.
Zum Thema "welche Zahlen ergeben einen String", schau Dir mal einen
ASCII-Table an. Tante Google hat da einige zur Auswahl. Wenn Du Dir den
anschaust wirst Du feststellen, dass im ASCII-Table jede Zahl zwischen 0
und 127 etwas bedeutet. Die ersten 32 sind Steuerzeichen mit
hauptsächlich historischer Bedeutung. Das sollte das Mysterium warum aus
einem character array ein String wird etwas erhellen.
Zum Einlesen: Du bekommst grundsätzlich immer das Zeichen das Du
einliest. Wenn Du im obenstehenden String "whatever" das 9. Zeichen
einliest (also Zeichen mit Index 8, remember, C zählt von 0 weg!) dann
bekommst Du als Antwort 0. Weil das 9. Zeichen ist die Trenn-0 von C.
Zur Größe von Arrays: Nein, Arrays wachsen nicht automatisch. Wenn Du
versuchst einem Char Array der Größe 8 den String "vielzugross"
zuzuweisen gibt's einen Laufzeitfehler mit "index out of bounds".
Vorläufig solltest Du hier bei der Erstellung bereits einplanen dass Du
da später mehr reinschreiben willst. Später gibt's über dynamische
Speicherallokierung Mittel und Wege das on the fly zu lösen, aber lassen
wir das mal für später. Jedenfalls, automatisch ist da nix.
Grundsätzlich: In C ist wenig bis gar nix automatisch.
Zuweisen von Arrays: DER klassische Anfängerfehler. Fühl Dich in guter
Gesellschaft, hat jeder gemacht. :)
Das zu erklären bedarf eines an Ausholen, weil dafür sollten wir zuerst
mal erklären wie denn das so ist mit Pointern, Referenzen und
Dereferenzierung. Würd's Dich sehr stören wenn wir das etwas
verschieben? Sonst ist die Verwirrung unter Garantie komplett.
Heinz, das verschieben wir ;-)
Ok - bin satt :)
Ihr habt mir sehr geholfen, was raus kommt, wird in jedem Fall hier als
erstes stehen!
Jetzt gönnt euch eine saubere Radler -
;-)
Jay schrieb:> Es ist so, dass in C an ein paar Stellen der Namen eines Arrays> automatisch in einen Pointer auf das Array umgewandelt wird. Der Aufruf> einer Funktion ist so eine Stelle. Man kann eine Funktion nicht mit> einem Array aufrufen, auch wenn es so aussieht. Es ist in Wahrheit immer> ein Pointer der in der Funktion ankommt.
hier sei noch gesagt dass in c arrays und pointer absolut das selbe
sind.
Marc S. schrieb:> hier sei noch gesagt dass in c arrays und pointer absolut das selbe> sind.
so pauschal stimmt das aber nicht.
sizeof() liefert bei pointer etwas anders als bei einem Array.
Peter II schrieb:> Marc S. schrieb:>> hier sei noch gesagt dass in c arrays und pointer absolut das selbe>> sind.>> so pauschal stimmt das aber nicht.>> sizeof() liefert bei pointer etwas anders als bei einem Array.
sizeof ist aber ein compile time operator, zur laufzeit ist es das
selbe. der compiler zählt halt und etzt für sizeof diese zahl ein. und
das gilt natürlich auch nur für statische arrays.
Marc S. schrieb:> hier sei noch gesagt dass in c arrays und pointer absolut das selbe> sind.
gefährliche Aussage. Die hat in dieser Form schon zu so manchem Fehler
geführt.
Sagen wir so:
Der Indexoperator ist in C in Einheiten von Pointer Arithmetik
definiert.
Bei alleiniger Nennung des Namens eines Arrays, 'zerfällt' das Array in
die Startadresse seines ersten Elements (Ausnahme: sizeof)
Diese Aussagen passen auch auf dein Beispiel und vermeiden die nicht
korrekte Aussage, dass ein Array und ein Pointer dasselbe wären. Denn
das sind sie nicht.
File1.c
1
chartst[10];
File.c
1
externchar*tst;
Frage: Warum funktioniert das nicht, wo doch Array und Pointer angeblich
dasselbe sind?
TriHexagon schrieb:> Das kann man bei jedem vernünftigen Compiler einstellen (z.B. gcc mit> -funsigned-char). Trotzdem ist es schlechter Stil char anstatt unsigned> char oder besser uint8_t zu benutzen.
Richtig.
Das führt aber ganz unmittelbar zu der Frage: wozu zum Teufel ist dann
der Datentyp char eigentlich überhaupt gut, wenn man ihn bei sauberer
Programmierung ohnehin zu fast garnichts einsetzen kann?
Und die einzig korrekte Antwort ist: Es handelt sich de facto um eine
Inkarnation der C-Portibilitätslüge. Und zwar nur um eine einzige von
Unmassen ähnlicher (und auch völlig andersgearteter) Versuche, eine in
der bösen Realitität ziemlich fiktive Portabilität wenigstens auf dem
Papier herbeizuzaubern...
Übrigens: Andere Programmiersprachen sind da schon vor Jahrzehnten sehr
viel weiter beim Betrug gewesen. Man denke z.B. nur an das Konzept der
"Variants". Genauso grenzdebile Scheiße, die auch einfach nur Sachen
verspricht, die sie nie und nimmer halten kann.
Und jedem, der von der Sache was versteht, war von Anfang an klar, dass
es so ist. Man braucht also nur die Apologeten raussuchen, die Unfähigen
und Unwissenden (AKA: Fanboys) abziehen, und in dem, was überbleibt, hat
man eine zuverlässige und aussagekräftige Liste notorischer Lügner...
c-hater schrieb:> TriHexagon schrieb:>>> Das kann man bei jedem vernünftigen Compiler einstellen (z.B. gcc mit>> -funsigned-char). Trotzdem ist es schlechter Stil char anstatt unsigned>> char oder besser uint8_t zu benutzen.>> Richtig.
Nein, falsch.
> Das führt aber ganz unmittelbar zu der Frage: wozu zum Teufel ist dann> der Datentyp char eigentlich überhaupt gut, wenn man ihn bei sauberer> Programmierung ohnehin zu fast garnichts einsetzen kann?
Ausgehend von einer falschen Aussage kann man fast alles behaupten...
c-hater schrieb:> Das führt aber ganz unmittelbar zu der Frage: wozu zum Teufel ist dann> der Datentyp char eigentlich überhaupt gut, wenn man ihn bei sauberer> Programmierung ohnehin zu fast garnichts einsetzen kann?
Wurde hier schon 5mal gesagt aber nochmal: Für Zeichen und Arrays von
Zeichen aka Strings. Da interessiert dich dann überhaupt nicht ob es
signed oder unsigned ist aber du kannst auf jeden Fall Zeichen
speichern.
Karl Heinz schrieb:> Bei alleiniger Nennung des Namens eines Arrays, 'zerfällt' das Array in> die Startadresse seines ersten Elements (Ausnahme: sizeof)
Eine weitere Ausnahme ist der Address-of-Operator. Wenn das nicht so
wäre müsste man ja die Adresse der Startadresse aufs erste Element
kriegen. Aber das gibts nur bein Pointern.
c-hater schrieb:> TriHexagon schrieb:>>> Das kann man bei jedem vernünftigen Compiler einstellen (z.B. gcc mit>> -funsigned-char). Trotzdem ist es schlechter Stil char anstatt unsigned>> char oder besser uint8_t zu benutzen.
Bitte auch das zitieren:
TriHexagon schrieb:> Bei Strings und Zeichen char, ansonsten uint8_t. Da hast du allerdings> recht das hätte ich unterscheiden müssen.
Damit wird klarer was ich meinte, nämlich nicht dass grundsätzlich bei
Zeichen unsigned char anstatt char verwendet werden sollte.
Michael Reinelt schrieb:> c-hater schrieb:>> TriHexagon schrieb:>>>>> Das kann man bei jedem vernünftigen Compiler einstellen (z.B. gcc mit>>> -funsigned-char). Trotzdem ist es schlechter Stil char anstatt unsigned>>> char oder besser uint8_t zu benutzen.>>>> Richtig.>> Nein, falsch.
Immer noch falsch?
Michael Reinelt schrieb:> Ausgehend von einer falschen Aussage kann man fast alles behaupten...
Nanana.
Hier muß ich durchaus in c-hater's Horn blasen.
Also:
1. ein char ist ein char und weil - wie der Namen sagt - selbiger
eigentlich zum Ausdrücken von Text aller Art gedacht war und ist, soll
man ihn genau DAFÜR auch so benutzen wie er heißt.
2. Die Anlässe, vorzeichenbehaftet mit 8 Bit Entitäten zu rechnen und
sich deshalb int zu verkneifen, sind extrem exotisch.
Vorzeichenlos mit 8 Bit Entitäten zu rechnen, ist hingegen Alltagsbrot
und deshalb sollte man dafür auch den Begriff "byte" verwenden und sich
notfalls als "unsigned char" selber definieren. Das Wort "byte" kennt
jeder - im Gegensatz zu uint8_t.
Damit wären wir wieder mal bei typedef angekommen:
typedef alte Bezeichnung neue Bezeichnung
so ist das definiert. Es ist eine reiner Umbenennungsakt. Man hätte es
anstelle von "typedef" besser "rename" genannt. Aber um Klarheit war man
in C-Kreisen ja noch nie bemüht.
Von dem krampfigen uintxyz_t halte ich garnichts, denn erstens ist es
unleserlich und zweitens ist das alles nicht im Sprachumfang enthalten,
sondern lediglich eine schnöde Umbenennung per Headefile. Kein
C-Compiler in dieser Welt kennt von sich aus uint8_t. So etwas schafft
also weder neue Datentypen noch ist es ein Beitrag zur vielgepriesenen
Portabilität, die es in der Realität mehr oder weniger schlecht auch
ohne den ganzen uintxyz_t Zirkus schon immer gegeben hat.
Und wer stolz behauptet, daß er dank Beschäftigung mit 8 und 32 Bittern
zu so etwas greift und damit die gepriesene Portabilität für sich
hergestellt hat, ist ein Scharlatan. Die wahren Probleme sind nämlich
ganz woanders, z.B. bei der "Endianess" - ich hab selber sowohl mit Big-
als auch mit Little-Endian Systemen zu tun und da muß man auf ganz
andere Dinge achten als auf dieses alberne uintblabla_t. Obendraufkommen
noch Dinge wie Alignment bei einigen Systemen.
W.S.
W.S. schrieb:> Vorzeichenlos mit 8 Bit Entitäten zu rechnen, ist hingegen Alltagsbrot> und deshalb sollte man dafür auch den Begriff "byte" verwenden und sich> notfalls als "unsigned char" selber definieren. Das Wort "byte" kennt> jeder - im Gegensatz zu uint8_t.
Und wer garantiert einem, dass unsigned char wirklich ein Byte ist? Der
Standard garantiert zwar sizeof(char) = 1 aber das muss trotzdem kein
Byte sein. Es soll scheinbar auch wirre Architekturen geben mit mehr als
8 Bits pro Byte wobei mir sowas noch nie untergekommen ist.
W.S. schrieb:> Von dem krampfigen uintxyz_t halte ich garnichts, denn erstens ist es> unleserlich und zweitens ist das alles nicht im Sprachumfang enthalten,> sondern lediglich eine schnöde Umbenennung per Headefile. Kein> C-Compiler in dieser Welt kennt von sich aus uint8_t.
Wie definierst du den Sprachumfang? Gut es gehört nicht zur Sprache an
sich aber der Header stdint.h ist im Standard definiert. Möchtest du
vielleicht printf auch nicht benutzen weil es nicht fest in die Sprache
eingebaut ist sondern einen Header braucht?
Ich finde die Verwendung der uintxx_t Typen durchaus sinnvoll wenn man
einen Typ mit einer festgelegten Größe braucht. Zum Beispiel beim
Datenaustausch über Dateien oder Netzwerk. Wenn ich 32Bit brauche dann
schreibe ich uint32_t und nicht etwa long weil ich gerade auf dem AVR
bin. Natürlich muss man es nicht überall nutzen und für einfache
Schleifenzähler nehm ich auch int. Endianess ist natürlich auch wichtig
aber bisher ist mir noch keine Big-Endian CPU begegnet.
W.S. schrieb:> Vorzeichenlos mit 8 Bit Entitäten zu rechnen, ist hingegen Alltagsbrot> und deshalb sollte man dafür auch den Begriff "byte" verwenden und sich> notfalls als "unsigned char" selber definieren. Das Wort "byte" kennt> jeder - im Gegensatz zu uint8_t.
"Byte" kennt zwar jeder als Begriff, allerdings gibt es keine
einheitliche, von jedem akzeptierte Definition dafür:
http://de.wikipedia.org/wiki/Byte
Einig sind sich diese Definitionen nur in dem einen Punkt, dass ein Byte
eine Ansammlung mehrerer Bits ist.
Beim Datentyp uint8_t steckt bereits im Namen viel Information über die
Eigenschaften des Typs:
- Die "8" sagt aus, dass der Typ aus 8 Bits besteht. Nicht 5 Bits, nicht
7 Bits, auch nicht "mindestens 8 Bits", sondern exakt 8 Bits.
- Das "int" sagt aus, dass diese 8 Bits ein Integer, d.h. eine ganze
Zahl repräsentieren. Damit ist klar, dass für diesen Datentyp
arithmetische Operationen wie Addition und Multiplikation definiert
sind.
- Das "u" sagt aus, dass diese Zahl vorzeichenlos ist
- Aus den vorangegangenen drei Punkten lässt sich der Wertebereich des
Typs ableiten: Er umfasst alle ganzen Zahlen im Bereich von 0 bis 255.
All diese Eigenschaften werden durch den Begriff "Byte" nicht
impliziert.
Um zweifelsfrei zu klären, ob der Typ "byte" auch auf exotischen
Prozessorarchitekturen genau 8 Bits enthält, ob diese Bits einfach nur
ein Bitmuster oder tatsächlich eine Zahl darstellen und ob ggf. diese
Zahl vorzeichenlos ist, muss die Dokumentation herangezogen werden.
Die Programmierpsprachen, die "byte" als Datentyp enthalten, verwenden
für Integertypen mit größerem Wertebereich gerne Namen wie "word",
"longword", "doubleword", "quad" u.ä., bei denen die Wertebereiche noch
viel unklarer und oft auch architekturabhängig sind.
Um alle Wünsche und Anforderungen abzudecken, bietet C mehrere Gruppen
von Integer-Datentypen:
Die architekturabhängigen Typen:
- char
- short
- int
- long
- long long
jeweils optional mit den Attributen "signed" oder "unsigned"
Die Typen mit definierter Bitgröße als Typsynonyme:
- int8_t
- int16_t
- int32_t
- int64_t
jeweils optional mit dem Präfix "u" (für unsigned)
Darüberhinaus gibt noch weitere Gruppen von Typsynonymen, wie bspw.
[u]int_leat<N>_t und [u]int_fast<N>_t.
> Und wer garantiert einem, dass unsigned char wirklich ein Byte ist? Der> Standard garantiert zwar sizeof(char) = 1 aber das muss trotzdem kein> Byte sein. Es soll scheinbar auch wirre Architekturen geben mit mehr als> 8 Bits pro Byte wobei mir sowas noch nie untergekommen ist.
Z.B. Motorola/Freescale DSP560xx/DSP563xx. Für diese DSPs gibt einen
"nicht mehr ganz frischen" GCC (v1.52 oder so), bei dem gilt:
char = 24 Bit
short int = 24 Bit
int = 24 Bit
long int = 48 Bit
und damit sizeof(char) = sizeof(short int) = sizeof(int) = 1
und sizeof(long int) = 2.
Spätestens bei solchen exotischen Prozessoren fällt man auf die Schnauze
wenn man davon ausgeht, dass ein char immer 8 Bit hat.
Allerdings programmiert man (also zumindest ich) die genannten
Prozessoren in der Regel in Assembler.
Man kann auch sagen, es gibt einen Unterschied zu dem was der Volksmund
8 Bit nennt (ein Byte) und was der Spezialist 8 Bit nennt, wenn er es
unbedingt braucht: Ein Oktett.
Nun ist die Bezeichnung Oktett schon sehr exotisch und in 99,9...% aller
Fälle kann man ein Byte als 8 Bit annehmen. Eine Ausnahme ist
ausgerechnet C. Da funktioniert 8 Bit == char == Byte eben nicht in 100%
aller Fälle.
Wenn man, wie in diesen Thread, C Grundlagen auseinanderklamüsert und
versucht Missverständnisse zu vermeiden, dann lohnt es sich schon nicht
neue falsche Aussagen einzuführen wie char == 8 Bit (oder pointer ==
array). Beides ist nun mal bei C falsch.
Zur Frage nach dem C-Standard, eine alte Version konnte man mal relativ
preiswert in einer kommentierten Auflage kaufen. Das dumme an den
Kommentaren war nur, dass sie erschreckend falsch waren
http://www.lysator.liu.se/c/schildt.html Statt dessen würde ich, auch
wenn es ebenfalls nicht mehr den neusten Standard beschreibt, das
klassische Lehrbuch
http://en.wikipedia.org/wiki/The_C_Programming_Language empfehlen.
> int * a, * b;> (Und ob du da jetzt Leerzeichen machst oder nicht, ist dem> C Compiler wurscht)
genau, man kann sogar
int*a,*b;
schreiben.
> Nun ist die Bezeichnung Oktett schon sehr exotisch.
Ja, die ganze Welt schreibt Byte, nur bei den Franzosen heißt es Oktett
(und sie meinen damit ein Byte).
Hannes Jaeger schrieb:> 8 Bit == char == Byte
Die kleinste adressierbare Einheit ist in C ein Byte (per Definition,
was von der Größe her ein char ist).
Damit man weiß wie groß das ist, gibt es das Makro CHAR_BIT aus
limits.h
Und dieses Byte muss mindestens 8-Bit haben, kann auch mehr sein
Hannes Jaeger schrieb:> dann lohnt es sich schon nicht> neue falsche Aussagen einzuführen wie char == 8 Bit (oder pointer ==> array). Beides ist nun mal bei C falsch.
Das erste kann richtig sein, (das zweite ist auf alle Fälle falsch)
Hannes Jaeger schrieb:> Zur Frage nach dem C-Standard, eine alte Version konnte man mal relativ> preiswert in einer kommentierten Auflage kaufen.
Es gibt die Drafts zu den Standards. Die letzen davon stimmen jeweils
mit dem eigentlichen Standard überein.
Die Links dazu stehen bei https://www.c-plusplus.net/forum/300567
Und es gibt noch "The Standard C Library" von P.J. Plauger.
Das bezieht sich zwar auf C89, dafür aber mit Kommentaren und Quellcode
zu den Standardfunktionen. Plauger hat C89 selber mit verabschiedet.
W.S. schrieb:> Von dem krampfigen uintxyz_t halte ich garnichts, denn erstens ist es> unleserlich und zweitens ist das alles nicht im Sprachumfang enthalten,> sondern lediglich eine schnöde Umbenennung per Headefile. Kein> C-Compiler in dieser Welt kennt von sich aus uint8_t. So etwas schafft> also weder neue Datentypen noch ist es ein Beitrag zur vielgepriesenen> Portabilität, die es in der Realität mehr oder weniger schlecht auch> ohne den ganzen uintxyz_t Zirkus schon immer gegeben hat.
Diesen Unsinn lese ich seit Jahren öfters und sehe da bis heute kein
Problem.
1. Die Typen gehören zu C99 (stdint.h), also zum Sprachumfang.
2. Lassen sich die Typen auch nach belieben umbenennen (z.B. u32). Wobei
die Namen der Typen einem System folgen und Sinn ergeben.
3. Der Standard garantiert, dass die Typen auch so breit sind. Da ändert
der Umstand, dass diese Typen als typedefs umgesetzt werden, gar nichts.
4. Die Entscheidung die Typen nicht in den Sprachkern aufzunehmen ist
legitim. Man will damit mögliche Kompatibilitätsprobleme mit altem Code
aus dem Weg gehen und den Sprachkern kompakt halten. An vielen neuen
Sprachen sieht man diesen Trend, Sprachelemente mit der
Standardbibliothek umzusetzen (wenn es möglich ist). Das sieht man unter
anderem gut an Rust.
Also alles nur Märchen?
Gerade die [u]int<N>_t vermindern die Portabilität, weil es Probleme
geben kann, wenn diese nicht existieren.
Man sollte sich schon Gedanken machen, wo man welchen Typ einsetzt.
Dirk B. schrieb:> Gerade die [u]int<N>_t vermindern die Portabilität, weil es Probleme> geben kann, wenn diese nicht existieren.>> Man sollte sich schon Gedanken machen, wo man welchen Typ einsetzt.
Ok das lasse ich gelten, wobei sowas relativ selten vorkommen sollte
(auf exotischen Architekturen). Eine Sprache die auf allen Architekturen
gleich gut funktioniert wird es allerdings nie geben.
TriHexagon schrieb:> Eine Sprache die auf allen Architekturen> gleich gut funktioniert wird es allerdings nie geben.
Kommt auch auf das Problem an. (wird Hardware angesprochen, kann es
abstrakt berechnet werden, ....)
W.S. schrieb:> Von dem krampfigen uintxyz_t halte ich garnichts, denn erstens ist es> unleserlich und zweitens ist das alles nicht im Sprachumfang enthalten,> sondern lediglich eine schnöde Umbenennung per Headefile.
Nach dieser Argumentation ist im Sprachumfang auch keinerlei Möglichkeit
enthalten, Text auszugeben oder dynamischen Speicher anzufordern. Denn
printf() und malloc() sind nicht Teil des Sprachkerns, sondern der
Standardbibliothek, genauso wie uint*_t. Letzteres zugegebenermaßen erst
seit 16 Jahren.
> Kein C-Compiler in dieser Welt kennt von sich aus uint8_t. So etwas> schafft also weder neue Datentypen
Warum soll man auch neue Datentypen dafür brauchen?
eProfi schrieb:>> Nun ist die Bezeichnung Oktett schon sehr exotisch.
Sie ist der Fachausdruck. Das ist im übrigen auch die offizielle
Bezeichnung nach ISO. "Byte" ist nur der umgangssprachliche Begriff für
"8 Bit".
> Ja, die ganze Welt schreibt Byte, nur bei den Franzosen heißt es Oktett> (und sie meinen damit ein Byte).
Es ist genau umgekehrt. Wir schreiben Byte und meinen damit in der Regel
Oktett. Als der Begriff "Byte" erfunden wurde, waren übrigens eher
andere Größen als 8 Bit üblich.
Dirk B. schrieb:> Gerade die [u]int<N>_t vermindern die Portabilität, weil es Probleme> geben kann, wenn diese nicht existieren.
Wo soll da das Problem sein?
Für den seltenen Fall, dass man solchen Code mit einem Compiler
übersetzen muss, der auf einem Stand von vor sechzehn Jahren ist:
Eine Light-Version von stdint.h schreibt man sich innerhalb von Minuten.
Wenn es sein muss auf Basis einer stdint.h, die man aus dem Netz fischt.
Dass heißt, man kann diese fehlenden Typen bei vergammelten Compilern
mit Bordmitteln nachrüsten, und kann den Gammelcompiler weiter benutzen.
Was mehr willst du eigentlich?
Braucht jemand die Datentypen öfters hat er so eine Headerdatei für
seinen Gammelcompiler sowieso bereits fertig in der Schublade.
Laut GNU header portability guide sollte man besser <inttypes.h> verwend
en, weil darin auf jeder (GNU bekannten) Plattform stdint.h inkludiert
wird, falls diese dort existiert. inttypes.h existiert auf allen.
Neben [u]int<N>_t gibt es ja in <inttypes.h> auch die "least" und "fast"
Varianten. Wenn man also z.B. einen Zähler von 1.30000 braucht, dann
kann man den als uint_least16_t definieren, wenn man Speicherplatzangst
hat, oder uint_fast16_t, wenn man die schnellste Variante auf der
entsprechenden Hardware braucht.
int, long, short, .. auf feste Breiten zu definieren, (wie in Java,)
wäre nicht sehr zukunftssicher. Dann hätte man zu PDP11-Zeiten int auf
16 und long auf 32 Bit festgelegt. Und als C "erfunden" wurde auf der
PDP7, gab es 18bit Worte (und eventuell 9-Bit char?).
Hannes Jaeger schrieb:> Für den seltenen Fall, dass man solchen Code mit einem Compiler> übersetzen muss, der auf einem Stand von vor sechzehn Jahren ist:
Es geht nicht um vergammelte Compiler sondern "exotische" Architekturen.
Da, wo ein Byte mehr als 8 Bit hat.
Da kann es keinen uint8_t geben. Aber ein uint_least8_t schon.
Dirk B. schrieb:> Gerade die [u]int<N>_t vermindern die Portabilität, weil es Probleme> geben kann, wenn diese nicht existieren.
Da habe ich lieber einen Fehler beim Compilieren als das es nacher so
aussieht es würde alles funktionieren aber für bestimmte Wertebereiche
geht dann was schief.
Rolf Magnus schrieb:> Nach dieser Argumentation ist im Sprachumfang auch keinerlei Möglichkeit> enthalten, Text auszugeben oder dynamischen Speicher anzufordern. Denn> printf() und malloc() sind nicht Teil des Sprachkerns, sondern der> Standardbibliothek
Schreibe nicht so ein Zeugs, das macht mich so langsam zornig.
printf ist eine ganz schnöde Funktion, die sich jeder nach seinem Gusto
selber schreiben könnte wenn er das denn wollte.
Der C-Compiler kennt printf oder malloc jedenfalls NICHT. Punkt. Er
kennt von Hause aus ÜBERHAUPT KEINE Funktionen. Man muß selbige dem
Compiler nämlich vor der ersten Verwendung erstmal klarmachen und dazu
dient ein sogenanntes Headerfile, was man einbinden muß: #include ...
Diese Syntax wirst du ja wohl kennen.
Kurzum, alle (ja ALLE!) Funktionen in C gehören nicht zum Sprachumfang,
sind also dem C-Compiler nicht von selbst bekannt. Das ist bei Pascal
durchaus anders, dort sind Funktionen wie chr, val, inc, dec, write, at
usw. im Sprachumfang enthalten, weswegen der Pascal-Compiler sowas ohne
jegliche uses Anweisung von sich aus kennt.
Also, Rolf, hast du das nun verstanden?
W.S.
W.S. schrieb:> Der C-Compiler kennt printf oder malloc jedenfalls NICHT. Punkt.
Was anderes hat hier auch niemand behauptet. Natürlich braucht man ein
include. Ändert aber nichts daran, dass die Header trotzdem zu C gehören
und man davon ausgehen kann, dass diese vorhanden sind.
Siehe hier Kapitel 7:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
Sebastian V. O. schrieb:> Und wer garantiert einem, dass unsigned char wirklich ein Byte ist?
Ein Blick in das Manual der betreffenden Hardware. Anders geht es
prinzipiell nicht. Abgesehen davon IST ein Byte ein unsigned char und es
sollte dir recht schwerfallen, davon eine Ausnahme in der realen Welt zu
finden, die irgendwelche Relevanz hat.
Sebastian V. O. schrieb:> Wie definierst du den Sprachumfang?
Ganz einfach: alles das, was der C-Compiler per se versteht. Guck in die
Doku zu irgend einem Compiler, da steht es drin. Normalerweise ist die
Doku zu etwaigen Standard-Bibliotheken ein davon völlig verschiedenes
Dokument.
Sebastian V. O. schrieb:> Endianess ist natürlich auch wichtig> aber bisher ist mir noch keine Big-Endian CPU begegnet.
Siehste, ich habe seit vielen Jahren genau DAS als täglich Brot. Genau
deshalb hab ich z.B. meine Fonts auch so geschrieben, daß sie mit beiden
Endian-Arten funktionieren - und dazu braucht man uintxyz_t und
Konsorten nicht wirklich.
Das kannst du dir merken: Portabilität ist eine Sache der
Software-Strategie und der Struktur-Entwürfe und hat nichts mit irgend
einer konkreten Programmiersprache zu tun - und schon garnicht mit
irgend welchen verquaasten Umbenennungen von Basistypen in C.
Yalu X. schrieb:> "Byte" kennt zwar jeder als Begriff, allerdings gibt es keine> einheitliche, von jedem akzeptierte Definition dafür:
Yalu, bleib wenigstens DU hier auf dem Teppich. Du mußt auch nicht jeden
Unsinn glauben, der in einem Wiki steht. Wenn wir mal ganz kurz
historisch werden, dann fing das Ganze mit Baudot an und war 5 Bit breit
- und NIEMAND hat dazu Byte gesagt. Und ASCII war schon immer nur 7 Bit
breit, damit man das 8. Bit im Byte für die Parität benutzen konnte. Bei
Lochbandlesern war das essenziell. Und wenn du mal in steinalte Dokus
von Intel guckst, so aus den frühen 70er Jahren, dann sollte auch dir
klar werden, daß es schon ganz lange das Byte zu exakt 8 Bit gibt. Mehr
noch:
8 Bit = byte,
16 Bit = word,
32 Bit = dword,
64 Bit = qword,
80 Bit = tword.
Diese Klassifikation ist mittlerweile über 37 Jahre alt. Reicht das?
Hier geht es um was ganz Anderes, nämlich die Unfähigkeit zu klaren
Verhältnissen zu kommen. ANSI hatte es einmal geschafft, die Machete in
den C-Wust zu hauen, saubere Prototypen und ordentliche Argumente zu
schaffen. Aber zu einem zweiten Aufräumen, das nach Sachlage
allerdringendst erforderlich wäre, haben sie es nicht geschafft.
Es geht - wie man hier wieder mal deutlich sieht - auch um die
Beschränktheit der Leute. Wenn jemand argumentiert, daß es eine
Headerdatei in die Dokumente von C99 geschafft hat und deshalb das, was
da drin steht, somit zum Sprachumfang gehören würde, dann kann ich mir
nur an den Kopf greifen. Welch ein Unverstand. Entweder gehört etwas zum
Sprachumfang, dann ist es in die Compiler bereits eingebaut und bedarf
keiner Headerdatei, oder es gehört eben NICHT dazu und muß erst per
Headerdatei definiert werden. Leute, lernt Denken.
W.S.
W.S. schrieb:> Sebastian V. O. schrieb:>> Und wer garantiert einem, dass unsigned char wirklich ein Byte ist?>> Ein Blick in das Manual der betreffenden Hardware.
Aha. Ich dachte es ging um Portabilität. Wieso muss ich jetzt in
hardwarespezifische Unterschlagen schauen?
W.S. schrieb:> Normalerweise ist die> Doku zu etwaigen Standard-Bibliotheken ein davon völlig verschiedenes> Dokument.
Die Doku irgendeines Compilers interessiert mich nicht. Was zählt ist
der Standard und da ist Language und Library beides in einem Dokument
(oben verlinkt).
W.S. schrieb:> Genau> deshalb hab ich z.B. meine Fonts auch so geschrieben, daß sie mit beiden> Endian-Arten funktionieren - und dazu braucht man uintxyz_t und> Konsorten nicht wirklich.
Rein aus Interesse: Wie speicherst du deine Fonts? Wenn irgendwie
textbasiert hast du das Problem natürlich umschifft aber wenn man binär
speichern möchte dann muss man sich auf eine Bitbreite, Endianness,
Alignment usw. festlegen. Wenn du binär arbeitest, wie realisierst du
fest vorgegebene Bitbreiten von z.B. 32Bit?
W.S. schrieb:> Es geht - wie man hier wieder mal deutlich sieht - auch um die> Beschränktheit der Leute. Wenn jemand argumentiert, daß es eine> Headerdatei in die Dokumente von C99 geschafft hat und deshalb das, was> da drin steht, somit zum Sprachumfang gehören würde, dann kann ich mir> nur an den Kopf greifen. Welch ein Unverstand. Entweder gehört etwas zum> Sprachumfang, dann ist es in die Compiler bereits eingebaut und bedarf> keiner Headerdatei, oder es gehört eben NICHT dazu und muß erst per> Headerdatei definiert werden. Leute, lernt Denken.
Das ist der größte Blödsinn, den ich seit langem gehört habe.
Abgesehen davon sind z.B. bei gcc die meisten Standardfunktionen
tatsächlich im Compiler eingebaut.
>8 Bit = byte,>16 Bit = word,>32 Bit = dword,>64 Bit = qword,>80 Bit = tword.
Das ist die Nomenklatur EINES Herstellers. Und auch sehr logisch
aufgebaut. Warum sind 8Byte ein QuadWord, aber 10Byte keine FiveWord
oder TenByte?
Ganz wichtig: gegen die Portabilität von C spricht ja, daß es auf
praktischer keiner Plattform verwendet wird, oder :-))
Bastler schrieb:>>8 Bit = byte,>>16 Bit = word,>>32 Bit = dword,>>64 Bit = qword,>>80 Bit = tword.> Das ist die Nomenklatur EINES Herstellers.
Richtig. Bei der weltweit am weitesten verbreiteten CPU-Architektur ist
die Nomenklatur anders. Da ist ein word nämlich 32 Bit breit.
Man sieht hier recht klar, daß W.S. Schwierigkeiten hat, über seinen
Tellerrand zu schauen und glaubt, daß alles, was er nicht kennt, auch
nicht relevant ist.
Sebastian V. O. schrieb:> Aha. Ich dachte es ging um Portabilität. Wieso muss ich jetzt in> hardwarespezifische Unterschlagen schauen?
Und was willst du portieren? Du erwartest nicht ernsthaft, ein
Windows-Programm bloß per Neukompilieren auf ner Bare-Metal-MIPS
lauffähig zu kriegen odern nen SDIO-Treiber vom ARM7TDMI auf nem MAC?
Wer an reinen Algorithmen interessiert ist, der nimmt dafür eine
ausreichend hardwareferne Sprache, wie z.B. Fortran oder so. Aber wer in
C und das auch noch so einigermaßen hardwarenah programmiert, der muß
sich zwangsläufig auch mit der zu beglückenden Hardware vertraut machen.
Portabilität ist sowieso ein Witz, wenn man sich weigert, in
"hardwarespezifische Unterschlagen (zu) schauen".
Sebastian V. O. schrieb:> Wenn du binär arbeitest, wie realisierst du> fest vorgegebene Bitbreiten von z.B. 32Bit?
Byteweise und bitseriell. Daß heißt, Bytegrenzen sind für die Zeichen
irrelevant. Ist eigentlich ganz einfach.
Heinz L. schrieb:> Erh... Leute, der Threaderöffner hat glaub ich das Gebäude bereits> verlassen. :)
Tja, mich wundert das nicht. Bei so einer grandiosen Beratung...
W.S.
Heinz L. schrieb:> Erh... Leute, der Threaderöffner hat glaub ich das Gebäude bereits> verlassen. :)
Ist die Luft jetzt wieder rein? Kann ich aus meinem Versteck?
Da bin ich ja mal froh, dass ihr fast alle gleicher Meinung seid,
danke für die vielen Infos zwischen den Zeilen. Ich habe eine Menge
dazugelernt und bin bereit für das nächste Kapitel "pointer" in allen
Variationen, dazu erstelle ich demnächst eine neue Frage.
Danke!