Welchen Sinn hat der Datentyp "bool"?

OP #7552419
Lesenswert?

Hallo, verwende den freien Compiler XC32 von Microchip. Habe jetzt festgestellt, das ein "bool" die gleich Größe an Ram Speicher wie ein "char" verbraucht.
Warum sollten man dann überhaupt "bool" verwenden ?

Warum bekommt es ein moderner Compiler nicht hin, in ein Byte 8 bools zu packen ?????

#7552434
Lesenswert?

Ineffizient. Ein Byte oder ein Wort lesen, dann die anderen Bits ausmaskieren kostet Zeit und Code. Beim Schreiben genauso. Ausnahme: Der Prozessor hat entsprechende Bit-Befehle, etwa MSC-51 oder bei ARM Bitbanding. Letzteres wird aber nur stiefmütterlich unterstützt und ist (deshalb?) bei neueren Typen gar nicht mehr vorhanden.

#7552437
Lesenswert?

Dirk F. schrieb:

peicher wie ein "char" verbraucht.

Ja klar, was denn sonst? Speicherzellen sind numal mindestens 8 bit groß.

Dirk F. schrieb:

Warum sollten man dann überhaupt "bool" verwenden ?

Warum sollte ich mich mit CHAR mit 255 Zuständen herumschlagen, wenn bool schon vom Compiler aus nur zwei Zustände kennt?

Dirk F. schrieb:

Warum bekommt es ein moderner Compiler nicht hin, in ein Byte 8 bools zu packen ?????

Wieviel Speicher denkst Du denn, kostet es, sich zu merken, wo und an welcher Stelle der Bool ist?

#7552448
Lesenswert?

Dirk F. schrieb:

Warum sollten man dann überhaupt "bool" verwenden ?

Das dachten sich Kernigham&Ritchi auch.

Warum bekommt es ein moderner Compiler nicht hin, in ein Byte 8 bools zu packen ????

Sie bekommen es hin, alles eine Frage wie gut der Compilerschreiberling war.

https://onlinedocs.microchip.com/oxy/GUID-BB433107-FD4E-4D28-BB58-9D4A58955B1A-en-US-5/GUID-1D1337FF-1B2E-4E4A-BDF3-31254932C478.html

Entscheidend ist, pointer auf Bits zu verbieten, damit

1
*register.bit3

kein Problem wird.

(Firma: egal) #7552451
Lesenswert?

Andreas B. schrieb:

Ineffizient.

Jo! Einfach sich mal eine Makro bastel was sowas nachbildet und sich dann den Assembler output anschauen. Um 7-Bit Datenspeicher einzusparen, hat man dafür dann mehrere Byte an zusätzlichem Code bei jedem Zugriff darauf.

Im Prizip kann man sich sowas auch mit "bit field" nachbasteln, da erzeugt der Compiler dann den ganzen zusätzlichen Programmcode im Hintergrund:

Gast #7552671
Lesenswert?

Teo D. schrieb:

Dirk F. schrieb:

Warum bekommt es ein moderner Compiler nicht hin, in ein Byte 8 bools zu packen ?????

Tut er doch! ... Wenn möglich und GEWÜNSCHT. Also auf die Optimierungsstufe achten! (Standard für lau tut/tat!) (Zuletzt mit V5.x verwendet und geprüft. Nich weils nötig war, nur haben wollen, mus so sein etc. :)

Ist das Chinesisch? Lau tut tat? Bitte uebersetze doch Mal.

#7552691
Lesenswert?

Teo D. schrieb:

Tat = Schreibfehler

Klingt in diesem Kontext dann aber komplett bizarr:

