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!
Naja könntest Dein Array auch in einer Schleife durchlaufen und mittels Schiebeoperationen, die jeweiligen bits im Ausgabebyete setzen oder löschen
Gast
#5496411
> Die [Bitfields] lassen sich aber IIRC nicht als Array ansteuern.
Korrekt. Die kleinste adressierbare (notwendig für ein Array) Einheit
ist ein Byte.
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:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
Die Ausgabe-Variable "packed" ist ein Array, weil µSer keinen einzelnen uint8_t akzeptiert. Das macht letztendlich aber kaum einen Unterschied.
Gast
#5496610
> ob ein Array nur nullen (und an anderer Stelle nur Einsen) enthält
Ganz fies kann das das so testen:1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
memchr() benutzt intern eine Schleife die maximal sizeof(arr) pro memchr() durchlaufen wird.
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.
Gast
#5497173
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.
Gast
#5497191
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.
Gast
#5497253
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
Gast
#5497265
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.
Gast
#5497274
was ist dadran unportabel`?
Gast
#5497324
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
Gast
#5497441
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.
Gast
#5497446
Dummes zeug, das regelt der compilerhersteller. Wenn du das anzweifels darfts du gar nix mehr machen.
Gast
#5497450
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.
Hier noch ein Beispiel:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
Wenn man das für AMD64 kompiliert, kommt heraus:
1 | |
2 | |
Kompiliert man hingegen für IA-32:
1 | |
2 | |
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:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
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:
1 | |
2 | |
1 | |
2 | |
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.