Hier ein kleiner Beispielcode,
den ich im AVR-Studio simuliert habe.
1
intmain(void)
2
{
3
4
charn=0;
5
charx=4;
6
7
while(1)
8
{
9
while(1)
10
{
11
if(--x>0)break;
12
x=4;
13
}
14
15
n++;
16
}
17
18
return0;
19
}
Es funktioniert wie erwartet. Solange x
größer ist als 0, wird die innere while-
Schleife sofort verlassen. Wir x zu 0,
wird x mit 4 nachgeladen.
Mache ich aber aus dem "Größer-Vergleich"
einen "Größer-Gleich-Vergleich" wie im
nächsten Beispiel.
1
intmain(void)
2
{
3
4
charn=0;
5
charx=4;
6
7
while(1)
8
{
9
while(1)
10
{
11
if(--x>=0)break;
12
x=4;
13
}
14
15
n++;
16
}
17
18
return0;
19
}
wird die innere while-Schleife
immer sofort verlassen. Die Zeile
1
x=4;
wird überhaupt nicht mehr ausgeführt.
x zählt ins negative.
Ich versteh es nicht. Wer hat eine Erklärung?
Stefan schreibt:
>Ein char ist nicht per Default signed (auch nicht unsigned).
Das war es. Danke.
Flo schreibt:
>Nimm mal nen anderen Datentyp als char für x und n.
Ja, mit int klappt es auch ohne signed.
Genaugenommen ist char ohne weitere Angabe natürlich schon entweder
immer signed oder immer unsigned, etwas drittes gibt es schliesslich
nicht. Es ist aber "implementation defined", welches von beiden der
Default ist, das kann also jeder C-Compiler so machen wie er (bzw. der
Hersteller) es will, es muss gemäss Standard nur im Handbuch
dokumentiert werden, welche Wahl getroffen wurde.
Je nach Compiler kann man das ausserdem noch über Compileroptionen
beeinflussen (z.B. -fsigned-char/-funsigned-char beim GCC).
"int" (oder z.B. auch "short" und "long") sind dagegen ohne Angabe immer
signed.
Andreas
>Genaugenommen ist char ohne weitere Angabe natürlich schon entweder>immer signed oder immer unsigned, etwas drittes gibt es schliesslich>nicht.
Unfug.
signed char, unsigned char und char sind 3 verschiedene Typen.
Standard lesen und kein Halbwissen daherschwätzen.
DerAlbi schrieb:
>>Genaugenommen ist char ohne weitere Angabe natürlich schon entweder>>immer signed oder immer unsigned, etwas drittes gibt es schliesslich>>nicht.> Unfug.> signed char, unsigned char und char sind 3 verschiedene Typen.> Standard lesen und kein Halbwissen daherschwätzen.
Abschnitt 6.2.5, Satz 15:
The three types char, signed char, and unsigned char are collectively
called the character types. The implementation shall define char to have
the same range, representation, and behavior as either signed char or
unsigned char.
Andreas
Nelix und Gastino schreiben:
>Ich weiß es sind nur kurze Beispiel,>aber der Programmierstil ist grausam.
Konstruktive Kritik wird gerne genommen.
Nichtssagende Kommentare verschwenden nur Platz !
Ganymed schrieb:
> Konstruktive Kritik wird gerne genommen.> Nichtssagende Kommentare verschwenden nur Platz !
Deine Zeile mit dem if z.B. warum machst du da eine Einrückung?
Offensichtlich wird hier nicht wenn die Abfrage eintrifft x gesetzt...
das sit auf jedenfall wenn du das später mal nur kurz überfliegst
Fehleranfällig.
"Grausam" Wird allerdings denke ich empfunden, das du eine (endlos)
Schleife nutzt, welche dann durch eine Bedingung abgebrochen wird um
ansosnten eine Variable zuzuweisen... das ganze geht auch mit einer
normalen Abfrage:
1
intmain(void){
2
charn=0;
3
charx=4;
4
5
while(1){
6
if(--x<0){
7
x=4;
8
}
9
n++;
10
}
11
return0;
12
}
Welchem Zweck 'n' in dem Beispiel dient ist auch erstmal nicht deutlich.
Außerdem das dekrementieren innerhalb der Abfrage ist auch im Zweifel
eher verwirrend den förderlich...
Ach ja eher nebensächlich aber wo wir gerade bei "Stilkritik" sind ;)
Vor Satzzeichen gehört kein Leerzeichen! Auch nicht bei einem '!' ;P
Andreas Ferber schrieb:
> DerAlbi schrieb:>>>Genaugenommen ist char ohne weitere Angabe natürlich schon entweder>>>immer signed oder immer unsigned, etwas drittes gibt es schliesslich>>>nicht.>> Unfug.>> signed char, unsigned char und char sind 3 verschiedene Typen.>> Standard lesen und kein Halbwissen daherschwätzen.>> Abschnitt 6.2.5, Satz 15:>> The three types char, signed char, and unsigned char are collectively> called the character types. The implementation shall define char to have> the same range, representation, and behavior as either signed char or> unsigned char.
Und? Dass es dem Compiler überlassen wird, ob er sich auf binärer Ebene
für signed oder unsigned entscheidet, ändert nichts daran, dass es auf
C-Ebene drei unterschiedliche Typen sind. Steht doch auch eindeutig da:
> The three types char, signed char, and unsigned char ...
Ganymed schrieb:
> Konstruktive Kritik wird gerne genommen.> Nichtssagende Kommentare verschwenden nur Platz !
Die ganze Konstruktion mit den zwei Endlosschleifen, um eine Variable
nach 4 Durchläufen zurückzusetzen (und eine andere um 1 zu erhöhen), ist
grausam. Und das i-Tüpfelchen ist das
1
if(--x>0)break;
Bei solchen Konstruktionen fragen sich mit Sicherheit 80% der
C-Programmierer, was als erstes passiert: Das Dekrementieren von x oder
der Vergleich. Fehlervermeidung beginnt mit eindeutigem und lesbarem
Code. Man sollte nicht jede C-Syntaxkrankheit anwenden, nur weil sie
möglich ist.
Eine Möglichkeit für die Hauptschleife wäre folgende:
Stefan Ernst schrieb:
> Und? Dass es dem Compiler überlassen wird, ob er sich auf binärer Ebene> für signed oder unsigned entscheidet, ändert nichts daran, dass es auf> C-Ebene drei unterschiedliche Typen sind. Steht doch auch eindeutig da:
Kommt darauf an wie Du unterschiedlich definierst.
Wenn Du definierst daß 'unterschiedlich' bedeutet, daß sie 'nicht
gleich' sind, dann sind es nur 2 Datentypen, da char entweder gleich
signed char oder unsigned char ist.
Fakt ist es gibt nur 2 unterschiedliche.
Stefan Ernst schrieb:
> Und? Dass es dem Compiler überlassen wird, ob er sich auf binärer Ebene> für signed oder unsigned entscheidet, ändert nichts daran, dass es auf> C-Ebene drei unterschiedliche Typen sind. Steht doch auch eindeutig da:
Das ist eine rein akademische Diskussion ohne jeglichen Wert für den
Programmierer. Der Compiler entscheidet sich für signed oder unsigned
und nur das ist letztendlich relevant. Dass "char" noch als eigenständig
dritter in der Spezifikation definiert ist, der aber immer entweder
als signed oder als unsigned behandelt wird, ist doch wirklich
unerheblich.
Micha schrieb:
> Gastino G. schrieb:>> Du erhöhst in Deinem Beispiel n mit jedem Durchlauf, das soll aber nur>> erhöht werden, wenn die Bedingung erfüllt ist.> Sicher?
Ja. Im Urspungsbeispiel des Thread-Eröffners war offensichtlich das
beabsichtigt.
Gastino G. schrieb:
>>> Du erhöhst in Deinem Beispiel n mit jedem Durchlauf, das soll aber>>> nur erhöht werden, wenn die Bedingung erfüllt ist.>> Sicher?>> Ja. Im Urspungsbeispiel des Thread-Eröffners war offensichtlich das> beabsichtigt.
Ob das beabsichtigt war, weiß ich nicht. Ich bin mir auch nicht ganz
sicher, welche Bedingung du genau meinst. Programmiert ist es so, daß n
immer dann inkrementiert wird, wenn x nicht wieder auf 4 initialisiert
wird, also auf meinen Code umgesetzt so:
1
while(1){
2
if(x==0){
3
x=4;
4
}else{
5
x--;
6
n++;
7
}
8
}
9
return0;
Generell kann man schwer sagen, was wirklich beabsichtigt ist, schon
alleine, weil hier der Teil des Code fehlt, der überhaupt was macht und
man nicht weiß, wo der eigentlich stehen würde.
fragensteller schrieb:
> warum wird eigentlich noch so oft "char" als ersatzfür 8-bit integer> verwendet?
char ist nicht der "Ersatz" für einen 8-Bit-Integer, sondern für einen
kleinen Integer mit mindestens, aber nicht zwangsweise exakt 8 Bit.
> #include <stdint.h>>> int val() {> uint8_t i = (1 << 5) | (1 << 3) | (1 << 1);> int8_t j = 42;
uint_least8_t und int_least8_t würden besser passen, sofern die Größe
wichtiger ist als die Geschwindigkeit, ansonsten uint_fast8_t und
int_fast8_t. Die (u)int*_t ohne least/fast sollte man nur dann
verwenden, wenn man wirklich unbedingt einen Integer mit exakt der
angegebenen Breite benötigt.
>> return i-j;> }
Udo R. S. schrieb:
> Kommt darauf an wie Du unterschiedlich definierst.> Wenn Du definierst daß 'unterschiedlich' bedeutet, daß sie 'nicht> gleich' sind, dann sind es nur 2 Datentypen, da char entweder gleich> signed char oder unsigned char ist.>> Fakt ist es gibt nur 2 unterschiedliche.
Unsinnige Wortklauberei.
Fakt ist:
Binärer Kontext: 2 unterschiedliche.
C Kontext: 3 unterschiedliche.
Und da hier in C programmiert wird, interessiert nun mal in erster Linie
letzteres.
Gastino G. schrieb:
> Das ist eine rein akademische Diskussion ohne jeglichen Wert für den> Programmierer. Der Compiler entscheidet sich für signed oder unsigned> und nur das ist letztendlich relevant. Dass "char" noch als eigenständig> dritter in der Spezifikation definiert ist, der aber immer entweder> als signed oder als unsigned behandelt wird, ist doch wirklich> unerheblich.
Programmierer wie du, die das "unerheblich" finden, sind dann genau der
Grund dafür, warum man sich beim Portieren so oft damit rumärgern muss.
Gastino G. schrieb:
> Das ist eine rein akademische Diskussion ohne jeglichen Wert für den> Programmierer.
Das würde ich so nicht sagen
Am besten geht man nach Schema F vor
* hab ich einen Character, im Sinne eines Zeichens in einem Text
Ja -> benutze einen char
* habe ich einen 'kleinen Integer mit Vorzeichen'
Ja -> benutze signed char
* habe ich einen 'kleinen Integer ohne Vorzeichen'
Ja -> benutze unsigned char
Speziell die letzte Möglichkeit sollte man sich zu Herzen nehmen, wenn
man es mit Bytes zu tun hat. Mit plain vanilla char ist da schon so
mancher auf die Schnauze gefallen.
Ganz besonders ärgerlich ist das Nichtbeachten bei der Portierung von
Compiler A nach Compiler B, wenn beide unterschiedlicher Ansicht über
die Natur von char sind.
> Der Compiler entscheidet sich für signed oder unsigned> und nur das ist letztendlich relevant.
Relevant ist, dass man dem Compiler nur dann die Entscheidung überlässt,
wenn man es mit Charactern in Sinne von 'Zeichen im Text' überlässt. In
allen anderen Fällen ist man explizit.
Rolf Magnus schrieb:
> Ob das beabsichtigt war, weiß ich nicht. Ich bin mir auch nicht ganz> sicher, welche Bedingung du genau meinst. Programmiert ist es so, daß n> immer dann inkrementiert wird, wenn x nicht wieder auf 4 initialisiert> wird, also auf meinen Code umgesetzt so:
Ja, jetzt ist das in Deinem Beispiel auch so. In Deinem vorherigen
Beispiel haben die Klammern um die Inhalte Deiner
if{}else{}-Konstruktion gefehlt und n wäre in jedem Durchlauf
inkrementiert worden.
Stefan Ernst schrieb:
> Programmierer wie du, die das "unerheblich" finden, sind dann genau der> Grund dafür, warum man sich beim Portieren so oft damit rumärgern muss.
Ganz im Gegenteil. Wenn man sich klar macht, dass sich der Compiler bei
der Verwendung von char für das Eine oder Andere entscheidet, dann
kümmert man sich um den Kern der Geschichte und nicht um eine angeblich
dritte Variante, die aber real umgesetzt überhaupt nicht existiert!
Insofern führt eher Deine Ansicht, es gäbe wirklich 3 Varianten, zu
Problemen bei der Portierung. Da spielt nämlich durchaus eine Rolle, wie
es am Ende binär umgesetzt ist!
Karl heinz Buchegger schrieb:
> Relevant ist, dass man dem Compiler nur dann die Entscheidung überlässt,> wenn man es mit Charactern in Sinne von 'Zeichen im Text' überlässt. In> allen anderen Fällen ist man explizit.
Und genau das tut man, wenn man sich klar macht, dass real nur zwei
Varianten existieren und die Dritte nur eine beliebige Definition des
Compilers ist.
Gastino G. schrieb:
> Karl heinz Buchegger schrieb:>> Relevant ist, dass man dem Compiler nur dann die Entscheidung überlässt,>> wenn man es mit Charactern in Sinne von 'Zeichen im Text' überlässt. In>> allen anderen Fällen ist man explizit.>> Und genau das tut man, wenn man sich klar macht, dass real nur zwei> Varianten existieren und die Dritte nur eine beliebige Definition des> Compilers ist.
Nein. Man tut es dann, wenn man weiß, daß es in C diese drei Typen gibt,
und daß man sich überhaupt nicht darum zu kümmern braucht, wie char
implementiert ist, weil man es eh nur in einem Kontext benutzt, wo das
keine Rolle spielt.
Rolf Magnus schrieb:
> Nein. Man tut es dann, wenn man weiß, daß es in C diese drei Typen gibt,> und daß man sich überhaupt nicht darum zu kümmern braucht, wie char> implementiert ist, weil man es eh nur in einem Kontext benutzt, wo das> keine Rolle spielt.
Dein Teilsatz "weil man es eh nur in einem Kontext benutzt, wo das keine
Rolle spielt" bestätigt doch gerade meine Aussage.
Deine Aussage war doch, daß man sich klar sein muß, daß char nur
entweder wie signed char oder wie unsigned char implementiert sein kann.
Meine Aussage ist, daß man einfach davon ausgehen kann, daß es auf
C-Ebene ein dritter, von denen beiden erstmal unabhängiger Typ ist und
es einem egal sein kann, ob der jetzt intern wie einer der beiden
anderen implementiert ist oder nicht. Daß er das sein muß, ist nur für
den wichtig, der einen Compiler bauen will, nicht für den, der damit
Programme übersetzt.
Rolf Magnus schrieb:
> Deine Aussage war doch, daß man sich klar sein muß, daß char nur> entweder wie signed char oder wie unsigned char implementiert sein kann.> Meine Aussage ist, daß man einfach davon ausgehen kann, daß es auf> C-Ebene ein dritter, von denen beiden erstmal unabhängiger Typ ist und> es einem egal sein kann, ob der jetzt intern wie einer der beiden> anderen implementiert ist oder nicht. Daß er das sein muß, ist nur für> den wichtig, der einen Compiler bauen will, nicht für den, der damit> Programme übersetzt.
Nein, das ist falsch. Es spielt für den Programmierer durchaus eine
Rolle, wie der Compiler solche Variablen definiert, denn das hat
Auswirkungen auf das Programm. Das sieht man schon an obigem Beispiel.
Wenn bei dem Beispiel der Compiler "char" als "unsigned" definiert, dann
funktioniert sein Programm nicht mehr.
Gastino G. schrieb:
> Nein, das ist falsch. Es spielt für den Programmierer durchaus eine> Rolle, wie der Compiler solche Variablen definiert, denn das hat> Auswirkungen auf das Programm.
Genau darum gehts.
Lern deinem Auszubildenden, dass es 3 'char' Typen gibt, von denen einer
für Zeichen und 2 fürs Rechnen vorgesehen sind und er ist das Problem
los. Zudem stimmt diese Aussage auch mit dem geforderten Verhalten im
C-Standard überein.
So einfach kann die Welt manchmal sein.
> Das sieht man schon an obigem Beispiel.> Wenn bei dem Beispiel der Compiler "char" als "unsigned" definiert, dann> funktioniert sein Programm nicht mehr.
Der Fehler in seinem Programm ist, dass er einen unqualifizierten char
zum rechnen benutzt. Getrau der einfachen Tabelle der 3 möglichen
char-Datentypen und ihrer Verwendung
char Zeichen
signed char rechnen mit Vorzeichen
unsigned char rechnen ohne Vorzeichen
hat er damit einen Fehler gemacht und zahl die Rechnung dafür. Den
Fehler macht er nur ein einziges mal.
Du veranstaltest hier einen Streit um des Kaisers Bart. Wie bei so manch
anderen Dingen, wären viele Programmierer froh, wenn zb auch diese Sache
in C anders wäre. Aber die Dinge sind nun mal so wie sie sind und können
jetzt nicht mehr geändert werden.
Wenn ich nur mit Character (Char) rechne, also in Zeichenketten
aufbewaren, Vergleichen, Zeichen aufaddieren (zB 'A'+5 => 'F'), ist es
mir tatsächlich egal, wie Char intern verwendet wird.
Wenn ich aber damit "Rechne", also:
1
chara,b;
2
...
3
a=10;
4
...
5
b=5-a;
Ist es mir nicht mehr egal.
Denn entweder will ich negative Zahlen zulassen (b = -5) oder ich möchte
gerne den Überlauf haben (b = 251). Sowas möchte ich nicht dem Compiler
überlassen.
Im letzteren Fall sollte man aber eher auf int8_t bzw uint8_t
ausweichen, damit der Unterschied wiklich deutlich ist.
Karl heinz Buchegger schrieb:
> Du veranstaltest hier einen Streit um des Kaisers Bart.
Irrtum. Die Rechthaberei begann erst, als jemand ganz besonders schlau
auf 3 Typen in der Spezifikation verwies und es als Unfug und
"Halbwissen" bezeichnete, dass char immer entweder signed oder unsigned
ist.
Gastino G. schrieb:
> Es spielt für den Programmierer durchaus eine Rolle, wie der Compiler> solche Variablen definiert, denn das hat Auswirkungen auf das Programm.> Das sieht man schon an obigem Beispiel.
Irgendwie drehen wir uns im Kreis.
> Wenn bei dem Beispiel der Compiler "char" als "unsigned" definiert,> dann funktioniert sein Programm nicht mehr.
Richtig. Deshalb verwendet man char nicht zum Rechnen, und genau darauf
bezieht sich auch meine Aussage.
Christian H. schrieb:
> Wenn ich nur mit Character (Char) rechne, also in Zeichenketten> aufbewaren, Vergleichen, Zeichen aufaddieren (zB 'A'+5 => 'F'), ist es> mir tatsächlich egal, wie Char intern verwendet wird.>> Wenn ich aber damit "Rechne"
... dann hast du schon was falsch gemacht.
> Denn entweder will ich negative Zahlen zulassen (b = -5) oder ich> möchte gerne den Überlauf haben (b = 251). Sowas möchte ich nicht dem> Compiler überlassen.
Und genau deshalb nimmt man dafür auch nicht char. Warum ist das denn so
schwer zu verstehen?
Rolf Magnus schrieb:
> Und genau deshalb nimmt man dafür auch nicht char. Warum ist das denn so> schwer zu verstehen?
Warum habe ich das folgende noch als Zusatz geschrieben? Etwa nicht
gelesen?
Christian H. schrieb:
> Im letzteren Fall sollte man aber eher auf int8_t bzw uint8_t> ausweichen, damit der Unterschied wiklich deutlich ist.