Also auf die Optimierungsstufe achten! (Standard für lau tut/tat!) (Zuletzt mit V5.x verwendet und geprüft.

Ich hatte mir das so ins Hochdeutsche übersetzt:

Also auf die Optimierungsstufe achten, die [im XC8] kostenlose Standardoptimierung genügt bzw. genügte früher, als ich [Teo] das mit V5.x [des XC8] nutzte.


Oh, meine Fehlschreibung "etwass" ist peinlich, da muss die Tastatur geprelllllllllt und ich Blindfisch das nicht gesehen haben.

Gast #7552713
Lesenswert?

Warum sollten man dann überhaupt "bool" verwenden ?

Damit man quasi "natursprachlichen" Code niederschreiben kann.

OK, für einen englisch-sprecher ist der Verständnisvorteil offensichtlicher:

1
if (motorenabled)  led_red = 0xFF;  //statt 
2

3

4
if (motorenabled != 0)  led_red = 0xFF;  // oder
5

6

7
if (motorenabled == 0xFF)  led_red = 0xFF;

mit dem Schlüsselwort "then" wäre es noch "natürlicher"

Auf deutsch und auf "magic numbers" verzichtet:

1
 falls motorangeschaltet dann Rotlicht = VOLL;

aber wer lässt sich schon c-code "vorlesen" oder legt Wert darauf, das auch C-Unkundige verstehen könnten was das Programmieräffchen da zusammengekritzelt hat hat...

#7552767
Lesenswert?

Hmmm schrieb:

Dafür braucht man keine Bools, if(motorenabled) würde auch mit einem int o.dgl. funktionieren. ...

Es geht natürlich beides. Mit bool kann man in C nicht mehr machen als auch mit int oder char möglich ist.

Aber mit bool drückt man klar aus, daß man nicht mit einer von vielen möglichen Zahlen hantieren will, sondern mit "ja" oder "nein".

(Es soll Leute geben, die lesbare Programme bevorzugen.)

In C++ wird es dann interessanter, weil da mehr auf Typen geachtet wird. Bspw. beim Überladen von Funktionen kann ein Aufruf mit einer bool etwas anderes sein als ein Aufruf mit einer Zahl.

Man könnte eine Funktion, die Werte ausgibt, z.B. für bool anders überladen als für char oder int:

1
    bool b = false;
2
    char c = 'A';
3
    int  i = 42;
4
    ...
5
    print( b ); // könnte "false" ausgeben oder "no" statt 0
6
    print( c ); // könnte A ausgeben statt 65
7
    print( i ); // könnte 42 ausgeben

Oder streams (vulgo files, wie std::cout als Standardausgabe, oder eine echte Datei): Wenn man so einen stream an etwas übergibt, was einen stream erwartet, wird er halt als stream behandelt. f.write(...) würde also etwas in die Datei f schreiben, und wieder f zurückliefern. Benutzt man einen stream aber in einem logischen Ausdruck, wird er in bool konvertiert. Diese Konvertierung ist für streams aber so überladen, daß sie true ergibt, wenn der stream noch funktioniert, oder false, wenn nicht (z.B. Fehler aufgetreten). Das ermöglicht eine Konstruktion wie:

1
    while( f.read(...) ) tuwas...

, weil while einen logischen Ausdruck braucht und die Konvertierung von f nach bool den Status des streams liefert.

Wer also meint, bool ist überflüssig, der hat eigentlich recht und darf gerne ohne bool weiter herumstümpern :-)

#7552903
Lesenswert?

Teo D. schrieb:

ne Zahl >1 zugewiesen. Nich mal ne Warnung (-9) gibts

Ist ja eigentlich auch nicht schlimm:

  • Mit 1/true/!0/!false vergleicht man eh nicht
  • Warnungen macht Lint oder Co

Auf die Schnauze fällt man dann, wenn man es einem bitfeld-bool zuweist.

(Seit einiger Zeit nutze ich 0/1 statt false/true. Keine Ahnung, warum ich das geringfügig besser lesbar finde, trotz verwechselungsgefahr mit integerwert)

#7552931
Lesenswert?

Bruno V. schrieb:

Teo D. schrieb:

ne Zahl >1 zugewiesen. Nich mal ne Warnung (-9) gibts

Ist ja eigentlich auch nicht schlimm:

Aber ne Warnung wärs wert, denke ich.

Ich glaub, ein paar Synapsen tuns grad wieder... Wenn ich mich recht erinnere, ist beim XC8 bool, auch nur ein Makro mit _bit. Das in ein Byte stopfen, geht denke ich, da auch nur mit statischen.

Gast #7552983
Lesenswert?

Wladimir schrieb:

Ich denke, dass bool beim C99 Standard extra für die Pascal und Basic - Koryphäen eingeführt wurde, weil die damals auch mal C probieren wollten!

bool in Basic ? Hatte der 9kbyte grosse Interpreter des C64 überhaupt Platz für solchen Zuckerguß?

https://www.c64-wiki.de/wiki/C64-Befehle

Und klar, das "boolean" als Hochsprachenkonstrukt von Embedded Hackern die C eher als eine Art "Assembler für Warmduscher" verwenden, weitgehend ignoriert wird.

