Hallo zusammen
Normalerweise (beim AVR) hat ja ein
1
int
2bytes
Davon bin ich nun auch beim STM32 ausgegangen, und hab mir ein strukt
angelegt.
Leider hat der int nun aber 4bytes.
Ich benötige diesen wertebereich jedoch nicht.
2bytes genügen.
Gibt es eine möglichkeit, einen Datentypen mit 2bytes zu definieren
oder ist bereits einer vorhanden?
Danke
Linuxi schrieb:> Es gibt bestimmt sowas wie uint16_t oder so.>> Int hat unterschiedliche größen je nach Zielsystem.
uint16_t kennt der compiler nicht :)
Ist ARM-GCC mit der CooCox IDE
MZ schrieb:> binde mal folgende Headerdatei ein:>> #include <stdint.h>>> dort sind die uint8_t, uint16_t,... definiert
hab ich gerade gemacht.
Aber uint_16 ist nicht drinn (siehe bild)
A. K. schrieb:> Uli schrieb:>> "short" ist immer 2 Byte.>> Falsch.
Ok also inzwischen, weiss ich was ich alles nicht nehmen kann.
Aber was ist denn nun 2bytes gross?
Uli schrieb:> "short" ist immer 2 Byte.
Es gilt:
char <= short <= int <= long <= long long
Mit folgenden Einschränkungen:
- char ist der kleinste unterstützte Datentyp
- short und int sind mindestens 16 Bit breit
- long ist mindestens 32 Bit breit
- long long ist mindestens 64 Bit breit
- Die Grösse aller Datentypen sind ein Vielfaches der Grösse von char
Tim schrieb:> Ok also inzwischen, weiss ich was ich alles nicht nehmen kann.> Aber was ist denn nun 2bytes gross?
Ein uint16_t ist genau 2 Bytes gross. Das "Problem" an den Datentypen
aus stdint.h ist, dass die (u)intXX_t laut Standard Typen nicht
definiert werden müssen (und das ist auch gut so), die (u)int_fastXX_t
und (u)int_leastXX_t Typen sehr wohl. Das sind die Beiden Typen, die
auch verwendet werden sollten. Die (u)intXX_t werden normalerweise nur
benötigt, wenn die exakte Breite aufgrund von Einschränkungen der
Hardware zwingend nötig ist.
Die Frage ist also: Benötigst du wirklich genau 16 Bit, oder tun es auch
mindestens 16 Bit?
Wer hat denn das mit dem short umdefiniert?
Dann hoffe ich mal der char immer noch nur ein Byte ist und long immer
noch 4 Byte sind.
Das Int >= 2 Byte ist, sollte ja klar sein.
Uli schrieb:> ... und long immer noch 4 Byte sind.
Falsch. Im 64-Bit Programmiermodell von Linux und diversen Unixen ist
"long" 64 Bits breit.
> er hat denn das mit dem short umdefiniert?
Dennis Ritchie.
be stucki schrieb:> Die Frage ist also: Benötigst du wirklich genau 16 Bit, oder tun es auch> mindestens 16 Bit?
Ja,
da ich ein Strukt definiere und dieses strukt wird über einen
definierten buffer gelegt.
Wenn nun also int variier im strukt, so habe ich die falschen daten...
Uli schrieb:> Wer hat denn das mit dem short umdefiniert?
Niemand, das war noch nie so definiert.
> Dann hoffe ich mal der char immer noch nur ein Byte ist
Ja.
Können aber mehr als 8 Bit sein...
> und long immer noch 4 Byte sind.
War auch noch nie so definiert.
> Das Int >= 2 Byte ist, sollte ja klar sein.
Das war dann wohl ein Glückstreffer.
be stucki schrieb:> Uli schrieb:>> "short" ist immer 2 Byte.>> Es gilt:> char <= short <= int <= long <= long long>> Mit folgenden Einschränkungen:> - char ist der kleinste unterstützte Datentyp
und mindestens 8 Bit breit
> - short und int sind mindestens 16 Bit breit> - long ist mindestens 32 Bit breit> - long long ist mindestens 64 Bit breit> - Die Grösse aller Datentypen sind ein Vielfaches der Grösse von charbe stucki schrieb:> - Die Grösse aller Datentypen sind ein Vielfaches der Grösse von char>> Tim schrieb:>> Ok also inzwischen, weiss ich was ich alles nicht nehmen kann.>> Aber was ist denn nun 2bytes gross?>> Ein uint16_t ist genau 2 Bytes gross.
Er kann auch 1 Byte groß sein. Ist er auf dieser Plattform aber nicht.
> Die Frage ist also: Benötigst du wirklich genau 16 Bit, oder tun es auch> mindestens 16 Bit?
Und danach kommt dann die Frage: Soll der Typ möglichst klein oder
möglichst schnell sein? Danach entscheidet man, ob least oder fast.
Rolf Magnus schrieb:> Er kann auch 1 Byte groß sein. Ist er auf dieser Plattform aber nicht.
Stimmt. Denn in C ist ein Byte gleich gross wie ein char.
Tim schrieb:> da ich ein Strukt definiere und dieses strukt wird über einen> definierten buffer gelegt.
Ich nehme mal an, dass du der selbe Tim bist, wie in folgendem Beitrag:
Beitrag "Strukt hat unterschiedliche länge"
Daher gibts von mir wieder die gleiche Antwort:
be stucki schrieb:> Tim schrieb:>> bmp_f_hdr = (struct bmp_file_header *)&ucBuffer[0];>> Ganz ganz unschön, fehleranfällig und nicht portabel.> Stichwort: Padding Bytes>> Schreib eine Funktion, der du einen Header übergeben kannst und dir eine> korrekt befüllte Struktur zurückliefert.
Dann sind halt alle Compiler die mir untergekommen sind Glückstreffer.
Ich hatte noch nie einen in den letzten 30 Jahren der das anders gemacht
hat.
Aber man lernt halt nie aus.
Tim schrieb:> Es soll ja nicht portabel sein.> Und wenn es es sauber programmiert ist, auch nicht fehleranfällig
Na gut. Wundere dich aber bitte nicht, wenn es mit der nächsten
Compilerversion nicht mehr funktioniert.
Die Grösse eines Datentyps kannst du mit dem sizeof Operator ermitteln.
Dieser Operator liefert dir die Grösse des Types in Bytes, wobei ein
Byte der Grösse eines chars entspricht.
Alternativ kannst du auch im Handbuch deines Compilers nachlesen, wie
gross die Datentypen sind.
Uli schrieb:> Ich hatte noch nie einen in den letzten 30 Jahren der das anders gemacht> hat.
Ich schon. Implementier mal 16-Bit Typen auf einer Maschine, die das
nicht mitbringt. Kann man machen, wäre bei der Maschine aber bös
umständlich und langsam gewesen, also liess ich es sein. short=int, und
in der 32-Bit Version waren das eben 32 Bits.
MZ schrieb:> dort sind die uint8_t, uint16_t,... definiert
.. und dort kann man auch genau sehen, woraus diese Typen definiert
sind. Genau DAS kann man dann für diesen Compiler auch direkt benutzen.
Ich tippe mal auf unsigned short für einen 16 Bit integer. Im Grunde
kennt kein C-Compiler von sich aus sowas wie uintBLA_t. Das ergibt sich
immer nur aus irgendeiner stdint.h die man sich bei Bedarf und Laune
auch selber schreiben kann und in der all diese monströsen
Typbezeichnungen auf die altbekannten Typen char und int nebst
Qualifiern wie long und short zurückgeführt sind.
Aber das nächste Problem ist die Packungsdichte: Bei Structs sorgt der
Compiler dafür, daß der nächste Member auf einer passenden Adresse
landet. Manche CPU's können auf 16 und 32 Bit Daten nämlich nur auf
"geraden" Adressen zugreifen. Deshalb kann es je nach Zielarchitektur
dazu kommen, daß du zwar nen 16 Bit Integer hinbekommst, aber der
Compiler direkt danach 2 Füll-Bytes einfügt. Das Gleich gilt für Char's.
W.S.
W.S. schrieb:> Ich tippe mal auf unsigned short für einen 16 Bit integer
Vielen Dank!
Es war ein short!
Nun klappts auch mit dem Strukt :)
Noch zur Info!
ARM-GCC bzw. der STM32 kann byteweise auf den Speicher hinter dem Strukt
zugreifen.
Habs getestet in dem ich ein strukt mit zwei chars (welche übrigens 8bit
sind) angelegt habe und dann im speicher nachgesehen habe, ob diese
direkt aufeinander folgen.
Es war so!
Nun mache ich einfach zu beginn meines Programms im main eine prüfung
mit sizeof() allen datentypen ob diese meinen benötigten längen
entsprechen.
Sollte also in zukunf der compiler oder sonstwas ändern, ist das problem
zwar nicht automatisch gelöst, aber ich kriege es zumindest gleich mit!
Danke
Und warum machst du es nicht wie jeder andere und nimmst die
Definitionen aus stdint.h? Warum so umständlich? Die wurden nicht zum
Spaß in C99 eingeführt.
TriHexagon schrieb:> Und warum machst du es nicht wie jeder andere und nimmst die> Definitionen aus stdint.h? Warum so umständlich? Die wurden nicht zum> Spaß in C99 eingeführt.
weil wir ja gehört haben, dass uint_16_t alles von 2bytes aufwärts sein
kann.
Was nützt mir das, wenn ich uint_16_t nehme und es dann 4 bytes sind?
Dann geht mein strukt ja trozdem nicht über meinen buffer.
Angenommen ich habe im strukt 4 mal nen uint16.
Wenn ich davon ausgehe, dass uint16 2 bytes sind dann lege ich einen
solchen buffer an:
unsigned char buffer[8];
wenn es nun 4bytes sind, reicht es aber nicht!
Also erklär mir bitte, inwiefern mir die stdint.h etwas nützt?
Wenn uint16_t größer als 2 Byte ist, kannst du es auch nicht mit short
hinbiegen.
Tim schrieb:> unsigned char buffer[8];>> wenn es nun 4bytes sind, reicht es aber nicht!
Dann machst du einfach:
1
uint8_tbuffer[sizeof(structx)];
Diese Definitionen aus stdint.h sind zudem verständlicher als die
dämlich unpräziesen short, int, long Bezeichnungen. In jedem Forum kommt
es zu einer neuen Streiterei wie diese sind, wie z.B. hier.
Tim schrieb:> weil wir ja gehört haben, dass uint_16_t alles von 2bytes aufwärts sein> kann.
Nö. uint_16_t hat IMMER 2 byte. deshalb ja die stdint.h.
> Was nützt mir das, wenn ich uint_16_t nehme und es dann 4 bytes sind?> Dann geht mein strukt ja trozdem nicht über meinen buffer.
Das hat was mit dem alignment der struct zu tun.
>> Angenommen ich habe im strukt 4 mal nen uint16.> Wenn ich davon ausgehe, dass uint16 2 bytes sind dann lege ich einen> solchen buffer an:>> unsigned char buffer[8];>> wenn es nun 4bytes sind, reicht es aber nicht!>> Also erklär mir bitte, inwiefern mir die stdint.h etwas nützt?
stdint.h liefert dir Variablen von denen Du genau weist wie groß sie
sind. Das alignment in den Structs kann man je nach compiler mit
#pragmas oder _attribute_ ((aligned (8)) beeinflussen. Deinen Buffer
solltest Du eh mit anderst definieren.
z.B. Für einen Buffer in welchen mystruct_t 4 mal rein passt
mystruct_t buffer[4];
oder
mystruct_t * buffer;
buffer = malloc(sizeof(mystruct_t)*4)
W.S. schrieb:> .. und dort kann man auch genau sehen, woraus diese Typen definiert> sind. Genau DAS kann man dann für diesen Compiler auch direkt benutzen.
Aber wozu? Warum nicht den standardisierten uint16_t Typ verwenden? Dann
weiß auch jeder, der den Code liest, was man eigentlich bezweckt! Die
"16" ist da nicht zum Spaß, die besagt "Sechzehn bits", und genau die
will der OP doch haben?!
Tim schrieb:> Habs getestet in dem ich ein strukt mit zwei chars
Alle Cortex-M3,4 können das, und damit auch die STM32F1,2,3,4. Aber
nicht die STM32F0 (Cortex-M0). Dokumentiert ist das im jeweiligen
"ARMv*M Architecture Reference Manual" (* = 6 für Cortex-M0, * = 7 für
Cortex-M3, 4), suche dort nach "alignment".
Tim schrieb:> weil wir ja gehört haben, dass uint_16_t alles von 2bytes aufwärts sein> kann.
Nein. Nur uint_fast16_t und uint_least16_t. uint16_t ist immer genau 16
Bits, und damit genau das was du willst. Der Leser weiß auch sofort,
dass du genau 16bit willst.
Siehe:
http://en.cppreference.com/w/c/types/integerTriHexagon schrieb:> Wenn uint16_t größer als 2 Byte ist
... was nie der Fall ist - du meinst wohl "wäre".
Tim schrieb:> Nun mache ich einfach zu beginn meines Programms im main eine prüfung> mit sizeof() allen datentypen ob diese meinen benötigten längen> entsprechen.
Indem du das Programm als C++ statt C kompilieren lässt, kannst du
solche Überprüfungen auch schon dann machen wenn der Compiler läuft, und
nicht erst während das Programm ausgeführt wird:
1
static_assert(sizeof(uint16_t)*CHAR_BIT==16,"uint16_t ist nicht 16 bit gross");
Wobei, wie gesagt, dieser Fall nie eintreten wird, da uint16_t immer
16bit groß ist.
Tobi D. schrieb:> In Coocox schreibe ich die Variablen immer so (wenn der Code nicht> portabel sein soll!):> u8> u16
Du schreibst absichtlich unportablen Code? Ist ja schön wenn du das
machst, aber das hier ratschlagartig zu verbreiten ist kontraproduktiv.
Ich verstehe nicht, was so schwierig an "uint16_t" ist.
Dr. Sommer schrieb:> Tobi D. schrieb:>> In Coocox schreibe ich die Variablen immer so (wenn der Code nicht>> portabel sein soll!):>> u8>> u16> Du schreibst absichtlich unportablen Code? Ist ja schön wenn du das> machst, aber das hier ratschlagartig zu verbreiten ist kontraproduktiv.> Ich verstehe nicht, was so schwierig an "uint16_t" ist.
Gut!
Nun hab ichs verstanden :)
Hab nun uint16_t und uint8_t verwendet.
Läuft TipTop!
Es gibt Leute, die Sparen Zeichen ohne Ende. Ich kenne jemanden, der
benutzt für Variablennamen nur einen Buchstaben. Dementsprechend schlimm
und schwer ist der Code lesbar
Tim schrieb:> Noch zur Info!> ARM-GCC bzw. der STM32 kann byteweise auf den Speicher hinter dem Strukt> zugreifen.
Das hängt vom Prozessor ab. Ich hab auch schon einen ARM programmiert,
der beim Versuch, mit falschem Alignment zuzugreifen, eine
Hardware-Exception ausgelöst hat.
> Habs getestet in dem ich ein strukt mit zwei chars (welche übrigens 8bit> sind) angelegt habe und dann im speicher nachgesehen habe, ob diese> direkt aufeinander folgen.>> Es war so!
Das ist aber wenig überraschend. Du hast dir da genau den einen Fall
rausgesucht, wo's praktisch nie Alignment-Probleme gibt, denn char hat
normalerweise keine Alignment-Anforderungen. Dementsprechend braucht
eine struct, die ausschließlich aus chars besteht, auch keinerlei
Padding-Bytes. Spannend wird es eher, wenn eben gerade nicht alle
Elemente vom Typ char sind.
> Nun mache ich einfach zu beginn meines Programms im main eine prüfung> mit sizeof() allen datentypen ob diese meinen benötigten längen> entsprechen.
Das ist zwar irgendwie umständlich, aber geht natürlich auch.
Tim schrieb:> TriHexagon schrieb:>> Und warum machst du es nicht wie jeder andere und nimmst die>> Definitionen aus stdint.h? Warum so umständlich? Die wurden nicht zum>> Spaß in C99 eingeführt.>> weil wir ja gehört haben, dass uint_16_t alles von 2bytes aufwärts sein> kann.
Da hast du es genau falsch rum. short kann alles ab 16 Bit sein,
uint16_t dagegen hat exakt 16 Bit. uint16_t könnte aber auch 1 Byte groß
sein, wenn CHAR_BIT == 16. Dann hat man das gleiche Problem aber auch
mit allen anderen Typen.
> Angenommen ich habe im strukt 4 mal nen uint16.> Wenn ich davon ausgehe, dass uint16 2 bytes sind dann lege ich einen> solchen buffer an:>> unsigned char buffer[8];>> wenn es nun 4bytes sind, reicht es aber nicht!
Wenn du einen Puffer willst, der genau die richtige Grröße für deine
Struct hat, mach halt:
Rolf Magnus schrieb:> unsigned char buffer[sizeof(deine_struct)];
Genial! :) Das ich da nicht drauf gekommen bin :)
Aber ich denke es ist dennoch sinnvoll, sich gedanken über die Grösse
der einzelnen elemente des strukts zu machen.
Ansonsten wird ja Speicherplatz verschwendet...