Forum: PC-Programmierung C Fragen über Fehlerausgabe und Datentypen


von inkrement (Gast)


Lesenswert?

Hallo liebe Forenuser,
ich habe mir wegen einer Lehrveranstaltung C angeeignet, doch leider 
habe ich noch genug offene Fragen und hoffe, dass ihr mir weiterhelfen 
könnt.

1) Fehlerausgabe.
Ich habe mittlerweile unzählige Funktionen zur Fehlerausgabe gesehen: 
fprintf(sterr,...), fatal, perror, ... doch welche soll man nun nutzen 
und wo sind ihre Vor/Nachteile?

2) Datentypen
Ich habe gelesen, dass es eigentlich keine Bool-Variablen gibt und man 
auf int zurückgreifen soll. Aber int hat (ja nach PC) 32bit, aber ich 
brauche doch nur 1 Bit? Auch wenn es in der heutigen Zeit meist egal ist 
- wie könnte ich nur ein bit allozieren?

Weiters muss ich für eine Übung einen Server schreiben, der ein eigenes 
Protokoll verwendet. Doch hier brauche ich nur 2 Byte, wobei ich auf 
jedes Bit zugreifen können muss - welchen Datentyp würdet ihr hier 
verwenden? Gibt es irgendwie eine Art bit-Array?

danke!

von (unknown) (Gast)


Lesenswert?

inkrement schrieb:
> 1) Fehlerausgabe.
> Ich habe mittlerweile unzählige Funktionen zur Fehlerausgabe gesehen:
> fprintf(sterr,...), fatal, perror, ... doch welche soll man nun nutzen
> und wo sind ihre Vor/Nachteile?

Solange nichts dagegen spricht, fprintf( stderrr... - das ist portabel 
und gibt aus.

perror() gibt Text zum aktuellen Wert der errno-Variable aus, ist also 
nicht für allgemeine Ausgaben geeignet.

>
> 2) Datentypen
> Ich habe gelesen, dass es eigentlich keine Bool-Variablen gibt und man
> auf int zurückgreifen soll. Aber int hat (ja nach PC) 32bit, aber ich
> brauche doch nur 1 Bit? Auch wenn es in der heutigen Zeit meist egal ist
> - wie könnte ich nur ein bit allozieren?

1 Bit geht gar nicht.
Auf Rechner mit knappem Speicher wird int auch keine 32 Bit haben, 
sondern nur 8 oder 16.

Wenn man geizen will, kann man uint8_t nehmen, weniger geht nicht ohne 
Klimmzüge.
Und den gesparten Speicher erkauft man sich gelegentlich mit langsamerem 
Programmlauf, je nach System.

von inkrement (Gast)


Lesenswert?

(unknown) schrieb:
> 1 Bit geht gar nicht.
> Auf Rechner mit knappem Speicher wird int auch keine 32 Bit haben,
> sondern nur 8 oder 16.
>
> Wenn man geizen will, kann man uint8_t nehmen, weniger geht nicht ohne
> Klimmzüge.
> Und den gesparten Speicher erkauft man sich gelegentlich mit langsamerem
> Programmlauf, je nach System.

Ok, Danke. Dann werde ich für die zwei Bytes uint16_t verwenden, aber 
wie könnte ich da auf jedes Byte einzeln zugreifen?

von Martin (Gast)


Lesenswert?

inkrement schrieb:
> Ich habe gelesen, dass es eigentlich keine Bool-Variablen gibt und man

_Bool existiert, ebenso stdbool.h.

> auf int zurückgreifen soll. Aber int hat (ja nach PC) 32bit, aber ich
> brauche doch nur 1 Bit? Auch wenn es in der heutigen Zeit meist egal ist
> - wie könnte ich nur ein bit allozieren?

Gar nicht. In C ist die Grundeinheit des Speicherzugriffs das Byte. Und 
das sind eben mindestens 8 Bit.