Dabei kann man mit passender Typisierung viele Fehler vermeiden bzw. schon im Compiler abfangen. Es sind schon Raketen abgestürzt wegen dem falschen typ.

#7553038
Lesenswert?

Klaus K. schrieb:

Wladimir schrieb:

Ich denke, dass bool beim C99 Standard extra für die Pascal und Basic - Koryphäen eingeführt wurde, weil die damals auch mal C probieren wollten!

bool in Basic ? Hatte der 9kbyte grosse Interpreter des C64 überhaupt Platz für solchen Zuckerguß?

1999 ist für den C64 etwas spät. Das war 5 Jahre nach EOL. Es war eher so die Zeit, als sie alle mit Visual Basic hantiert haben, das es da schon seit 8 Jahren gab.

Gast #7553055
Lesenswert?

1999 ist für den C64 etwas spät. Das war 5 Jahre nach EOL. Es war eher so die Zeit, als sie alle mit Visual Basic hantiert haben, das es da schon seit 8 Jahren gab.

Naja, wenn man "Visual Basic" meint sollte man auch "visual Basic" schreiben und nicht "BASIC". Und bei den amüsanten Vergleich zwischen C99 und "dem Rest der welt" geht es wohl eher um die Unterscheidung in "stark" und "Schwach" typisierte Sprachen. https://de.wikipedia.org/wiki/Typisierung_(Informatik)#Beispiele

Und das alle mit Visual Basic hantiert hätten ... IMHO gab es da mehr als das sprichwörtliche Gallische Dorf die ihren eigenen Zaubertrabk köchelten und damit in die Schlacht zogen: (Borland-) Pascal, ADA, Modula-2, Lisp, Forth, Erlang, ....

Wobei ohne Betrachtung des Anwendungsgebietes ist diese Einteilung IMHO allein zweckfrei. Esy geht halt darum, ob man in konkreten Anwendung Vorteile durch die Verwendung von "passenden Typen" hat. Der Prozessor selbt hat hat nur die Typen Datenbusbreite (char, int) und pointer auf Speicherzelle an Datenbus. Damit kommt dann auch der Embedded Coder besten aus.

Und dann gibt es Applikationen, die abstrakt weit weg vom Prozessor sind, beispielsweise GUI oder natursprachlich formulierte Ablaufsteuerungen, Datenbanken, ... die sind dann eher für "Hochsprachen und ihre Concepte" geeignet, da möchte man nicht die Programmlogik durch Maschinendetails "verbogen" sehen.

#7553424
Lesenswert?

Klaus K. schrieb:

Naja, wenn man "Visual Basic" meint sollte man auch "visual Basic" schreiben und nicht "BASIC".

Da stand "Basic", nicht "BASIC", und da das BASIC vom C64 zu der Zeit keine Rolle mehr gespielt hat und wie du richtig angemerkt hast, auch keinen bool-Typ hat, war das offensichtlich nicht gemeint.

Und bei den amüsanten Vergleich zwischen C99 und "dem Rest der welt" geht es wohl eher um die Unterscheidung in "stark" und "Schwach" typisierte Sprachen. https://de.wikipedia.org/wiki/Typisierung_(Informatik)#Beispiele

Es ging um die Frage, warum C99 einen Typ bool eingeführt hat. Das hat erst mal nicht soviel damit zu tun, dass C schwach typisiert ist. Es ist eher so, dass es deswegen nicht zwingend ein bool gebraucht hat und daher anfangs keins hatte. Schließlich funktioniert ja z.B. in einem if auch ein beliebiger numerischer oder Zeiger-Typ als Bedingung.

Und das alle mit Visual Basic hantiert hätten ... IMHO gab es da mehr als das sprichwörtliche Gallische Dorf die ihren eigenen Zaubertrabk köchelten und damit in die Schlacht zogen: (Borland-) Pascal, ADA, Modula-2, Lisp, Forth, Erlang, ....

Mit "alle" meinte ich alle, die irgendwas mit Basic am Hut haben, nicht alle Programmierer weltweit. C64-BASIC war 1999 nicht mehr so gefragt.

Wobei ohne Betrachtung des Anwendungsgebietes ist diese Einteilung IMHO allein zweckfrei. Esy geht halt darum, ob man in konkreten Anwendung Vorteile durch die Verwendung von "passenden Typen" hat.

Ja, deshalb hat man ja Hochsprachen.

