Hallo zusammen,
ich möchte in meinem Code abfragen, ob ein Array nur nullen (und an
anderer Stelle nur Einsen) enthält. Natürlich wäre es möglich das byte
mit 8 Operationen zusammenzusetzen, ich wollte es jedoch effizienter
machen. Da Arrays in C ja im Speicher immer ununterbrochen angelegt
werden (?), sollte der folgende Code die Aufgabe einfach und effizient
übernehmen.
bool arr[8]= {0,0,0,0,0,0,0,0};
uint8_t *pointer;
pointer = &arr[0];
Dies wird mir jedoch durch den Compiler versagt. Dabei sehe ich keinen
logischen Fehler in meiner Idee. Kann mir jemand sagen wieso das nicht
geht und wie man es ähnlich effizient machen könnte?
Das "bool arr[8]" sind 8 Byte und nicht 8 Bit. Bei einigen Compilern
sogar mehr, z.B. 32 Bit auf 32-bit Compilern.
Wenn man 8 Bit zu einem Byte zusammenfassen will, bräuchte man
Bitfields. Die lassen sich aber IIRC nicht als Array ansteuern.
Jim M. schrieb:> Das "bool arr[8]" sind 8 Byte und nicht 8 Bit. Bei einigen Compilern> sogar mehr, z.B. 32 Bit auf 32-bit Compilern.
Tatsache, das war mir gar nicht klar vorher. Dann werd ich meinen Code
wohl nochmal umschreiben. Vielen Dank!
Sven L. schrieb:> Naja könntest Dein Array auch in einer Schleife durchlaufen und mittels> Schiebeoperationen, die jeweiligen bits im Ausgabebyete setzen oder> löschen
Dass das so geht war mir auch vorher klar, nur wurde dir Operation
(mittlerweile geändert) jede Iteration meiner for(;;) Schleife
durchlaufen, und ich hatte gehofft man könne die 8 Operationen die dein
Vorschlag benötigt auf eine runterbrechen.
foobar schrieb:>> Die [Bitfields] lassen sich aber IIRC nicht als Array ansteuern.>> Korrekt. Die kleinste adressierbare (notwendig für ein Array) Einheit> ist ein Byte.
Das scheint universell zu sein oder? Mein 64 bit Desktop kann scheinbar
auch einzelne bytes adressieren, ebenso wie mein MC.
Btw, wenn ein einzelner bool arr[0]=true (z.B. alle bits gesetzt) und
uint8_t *pointer die gleiche Größe haben, ist es dann irgendwie möglich
den pointer auf die adresse von bool zu setzen und somit dessen Wert
(0xFF) als int auszulesen?
N. N. schrieb:> Das scheint universell zu sein oder? Mein 64 bit Desktop kann scheinbar> auch einzelne bytes adressieren, ebenso wie mein MC.
C(++) definiert das Byte als die kleinste adressierbare Einheit. Ein
char ist immer genau ein Byte groß. Die Anzahl an Bits pro Byte ist aber
nicht festgelegt; sie ist mindestens 8 und kann über das Makro CHAR_BIT
abgerufen werden. Auf den allermeisten Plattformen sind es aber 8.
uint8_t ist nahezu sicher auf allen Plattformen identisch zu entweder
unsigned char oder char oder existiert einfach nicht, falls ein char
mehr als 8 bits hat.
N. N. schrieb:> Btw, wenn ein einzelner bool arr[0]=true (z.B. alle bits gesetzt)
Es ist nicht festgelegt, welche Bits dann 1 sind. Meistens wird nur das
unterste Bit genutzt.
N. N. schrieb:> möglich> den pointer auf die adresse von bool zu setzen und somit dessen Wert> (0xFF) als int auszulesen?
Du kannst den Pointer auf char umcasten, was aber
implementations-spezifisches Verhalten bewirkt und somit unportabel und
unsauber ist. Auf int umcasten und dereferenzieren ist in C(++)
verboten. Es kann zwar den Anschein haben zu funktionieren, kann aber
früher oder später beliebig schief gehen (siehe auch strict aliasing).
Was hast du überhaupt vor? Möchtest du eine Datenstruktur so packen dass
du sie abspeichern oder verschicken kannst? Dann lies mal
Serialisierung. Mit der dort auch vorgestellten µSer-Bibliothek kann
die anfängliche Problemstellung so gelöst werden:
Danke für den sehr informativen Beitrag.
Niklas G. schrieb:> Was hast du überhaupt vor?
Im Moment gar nichts. Ich setze nun die Bits klassisch mit Masken
einzeln und es funktioniert gut wie erwartet. Die Pointerumwandlung ist
lediglich aus Interesse. Danke auch hier für deine Tipps, ich werde mal
überprüfen ob ein bool in meinem System auch i.A. nur über die unterste
Stelle implementiert ist.
Jack schrieb:> Ganz fies kann das das so testen:bool arr[8] =...;>> if(!memchr(arr, true, sizeof(arr)) {> // nur false (Nullen) in arr[]> }>> if(!memchr(arr, false, sizeof(arr)) {> // nur true (Einsen) in arr[]> }
Und im zweiten Fall fällt man dann je nach Implementierung ganz fies auf
die Schnauze.
Niklas G. schrieb:> Es ist nicht festgelegt, welche Bits dann 1 sind. Meistens wird nur das> unterste Bit genutzt.
Festgelegt ist true als "ungleich Null", false als "gleich Null".
Alles andere kann jeder Compiler-Bauer machen wie er möchte und hat
nichts mit C-Standard zu tun.
Wolfgang schrieb:> Niklas G. schrieb:>> Es ist nicht festgelegt, welche Bits dann 1 sind. Meistens wird nur das>> unterste Bit genutzt.>> Festgelegt ist true als "ungleich Null", false als "gleich Null".
Nein. Wenn man einen Integer ungleich Null in einen bool konvertiert,
kommt true raus, sonst false. Das sagt aber absolut gar nichts darüber
aus, was bei dieser Konvertierung passiert und wie die Bits eines bool
für true und für false aussehen müssen.
Guck dir mal diese deklaration an:
typedef struct
{
unsigned bit0:1;
unsigned bla:1;
unsigned murks:1;
unsigned schund:3;
}tBitfeld;
Da kannst Du jetzt noch eine union drüberlegen und dann entweder als
bit (also bla=1) oder als uint8_t, uint16_t oder uint32_t
ansprechen
hbl999 schrieb:> Da kannst Du jetzt noch eine union drüberlegen
Das ist im C implementation defined (also unportabel) und im C++
komplett verboten. Also nicht die beste Vorgehensweise.
hbl999 schrieb:> was ist dadran unportabel`?
C11 6.7.2.1 11:
The order of allocation of bit-fields within a unit (high-order to
low-order or low-order to high-order) is implementation-defined.
The alignment of the
addressable storage unit is unspecified
hbl999 schrieb:> was ist dadran unportabel`?
Da Padding, Byte Order, Integer -Formate sowie die Umsetzung von
Bitfields plattformspezifisch sind, ist das Ergebnis von solchen
union-Konstrukten unportabel. Generell sollte man unions ausschließlich
zum Speicher sparen nutzen, nicht für irgendwelche Konvertierungen.
Steht auch alles detailliert in Serialisierung.
hal9000 schrieb:> Dummes zeug, das regelt der compilerhersteller. Wenn du das> anzweifels darfts du gar nix mehr machen.
Nein, der Compiler warnt sogar teilweise in solchen Fällen, z.B. der GCC
in Form von Strict Aliasing Verletzungen. Der Compiler kann die
Unterschiede nicht weg-regeln ohne die Effizienz stark zu
beeinträchtigen. Im C-Standard würde das wohl kaum so stehen wenn die
Compiler das dann anders machen!
Und es ist richtig, dass in C wesentlich weniger erlaubt ist als oft
angenommen. Zur Datenkonvertierung sind aber bitweise Operationen
erlaubt und portabel.
Ist also plattformabhängig, d.h. unportabel. In C++ ist es wie gesagt
ganz verboten. Der Unterschied stammt hier konkret vom Padding, denn bei
AMD64 hat der 64bit-Integer ein 8-Byte-Alignment, es werden 7 leere
Bytes davor eingefügt. Auf IA-32 werden nur 3 benötigt. Solche
Unterschiede kann man teilweise mit Compiler-Optionen beeinflussen - das
macht es aber nur noch schlimmer, wenn das Verhalten des Programms sich
schon von Compiler-Optionen stören lässt!
Korrekt und portabel geht das nur mit Bit-Operationen. Diese werden von
meiner µSer-Bibliothek komfortabel gekapselt, damit könnte das dann so
aussehen:
Das Padding wird so gewählt dass es der AMD64-Version entspricht; somit
ist, unabhängig von der tatsächlichen Plattform, Compiler-Optionen und
-Version, die Ausgabe immer gleich: