Permalänk
Medlem

C, skapa 10 bitars array?

Jag försöker att skapa en tio bitars array men vet inte hur det ska skrivas. Det jag söker är alltså något i stil med:

struct { int number:10[STACK_SIZE]; } tenbit;

Både

struct { int number[STACK_SIZE]; } tenbit;

samt

struct { int number:10; } tenbit;

fungerar utan problem.

Visa signatur

alla forumedlemmar skapar glädje.
vissa när de loggar in.
andra när de loggar ut.

Permalänk
Medlem

Jag antar att du vill skapa en array an en godtycklig storlek som innehåller 10-bitars bitfält. Om du först skapar ditt bitfält som du beskriver ovan med

struct bitfield { unsigned int number:10; };

så kan du sedan skapa en array som innehåller tio sådana;

struct bitfield bf10[10];

Eller definiera och skapa arrayen på en gång:

struct bitfield { unsigned int number:10; } bf10[10];

Om du istället ville ha en array med 10 bitar där du enkelt kan sätta varje bit för sig skapar du istället ett bitfält med 10st individuella fält som är 1 bit breda, dvs:

struct bitfield { unsigned int bit0 : 1; unsigned int bit1 : 1; .... unsigned int bit9 : 1; } bf10; bf10.bit0 = 0; bf10.bit1 = 1; ....

Permalänk
Medlem

Tackar, var det första jag sökte.

Visa signatur

alla forumedlemmar skapar glädje.
vissa när de loggar in.
andra när de loggar ut.

Permalänk
Medlem

Är det inte enklare att bara använda de 10 lägsta bitarna i en uint16_t istället?

Visa signatur

Assembly är ett högnivåspråk.

Permalänk

struct bitfield { unsigned int number:10; }; struct bitfield bf10[10];

Skrivet av rankko:

Tackar, var det första jag sökte.

Värt att påpeka att även om du bara använder 10 bitar i bitfield så tar den upp en hel unsigned int (32 bitar) i arrayen. Så i exemplet ovan så kommer bf10 att vara 10*32=320 bits och inte 10*10=100 bits som man kanske kan tro.

Permalänk
Medlem
Skrivet av Gramner:

Är det inte enklare att bara använda de 10 lägsta bitarna i en uint16_t istället?

Det jag gjorde innan men tänkte att det var smartare att göra så här.

Skrivet av VirtualIntent:

struct bitfield { unsigned int number:10; }; struct bitfield bf10[10];

Värt att påpeka att även om du bara använder 10 bitar i bitfield så tar den upp en hel unsigned int (32 bitar) i arrayen. Så i exemplet ovan så kommer bf10 att vara 10*32=320 bits och inte 10*10=100 bits som man kanske kan tro.

Är det så? Det visste jag inte, tror jag får läsa på lite om poängen med det.

Visa signatur

alla forumedlemmar skapar glädje.
vissa när de loggar in.
andra när de loggar ut.

Permalänk
Medlem
Skrivet av VirtualIntent:

struct bitfield { unsigned int number:10; }; struct bitfield bf10[10];

Värt att påpeka att även om du bara använder 10 bitar i bitfield så tar den upp en hel unsigned int (32 bitar) i arrayen. Så i exemplet ovan så kommer bf10 att vara 10*32=320 bits och inte 10*10=100 bits som man kanske kan tro.

Om man kör 10 bitar från en char istället borde man väl spara in lite utrymme.

Permalänk
Medlem
Skrivet av VirtualIntent:

Värt att påpeka att även om du bara använder 10 bitar i bitfield så tar den upp en hel unsigned int (32 bitar) i arrayen. Så i exemplet ovan så kommer bf10 att vara 10*32=320 bits och inte 10*10=100 bits som man kanske kan tro.

Hur mycket utrymme en struct med diverse element i tar upp beror på arkitektur, kompilator och kompilatorflaggor. Oftast sätts padding in i structen så att varje medlem alignar till arkitekturerns word-size, men det går att komma runt (i de flesta fall) genom att tala om för kompilatorn att man inte vill aligna medlemmarna i structen. Om minnesutrymmet är väldigt viktigt kan du kolla upp i din kompilatormanual om det finns möjlighet att stänga av padding.

Permalänk

pelleplu: Visa gärna hur man i praktiken gör för att följande program ska skriva ut 100 istället för 320.

void main() { struct bitfield { unsigned int number:10; } bf10[10]; printf("%d", sizeof(bf10) * 8); }

Permalänk
Medlem

Det går inte tyvärr (eller om man skall se det som en fördel). Om du byter int till short så får du 160 istället vilket kanske liknar ditt usecase bättre.

Standarden säger något i stil med att man skall padda mot den största typen om jag inte mins fel (den kanske till och med padda mera, kommer inte ihåg). I detta fallet är typen int vilket alltid är 32 bitar.

Skriver man istället dit short så paddar den mot 16 bitar istället och slutar på 160 bitar.
Om man sedan föröker gå ett steg till och skriver char så säger standarden såhär: 10 är större än 8 och där med fel, dvs man får inte skapa en variabel med bitstorlek större än dess typ.

Använd inte bitpackning om ni vill spara datastorlek internt på datorn, använd enbart om ni gör typ spel och skall skicka data över nätverk eller bygga protokoll.

Visa signatur

Plan9 fan. In glenda we trust.

Permalänk
Medlem
Skrivet av dagle:

Det går inte tyvärr (eller om man skall se det som en fördel). Om du byter int till short så får du 160 istället vilket kanske liknar ditt usecase bättre.

Standarden säger något i stil med att man skall padda mot den största typen om jag inte mins fel (den kanske till och med padda mera, kommer inte ihåg). I detta fallet är typen int vilket alltid är 32 bitar.

Skriver man istället dit short så paddar den mot 16 bitar istället och slutar på 160 bitar.
Om man sedan föröker gå ett steg till och skriver char så säger standarden såhär: 10 är större än 8 och där med fel, dvs man får inte skapa en variabel med bitstorlek större än dess typ.

En int är inte definierad till 32 bitar, en short är inte definierad till 16 bitar och en char är inte definierad till 8 bitar. Det beror helt och hållet på vilket system du kompilerar för. Använd typdefinitionerna från <stdint.h> om du behöver en datatyp av en viss storlek.

Visa signatur

Assembly är ett högnivåspråk.

Permalänk
Medlem
Skrivet av dagle:

I detta fallet är typen int vilket alltid är 32 bitar.

Storleken på en int är inte exakt fastställd i C-standarden, så den kan variera beroende på kompilator, arkitektur m.m. Det går inte att lita på att den alltid är exakt 32 bitar stor.

Skrivet av dagle:

Använd inte bitpackning om ni vill spara datastorlek internt på datorn, använd enbart om ni gör typ spel och skall skicka data över nätverk eller bygga protokoll.

Bitfält bör inte användas över huvud taget, eftersom det inte finns någon fastställd standard för hur padding, endianness osv hanteras. Ska man skicka över nätverket finns det standardiserade funktioner (hton() m.fl. har jag för mig att dom heter) som konverterar data till ett standardiserat format för nätverkstrafik.

Skrivet av VirtualIntent:

pelleplu: Visa gärna hur man i praktiken gör för att följande program ska skriva ut 100 istället för 320.

void main() { struct bitfield { unsigned int number:10; } bf10[10]; printf("%d", sizeof(bf10) * 8); }

Vad använder du för kompilator? Kolla upp om det finns några kompilatorflaggor eller andra tricks i koden för att tvinga den att inte padda/aligna structmedlemmar.

Permalänk
Skrivet av pelleplu:

Vad använder du för kompilator? Kolla upp om det finns några kompilatorflaggor eller andra tricks i koden för att tvinga den att inte padda/aligna structmedlemmar.

Det är inte för min skull. Poängen är att ifall du inte kan visa att det går att göra i praktiken så är din tidigare kommentar ganska irrelevant.

Permalänk
Medlem
Skrivet av VirtualIntent:

Det är inte för min skull. Poängen är att ifall du inte kan visa att det går att göra i praktiken så är din tidigare kommentar ganska irrelevant.

Jag kanske inte varit helt tydlig i det jag skrivit, men den totala storleken på en struct kan ju aldrig bli mindre än en heltalsmultipel av 8 eftersom man inte kan adressera mindre än 1 byte (det finns såklart undantag, men normalt sett). Men med hjälp av t.ex. __attribute(packed) i GCC kan man göra sina structar så små som möjligt inom gränserna för vad som är tillåtet.

Vilken kommentar menar du är överflödig?

Permalänk
Medlem

Som nämnts tidigare kan du inte skapa arrayer vars element är av godtycklig storlek. Oavsett hur det paddas fram och tillbaka så måste elementen alignas till vad som är adresserbart. Du kan däremot, om du är riktigt desperat, skapa en array av bytes som så nära som möjligt täcker det utrymmet du ville allokera, och därefter skriva egna funktioner för read/write. Då kan du tex allokera dina 10*10 bitar med 13 bytes, och för att få ut tex andra elementet som såklart är o-adresserbart, så behöver du bara plocka ut andra och tredje byten (adresserbara) och shifta bitar lite höger och vänster. Om du är riktigt desperat som sagt.

Visa signatur

"Some poor, phoneless fool is probably sitting next to a waterfall somewhere, totally unaware of how angry and scared he's supposed to be." - Duncan Trussell

Permalänk
Skrivet av pelleplu:

Vilken kommentar menar du är överflödig?

Jag påpekade för rankko att den totala arrayen (bf10 om du kollar) kommer att ta upp mer plats än de 100 bitar han vill använda (och då menar jag också mer än 104 bitar), vilket kan vara värt att veta eftersom han verkar vilja spara minnesutrymme. Du skrev då någonting om att han kan kolla upp i sin kompilatormanual hur man gör för att en struct ska ta mindre plats, och det är den kommentaren jag menar är irrelevant ifall du inte kan visa på ett enda exempel där den totala arrayen faktiskt tar upp mindre än 320 bitar i minnet (genom att använda den tekniken som han frågor om).

Permalänk
Medlem
Skrivet av VirtualIntent:

Jag påpekade för rankko att den totala arrayen (bf10 om du kollar) kommer att ta upp mer plats än de 100 bitar han vill använda (och då menar jag också mer än 104 bitar), vilket kan vara värt att veta eftersom han verkar vilja spara minnesutrymme. Du skrev då någonting om att han kan kolla upp i sin kompilatormanual hur man gör för att en struct ska ta mindre plats, och det är den kommentaren jag menar är irrelevant ifall du inte kan visa på ett enda exempel där den totala arrayen faktiskt tar upp mindre än 320 bitar i minnet (genom att använda den tekniken som han frågor om).

Aha, nu förstår jag. Det går ju som du sa tidigare inte att få den att bli exakt 100 bitar eftersom bitfältet på 10-bitar i fallet man har det som en unsigned int kommer att paddas upp till 32 bitar. Men om man använder __attribute(packed) så kommer GCC att minimera det onödiga utrymmet, och lagra de tio bitarna i bitfältet i 2 st byte-stora variabler.

struct bitfield_packed { unsigned int number:10; } __attribute__((packed)); struct bitfield_packed bf10[10]; printf("%lu\n", sizeof(bf10) * 8);

Utskriften från det blir 160, kompilerat för ett x86_64-bitars system med gcc.

Permalänk
Skrivet av pelleplu:

Utskriften från det blir 160, kompilerat för ett x86_64-bitars system med gcc.

Ja se där, toppen Så att packa 100 bitar i 160 bitar är antagligen det bästa som någon vanligt förekommande kompilator gör i praktiken?

Jag instämmer med det du skrev, "Bitfält bör inte användas över huvud taget". Som Gramner skrev i sin första kommentar så kan man lika gärna använda en array av short, det kommer ändå ta lika mycket plats. Eller om man är väldigt angelägen om att spara minne göra som gibbon_ föreslår i sin kommentar.

Permalänk
Medlem

Om du vill lagra bitar tätt packat så går detta att använda t.ex.

/* functions to handle with bit array access */ static unsigned char *bitarray_allocate(int size) { return (unsigned char *)malloc((size+7)/8); } static void bitarray_zeroall(unsigned char *a, int size) { memset(a, 0, (size+7)/8); } static void bitarray_free(unsigned char *a) { free(a); } static int bitarray_value(unsigned char *a, int id) { return a[id>>3]&(1<<(id&0x7)); } static void bitarray_set(unsigned char *a, int id) { a[id>>3] |= (1<<(id&0x7)); } static void bitarray_clear(unsigned char *a, int id) { a[id>>3] &= ~(1<<(id&0x7)); }

Koden är tagen ifrån https://github.com/matricks/bam/blob/master/src/node.c#L265

Då kommer det hela ta 104 bits vilket är det närmaste du kommer. Dock är det en minnesallokering där i men det går ju lika bra att köra på en statisk array. Performance på dom där funktionerna är lite tveksam på vissa platformar också men men.

Visa signatur

Teeworlds - För dig som gillar gulliga saker med stora vapen.

Permalänk
Medlem
Skrivet av VirtualIntent:

Ja se där, toppen Så att packa 100 bitar i 160 bitar är antagligen det bästa som någon vanligt förekommande kompilator gör i praktiken?

Det borde det vara, så länge vi pratar "normala" arkitekturer och "normala" kompilatorer Förutsatt att varje element måste vara just 10 bitar, annars kan man ju strukturera ihop dom lite annorlunda och komma ner i utrymme. Men om man inte jobbar med extremt resursbegränsade inbyggda system så borde inte minnesutrymmet spela någon som helst roll i det här fallet Några byte är ju ganska så billigt nu för tiden.