Und dann gibt es Applikationen, die abstrakt weit weg vom Prozessor sind, beispielsweise GUI oder natursprachlich formulierte Ablaufsteuerungen, Datenbanken, ... die sind dann eher für "Hochsprachen und ihre Concepte" geeignet, da möchte man nicht die Programmlogik durch Maschinendetails "verbogen" sehen.

Wobei es natürlich trotzdem meist gewisse Randbedingungen gibt.

#7555470
Lesenswert?

Dirk F. schrieb:

Warum sollten man dann überhaupt "bool" verwenden ?

Damit man Hochsprachencode mit "true" und "false". schreiben kann. Mir fällt es aber trotzdem leichter, logische Operationen z.B. in Bitfeldern zu verarbeiten.

Von Flags, logischen Operationen mit Registern, um diese zu setzen, und lustigem Hin- und Herspringen willst du lieber nichts wissen.. ;) Abfragen wie or ax,ax gleich an den Anfang einer (bestimmten) Schleife zu setzen, hatte mir mal ein professioneller Programmierer beigebracht. Der Code von dem sah im Debugger auch super aus. Aber der konnte auch gut coden.

Von der Performance her sind auch Algorithmen sehr beliebt, wie auch effektiv.

Beitrag #7555481 wurde von einem Moderator gelöscht.
#7555496
Lesenswert?

Irgendwie übersehen hier alle, dass ein C99 bool die tolle Eigenschaft hat, dass er immer 0 oder 1 ist. Man braucht auch beim Casten von größeren Datentypen keine Angst zu haben, dass nur die unteren Bits beachtet werden. 0x40000 in einen bool mit sizeof(bool) == 1 zu casten, liefert 1. Das gleiche in einen uint8_t zu casten, liefert 0.

Gast #7555509
Lesenswert?

Daniel G. schrieb:

Irgendwie übersehen hier alle, dass ein C99 bool die tolle Eigenschaft hat, dass er immer 0 oder 1 ist. Man braucht auch beim Casten von größeren Datentypen keine Angst zu haben, dass nur die unteren Bits beachtet werden. 0x40000 in einen bool mit sizeof(bool) == 1 zu casten, liefert 1. Das gleiche in einen uint8_t zu casten, liefert 0.

Tja, wer zu doof zum casten ist, sollte es lassen.

1
uint8_t KleinerDatentyp = GrosserDatentyp ? 1 : 0;
Gast #7555637
Lesenswert?

Daniel G. schrieb:

Irgendwie übersehen hier alle, dass ein C99 bool die tolle Eigenschaft hat, dass er immer 0 oder 1 ist. Man braucht auch beim Casten von größeren Datentypen keine Angst zu haben, dass nur die unteren Bits beachtet werden.

Ein boolean wird nie gecastet, ein boolean wird nur in Bedingungsoperatoren wie if () etc verarbeitet. Siehe auch Beitrag "Re: Welchen Sinn hat der Datentyp "bool"?" und Matthäus 5,37 .

Beitrag #7556325 wurde von einem Moderator gelöscht.
#7556941
Lesenswert?

Bruno V. schrieb:

Hast Du mal ein Beispiel? Oder geht es nur um Assembler-Hacks

Es geht nur um eine Bedingungsprüfung. Du könntest auch IF(ax) schreiben.

In Asm geht aber die Schleifenkonstruktion etwas anders als in C, und Asm-Schleifen sind auch nicht so stark standardisiert. Beim Intel gibt es aber Befehlserleichterungen wie loop oder repz und auch die Register wie SI und DI z.B. für Richtungsabarbeitungen. Mit "Hacks" hat das nichts zu tun. Mit in logischen Operationen gut sein, aber schon.

#7561101
Lesenswert?

Warum sollten man dann überhaupt "bool" verwenden ?

Warum sollte ich mich mit CHAR mit 255 Zuständen herumschlagen, wenn bool schon vom Compiler aus nur zwei Zustände kennt?

Jo, genau - hilft zur Vermeidung von Seiteneffekten bei z.B. Rückgabewerten.

Angenommen musst nen Test schreiben, und musst potentiell alle Restklassen eines chars durchklappern, weil jemand auf Platz optimiert hat. Kann zwar auch sinnvoll sein, kommt aber drauf an. Pauschal da nur 2 Zustände brauchen zu müssen, könnte man halt irgendwie dezent als etwas sauberer erachten.

#7567123
Lesenswert?