Im übrigen wird das ganze nur ineffizienter, wenn du versuchst, 
unterhalb der "natürlichen" Zugriffsbreite des Prozessors 
rumzuwurschteln.

von Klaus W. (mfgkw)


Lesenswert?

Da gibt es mehrere Möglichkeiten:
1. Mit Bitmaske
1
-include <stdint.h>
2
...
3
    uint16_t   derWert = ...;
4
5
    /* Testen, ob Bit 3 (gezählt ab 0) gesetzt ist: */
6
    if( derWert & (1<<3) )
7
    {
8
        /* dann Bit 4 setzen: */
9
        derWert |= 1<<4;
10
    }
Dazu steht auch mehr im gcc-Tutorial, siehe links oben.

2. Man kann sich Bitfelder definieren, z.B.
1
    typedef struct
2
    {
3
       unsigned bit0: 1;
4
       unsigned bit1: 1;
5
       unsigned bit2: 1;
6
       unsigned bit3: 1;
7
       unsigned bit4: 1;
8
       unsigned bit5: 1;
9
       unsigned bit6: 1;
10
       unsigned bit7: 1;
11
       unsigned bit8: 1;
12
       unsigned bit9: 1;
13
       unsigned bit10: 1;
14
       unsigned bit11: 1;
15
       unsigned bit12: 1;
16
       unsigned bit13: 1;
17
       unsigned bit14: 1;
18
       unsigned bit15: 1;
19
    }
20
    zweiByte_bitweise_t;
21
....
22
23
    zweiByte_bitweise_t derwert;
24
    /* Testen, ob Bit 3 (gezählt ab 0) gesetzt ist: */
25
    if( derWert.bit3 )
26
    {
27
        /* dann Bit 4 setzen: */
28
        derWert.bit4 = 1;
29
    }

von Klaus W. (mfgkw)


Lesenswert?

Martin schrieb:
> _Bool existiert, ebenso stdbool.h.

Dann musst du aber auch sagen, nach wessen Hausstandard du das sagst.

von Klaus W. (mfgkw)


Lesenswert?

...

3. Man kann mit einer union verschiedene Sachen Typen an derselben 
Speicherstelle übereinander legen, z.B. eine uint16_t und ein Feld von 2 
uint8_t.
Nur in Grenzen empfehlenswert; man sollte sissen was man tut.

4. Mit Speicherakrobatik, dto..

von Klaus W. (mfgkw)


Lesenswert?

Klaus Wachtler schrieb:
> -include <stdint.h>

soll heißen:
#include <stdint.h>

Klaus Wachtler schrieb:
> zweiByte_bitweise_t derwert;

Groß-/Kleinschreibung sollte man schon beachten, also lieber derWert...

von Stefan E. (sternst)


Lesenswert?

Klaus Wachtler schrieb:
> Martin schrieb:
>> _Bool existiert, ebenso stdbool.h.
>
> Dann musst du aber auch sagen, nach wessen Hausstandard du das sagst.

Was meinst du mit "Hausstandard"? Das ist C-Standard (C99).

Aber natürlich belegt _Bool auch nicht nur ein Bit.

von inkrement (Gast)


Lesenswert?

Danke für die vielen Antworten. Bis auf eine weitere Frage wurden alle 
Unklarheiten beseitigt :)

Wenn ich jetzt 2 Bytes (in einem eigenem Protokoll) per TCP verschicken 
soll und der Server nun "pro zug" die 2 Bytes in einem uint16_t 
speichern soll, wie kann ich das machen?

... denn mit recv bekomme ich ja nur char für char oder ein chararray, 
doch ich will doch kein char. Muss ich das jetzt mit einem anderen 
Befehl machen, oder irgendwie casten? Also einen Buffer von 2 Chars 
empfangen und dann in einen uint16_t umwaldeln?

 Weiters wird es ja dann auch auf die Byte-Reihenfolge ankommen, da ich 
