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!
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.
(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?
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.
...
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..
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...
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.
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?!
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.
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?!
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.