ich hätte ja nichts dagegen, wenn ein bool auch tatsächlich ein bool wäre.

Ich habe aber noch keinen (C-) Compiler gesehen, der bei

1
static bool b = 12;

einen Fehler oder auch nur eine Warnung ausgespuckt hätte. So ist das keine Spur besser als

1
#define bool int
#7567288
Lesenswert?

Markus F. schrieb:

ich hätte ja nichts dagegen, wenn ein bool auch tatsächlich ein bool wäre.

Ein bool ist ein bool ist ein bool.

Ich habe aber noch keinen (C-) Compiler gesehen, der bei static bool b = 12; einen Fehler oder auch nur eine Warnung ausgespuckt hätte.

Das liegt aber nicht daran, dass es kein bool wäre, sondern daran, dass ein int implizit nach bool konvertiert werden kann. In b steht nachher nämlich trotzdem keine 12 drin.

So ist das keine Spur besser als #define bool int

Doch, denn da stünde nachher 12 drin.

#7582656
Lesenswert?

Rainer W. schrieb:

b=12 ist auch true

Das ist missverständlich: Alles != 0 (also auch 12) wird zu true gewandelt, wenn eine boolsche Entscheidung gebraucht wird (z.B. bei if, while, ...).

if(b==true) ist hingegen nur erfüllt, wenn b ein bool ist, nicht wenn es ein int ist.

Bei if & Co behilft man sich, indem man niemals auf true vergleicht (was den code oft besser macht). Bei Zuweisungen erfordert es jedoch Klimmzüge:

1
int     myBool_b = 256;
2
uint8_t myBool_c = b;   /* geht schief */
3
uint8_t myBool_d = !!b; /* Klimmzug */
Gast #7582695
Lesenswert?

Wf88 schrieb:

Wie ist das vorgegeben? Meckert der compiler dann, oder macht er einfach still ein TRUE (1) aus der 12?

Er macht ein TRUE (1) aus der 12.

Der Vorteil von bool (oder BOOL) ist, dass die Absicht des Programmierers klar ist, unabhängig ob der darunterliegende Datentyp ein INT oder ein BYTE ist.

// Hier weiss ich dass die Funktion TRUE liefert, wenn alles passt. // Im Fehlerfall wird FALSE zurückgeliefert. BOOL doSomething()

// Hier liefert die Funktion üblicherweise 0, wenn alles passt. // Im Fehlerfall wird ein Fehlercode zurückgeliefert. INT doSomething()

#7582696
Lesenswert?

Wf88 schrieb:

Rolf M. schrieb:

b=12 geht aber eben nicht, weil b ein bool ist, der den Wert 12 nicht annehmen kann.

Wie ist das vorgegeben? Meckert der compiler dann, oder macht er einfach still ein TRUE (1) aus der 12?

Eine Konvertierung von int nach bool macht wie oben angegeben aus 0 ein false, aus allem anderen ein true. Umgekehrt macht eine Konvertierung von bool nach int aus false eine 0 und aus true eine 1. Dabei können solche Konvertierungen implizit durchgeführt werden, also "still". Folgendes C-Programm gibt daher 1 aus:

1
#include <stdio.h>
2
#include <stdbool.h>
3
int main()
4
{
5
    int i = 12;
6
    bool b = i;
7
    int j = b;
8
    printf("%d\n", j);
9
}
#7582714
Lesenswert?

Rolf M. schrieb:

Dabei können solche Konvertierungen implizit durchgeführt werden, also "still". Folgendes C-Programm gibt daher 1 aus:

Die Konvertierungen können nicht nur implizit, also "still" durchgeführt werden, die werden grundsätzlich implizit, also "still" durchgeführt. Was dann zu der oben angemoserten Eigenschaft führt, das der Compiler nicht warnt.

Oliver

#7582811
Lesenswert?

Oliver S. schrieb:

Rolf M. schrieb:

Dabei können solche Konvertierungen implizit durchgeführt werden, also "still". Folgendes C-Programm gibt daher 1 aus:

Die Konvertierungen können nicht nur implizit, also "still" durchgeführt werden, die werden grundsätzlich implizit, also "still" durchgeführt.

Man kann sie per Cast durchaus auch explizit machen, wenn man das will:

1
   bool b = (bool)i;

Aber es ist in dem Fall nicht nötig, weil die implizite Konvertierung genauso geht.

Peter D. schrieb:

Oder bei getchar(): 0 .. 0xFF = gültiges Datenbyte EOF (-1) = end of file