ja einen Intel x86 Prozessor verwende?!

von Klaus W. (mfgkw)


Lesenswert?

recv() liest zwar Zeichen fpr Zeichen, bekommt aber sinnvollerweise 
einen Zeiger für den Puffer übergeben vom Typ void*.
An dieser Stelle kann man auch die Adresse einer uint16_t übergeben, als 
Länge dann natürlich 2.

Allerdings muß man hier aufpassen, ob die beiden Systeme die gleiche 
"Endianness" haben.
Es gibt "big endian" und "little endian" Systeme.
Bei LE (Intel bzw. AMD, AVR) liegen die 8 niederwertigen Bits an der 
niedrigeren der beiden belegten Adressen, die 8 höherwertigen an der 
höheren; bei BE (Motorola und alle anderen richtigen Systeme) genau 
umgekehrt.
Wenn man ab &einerVariable in einem LE-System schreibt und mit recv() 
oder anderen entsprechend auf einem BE-System liest oder umgekehrt, dann 
sind die beiden Byte vertauscht.
Also entweder auf einem BE-System immer tauschen beim Schreiben und 
Lesen oder immer auf einem LE-System, oder gar nicht binär übertragen, 
sondern in Text wandeln und den übertragen.

von inkrement (Gast)


Lesenswert?

Ich habe wieder einiges weitergebracht, aber bei dem Empfang der zwei 
Bits mit recv weiß ich noch nicht ganz, was das Beste wäre.

Die Funktion (recv) erhält ja nicht immer die geforderten 2 Bytes, 
sondern zwischen 1 und 2. Daher habe ich mir gedacht, dass ich einen 
uint8_t Buffer mit zwei Werten mache und solange recv mit jeweils 1 Byte 
aufrufe, bis dieser Buffer gefüllt ist. Damit sollte ja auch der Fall, 
dass ich nicht die zwei Bytes auf einmal bekomme, verhindert sein, oder?

Doch nun habe ich ein Array mit zwei uint8_t Werten - wie kann ich die 
zu einem uint16_t konvertieren und dabei Little/Big Endian beherzigen? 
Ich habe schon versucht den array-"Pointer" zu casten, doch da wollte 
der Compiler nicht mitspielen.

Gedacht hätte ich, dass ich in ein uint16_t konvertiere und dann mit 
ntohs Little/Big Endian Probleme beseitige. Durch TCP sollte ja auch die 
Reihenfolge der eintreffenden Bits richtig sein - oder wird der Plan 
nicht aufgehen?!

von Klaus W. (mfgkw)


Lesenswert?

inkrement schrieb:
> Gedacht hätte ich, dass ich in ein uint16_t konvertiere und dann mit
> ntohs Little/Big Endian Probleme beseitige. Durch TCP sollte ja auch die
> Reihenfolge der eintreffenden Bits richtig sein - oder wird der Plan
> nicht aufgehen?!

TCP direkt korrigiert gar nichts.
ntohs() etc. würden gehen, die wirst du aber auf einem AVR gar ncht 
haben.
Es hängt halt davon ab, um welche Systeme es überhaupt geht.

- Systeme bekannt: also auch LE/BE bekannt, bei Bedarf manuell 
konvertieren
- nicht bekannt/soll portabel sein:
  - ntohs() etc. nehmen, oder
  - gar nicht die uint16_t direkt aus dem Speicher schreiben/lesen,
    sondern gezielt erst das eine Byte senden, dann das andere
    ( (i&0xff) senden, dann ((i>>8)&0xFF) senden; beim Empfänger
    (erstesGelesenesByte|(zweitesGelesenesByte<<8)) bilden, oder
    umgekehrt ). Dadurch wird die Endianness egal. Oder:
  - Auf das ganze Binärgedöns verzichten und in Text wandeln,
    senden, beim Empfänger aus dem Text wieder zur Zahl wandeln.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.