Was überaus ungeschickt gemacht ist, denn char kann auch signed sein, also einen Wertebereich von -0x80 bis 0x7F haben.

Moderator Persönliche Seite #7583426
Lesenswert?

Rolf M. schrieb:

Was überaus ungeschickt gemacht ist, denn char kann auch signed sein, also einen Wertebereich von -0x80 bis 0x7F haben.

Der Return-Wert von getchar() ist int. Schon der erste Kernighan-Ritchie benutzte

1
  int c = getchar();

Ein char ist hier überhaupt nicht im Spiel.

Das K&R-Kopierprogramm

1
int main ()
2
{
3
    int c;
4

5
    while ((c = getchar()) != EOF)
6
    {
7
        putchar (c)
8
    }
9
    return 0;
10
}

funktioniert auch anstandslos auf Systemen, wo char signed sein kann. Die Verwendung von char in der Zuweisung wäre aber hier ein Fehler - nämlich des Programmierers, nicht der Bibliotheksfunktion.

#7583438
Lesenswert?

Doch, das mit dem Wertebereich von Char ist tatsächlich ein Problem, egal, ob ich die jetzt in ein int oder ein char packe. Ich konvertiere die dann immer erstmal nach unsigned char, bevor ich die weiter auswerte. Mir ist es schon passiert, dass ich ein Case in einem Switch hatte, und dann war das Zeichen negativ!

Der Wert von EOF ist aber auch Implementationsspezifisch, der wird fast überall auf was ausserhalb vom char Bereich gesetzt, egal ob char nun in der Implementation signed oder unsigned ist. Der Wert von EOF ist also in der Regel kein Problem.

Blöd ist nur, wenn man im selben Switch EOF und 0x80 prüfen will. Dann kann man nicht einfach nach "unsigned char" casten. Ok, '\x80' müsste dann eigentlich gehen. Aber so einen Fehler muss man erst mal finden!

Moderator Persönliche Seite #7583439
Lesenswert?

Udo K. schrieb:

else if (c == 'ä')

Bei ISO8859 funktioniert das durchaus. c hat dann nämlich einen positiven Wert, nämlich 0xE4 - auch wenn auf dem System char signed wäre. Beachte, dass Du "int c" geschrieben hast, nicht "char c".

getchar() liefert folgenden Wertebereich:

0 bis 255, -1 bei EOF - auch auf Systemen, wo char signed ist.

Ich wiederhole: bei getchar() ist überhaupt kein char im Spiel!

Moderator Persönliche Seite #7583445
Lesenswert?

Daniel A. schrieb:

Mir ist es schon passiert, dass ich ein Case in einem Switch hatte, und dann war das Zeichen negativ!

Natürlich kann das passieren, wenn Du char als Type verwendest. Das hat aber nichts mit getchar() zu tun.

Ich antwortete auf folgenden Satz:

Rolf M. schrieb:

Was überaus ungeschickt gemacht ist, denn char kann auch signed sein, also einen Wertebereich von -0x80 bis 0x7F haben.

Dieser bezog sich auf getchar(). Und darauf habe ich geantwortet. Dass Du jetzt darauf ansprichst, weil Du mal den Fehler gemacht hast, einen signed char buffer zu verwenden, hat mit getchar() überhaupt nichts zu tun.

Wenn Du Zeichen speichern(!) willst, dann natürlich unsigned char:

1
int main ()
2
{
3
    unsigned char buffer[20];    // Hier unsigned char verwenden!
4
    int           c;             // hier int verwenden!
5
    int           i = 0;
6

7
    while ((c = getchar ()) != EOF)
8
    {
9
        buffer[i++] = c;
10
        if (i == 20 - 1)
11
        {
12
            exit(1);  // zu lang
13
        }
14
    }
15
    buffer[i] = '\0';
16
    printf ("Die ersten 20 Zeichen: %s\n", buffer);
17
}

Aber zum Auswerten des Rückgabewertes von getchar() brauchst Du weiterhin ein int!

Moderator Persönliche Seite #7583452
Lesenswert?

Daniel A. schrieb:

Ich hab gerade nochmal nachgesehen, was der Standard sagt. Die Zeichen die getchar zurückgibt sind tatsächlich als ob sie vorher nach unsigned char konvertiert wurden. Und EOF muss negativ sein, aber es muss nicht zwingend -1 sein.

Eben. Wenn Du eine int-Variable nimmst, um den Rückgabewert von getchar() zu testen, bist Du immer im grünen Bereich. Von daher ist gar nichts "ungeschicktes" an der Funktion getchar(). Diese hat 1970 schon anstandslos mit Binärdateien funktioniert und funktioniert auch heute noch mit 8-Bit-Zeichensätzen wie ISO8859 oder UTF-8.

#7583467
Lesenswert?

Frank M. schrieb:

Daniel A. schrieb:

Mir ist es schon passiert, dass ich ein Case in einem Switch hatte, und dann war das Zeichen negativ!

Natürlich kann das passieren, wenn Du char als Type verwendest. Das hat aber nichts mit getchar() zu tun.

Das war was anderes. Den Fehler mache ich definitiv nie. War wohl das Zeichen positiv, aber die Zeichenkonstante negativ.

Gast #7583690
Lesenswert?

Frank M. schrieb:

Bei ISO8859 funktioniert das durchaus. c hat dann nämlich einen positiven Wert, nämlich 0xE4 - auch wenn auf dem System char signed wäre. Beachte, dass Du "int c" geschrieben hast, nicht "char c".

Mein Verständnis ist: Char 'ä' ist signed ((char)0xE4 < 0), und wird implizit auf int gewandelt, damit habe ich eine negative Zahl. Der Rückgabewert von getchar ist aber immer positiv oder -1. Der Vergleich wird also schiefgehen. Aber für ASCII Zeichen, die ja alle positiv sind, passt die Funktion. Negative Zeichenkonstanten muss man auf (unsigned char)'ä' wandeln. Das kommt aber in der Praxis selten vor.

#7583809
Lesenswert?

Udo K. schrieb:

Mein Verständnis ist: Char 'ä' ist signed ((char)0xE4 < 0), und wird implizit auf int gewandelt, damit habe ich eine negative Zahl. D

"Hällo" ist ein char Array. Und dort kann das zweite Element negativ oder positiv sein (halt wie char implementiert ist, als signed oder unsigned)

'ä' ist ein integer. Und positiv, wenn es "im erweiterten ASCII" liegt.

Wenn ich 'a' nun einem char zuweise, dann wird es in diesem char halt als signed oder unsigned interpretiert, aber erst dort.

#7583816
Lesenswert?

Frank M. schrieb:

Rolf M. schrieb:

Was überaus ungeschickt gemacht ist, denn char kann auch signed sein, also einen Wertebereich von -0x80 bis 0x7F haben.

Der Return-Wert von getchar() ist int. Schon der erste Kernighan-Ritchie benutzte int c = getchar();

Ja, das ist auch der korrekte Weg.

Wf88 schrieb:

Udo K. schrieb:

'ä'

Weil für ein 'ä' brauchst du anderes Charset als das default.

Frank M. schrieb:

Rolf M. schrieb:

Was überaus ungeschickt gemacht ist, denn char kann auch signed sein, also einen Wertebereich von -0x80 bis 0x7F haben.

Dieser bezog sich auf getchar(). Und darauf habe ich geantwortet. Dass Du jetzt darauf ansprichst, weil Du mal den Fehler gemacht hast, einen signed char buffer zu verwenden, hat mit getchar() überhaupt nichts zu tun.

Wenn Du Zeichen speichern(!) willst, dann natürlich unsigned char:

Wie "natürlich"? Der Typ für Text ist char. Alle Stringfunktionen wie printf() oder strcmp() arbeiten mit Arrays aus char, nicht aus unsigned char. Explizit sollte man die signedness nur angeben, wenn man damit rechnen oder Bitgefummel machen will, denn Text hat eigentlich keine Signedness.

Aber zum Auswerten des Rückgabewertes von getchar() brauchst Du weiterhin ein int!

Ja. Das Problem ist: char ist eigentlich der Texttyp. Wenn der signed ist, dann muss getchar() den Wert erst in einen unsigned char wandeln, was auch im Standard so steht. Spannender wird es z.B. bei toupper(). Sagen wir mal, ich möchte mit fgets() (das auch mit einem char* arbeitet) einen String einlesen und diesen dann per toupper() zeichenweise in Großbuchstaben wandeln:

1
char c[100];
2
fgets(c, 100, stdin);
3
for (size_t i = 0; c[i] != '\0'; ++i)
4
    c[i] = toupper(c[i]); // undefined behavior!

An toupper() dürfen nämlich außer EOF keine negativen Werte übergeben werden. Man muss erst nach unsigned char casten.

Frank M. schrieb:

Daniel A. schrieb:

Ich hab gerade nochmal nachgesehen, was der Standard sagt. Die Zeichen die getchar zurückgibt sind tatsächlich als ob sie vorher nach unsigned char konvertiert wurden. Und EOF muss negativ sein, aber es muss nicht zwingend -1 sein.

Eben.

Und genau das ist es, was ich meinte.

Udo K. schrieb:

Frank M. schrieb:

Bei ISO8859 funktioniert das durchaus. c hat dann nämlich einen positiven Wert, nämlich 0xE4 - auch wenn auf dem System char signed wäre. Beachte, dass Du "int c" geschrieben hast, nicht "char c".

Mein Verständnis ist: Char 'ä' ist signed ((char)0xE4 < 0), und wird implizit auf int gewandelt,

In C ist 'ä' bereits vom Typ int. In C++ ist es vom Typ char, muss aber so wie du sagst für den Vergleich erst nach int konvertiert werden (Integer-Promotion).

damit habe ich eine negative Zahl. Der Rückgabewert von getchar ist aber immer positiv oder -1. Der Vergleich wird also schiefgehen.

Das ist richtig.

Aber für ASCII Zeichen, die ja alle positiv sind, passt die Funktion. Negative Zeichenkonstanten muss man auf (unsigned char)'ä' wandeln.

Oder halt umgekehrt den Rückgabewert von getchar() nach char wandeln.

Das kommt aber in der Praxis selten vor.

Heutzutage mit UTF-8 gibt's kein 'ä' mehr.

Bruno V. schrieb:

'ä' ist ein integer. Und positiv, wenn es "im erweiterten ASCII" liegt.

Nein, eben nicht. Wenn char signed ist, ist 'ä' ein int mit negativem Wert!

#7585828
Lesenswert?

Der C-Standard sagt, dass es alle Integer-Typen in einer signed- und einer unsigned-Variante gibt. Dabei gilt, dass der Default, wenn man nichts explizit angibt, immer signed ist - außer für char. Da ist es implementation-defined, also der Compiler darf das entscheiden. Aus Gründen der Konsistenz mit den anderen Integer-Typen ist char deshalb zumindest bei gcc meist signed. Da es aber eben compilerabhängig ist, sollte an sich die Verwendung am besten so sein:

signed char - für einen kleinen Integer mit Vorzeichen unsigned char - für einen kleinen Integer ohne Vorzeichen char - für Text

Allerdings scheinen manche ein Problem damit zu haben, wenn Textzeichen in einem signed-Typ gespeichert werden, obwohl das streng genommen egal sein sollte. Text braucht nur genug Bits, um ein Zeichen zu speichern. Wie diese Bits verwendet würden, wenn man damit rechnen würde, spielt ja keine Rolle. Auf der anderen Seite sind dann noch die oben genannten Funktionen, die wie schon gesagt eher ungeschickt definiert sind und nicht sonderlich gut zu einem signed-char passen. Daher wird der eine oder andere mit dieser Option char auf unsigned zwingen. Am Ende schreibe ich aber meine Programme lieber so, dass es keine Rolle spielt, ob char signed oder unsigned ist und damit auch dieser Kommandozeilenparameter überflüssig ist.

#7586840
Lesenswert?

Andreas B. schrieb:

Ineffizient. Ein Byte oder ein Wort lesen, dann die anderen Bits ausmaskieren kostet Zeit und Code. Beim Schreiben genauso. Ausnahme: Der Prozessor hat entsprechende Bit-Befehle, etwa MSC-51 oder bei ARM Bitbanding. Letzteres wird aber nur stiefmütterlich unterstützt und ist (deshalb?) bei neueren Typen gar nicht mehr vorhanden.

C167 hatte auch Bitbefehle, da hätte das Sinn gemacht. Ist schon so lange her...ich weiß nicht mehr, ob der C-Compiler diese Befehle genutzt hat. Ansonsten ist die kleinste Zugriffseinheit bei den Prozessoren 1 Byte, 1 Wort, 1 Doppelwort usw...., abhängig von der Breite des Datenbus. Wie Andreas schrieb, würde es Code und Zeit kosten die Bits innerhalb eines Bytes zu maskieren. bool macht trotzdem Sinn in C: FALSE = 0; TRUE = !FALSE (also alles außer 0)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren