View Full Version : [C] Mascheramento dei bit
fbcyborg
02-07-2008, 15:20
Salve a tutti,
non riesco a capire bene come si fa il mascheramento dei bit.
Ho un tipo di dato unsigned long e mi servono i 3 bit meno significativi. Come li tiro fuori?
Qualcosa mi dice che dovrei fare una maschera di bit del tipo int mask = 3; e poi fare qualcosa del tipo if (miavariabile & mask) allora...
Solo che io non so bene come fare per verificare esattamente quanto vale ogni singolo bit.
Oppure dovrei fare 3 maschere di bit? Ma come dichiararle? Valori esadecimali? Binari??
Grazie.
DanieleC88
02-07-2008, 16:02
Basta usare (numero & 7): usando 3 ottieni invece i 2 bit meno significativi.
Se interessa conoscere uno alla volta i bit meno significativi puoi usare anche lo scorrimento verso destra per isolare l'ultimo bit ad ogni passo:
for (int i = 0; i < NumeroDiBit; ++i, numero >>= 1)
{
unsigned char bit = (numero & 1);
printf("Estratto il bit: %u.\n", bit);
}
fbcyborg
02-07-2008, 16:07
Ti ringrazio moltissimo!!!
Dunque preferisco utilizzare il primo metodo.
Allora, supponiamo che mi venga fornito un "unsigned long numero" e voglia comunicare il valore di ciascuno dei 3 bit meno significativi.
A seconda del valore di ciascun bit devo fare una certa cosa....
esattamente come dovrei fare?
Siccome hai detto che potrei usare anche lo scorrimento a destra, mi sono un po' confuso. Volendo usare solo il primo metodo?
Grazie ancora.
DanieleC88
02-07-2008, 16:36
Prova a cercare con la calcolatrice i valori che ti interessano: vedrai che 1, 3 e 5 isoleranno uno alla volta i bit meno significativi.
primo = (numero & 1);
secondo = (numero & 3) >> 1;
terzo = (numero & 5) >> 2;
fbcyborg
02-07-2008, 20:17
Oddio, scusami ma continua la mia confusione. Ci metterò un po' per capire bene ma ci riusciro!!! :muro: :muro: :muro:
Allora.. prima hai detto 7=111 .. OK!
Io ho una variabile unsigned long my_var, e di questa variabile mi interessano i tre bit meno significativi. A seconda del valore di ciascuno di essi devo fare qualcosa (ad esempio incrementare un contatore).
Se il bit 0 è 1 devo fare una certa operazione
Se il bit 0 è 0 devo fare un'altra operazione
Se il bit 1 è 1 devo fare una certa operazione
Se il bit 1 è 0 devo fare un'altra operazione
Se il bit 2 è 1 devo fare una certa operazione
Se il bit 2 è 0 devo fare un'altra operazione
Mi sto confondendo per il fatto che mi hai detto di mettere 1, 3 e 5.
DanieleC88
02-07-2008, 20:25
In effetti dovevano essere 1, 2 e 4... sorry... :D
Ho risposto di fretta, scusa, comunque il ragionamento è più o meno lo stesso, dovresti essere in grado di fare da solo a questo punto. ;)
fbcyborg
02-07-2008, 20:34
Allora..
per vedere quanto vale il primo bit meno significativo e memorizzarlo faccio:
int a = (my_var & 1);
per il secondo bit
int b = (my_var & 1) >> 1
per il terzo:
int c = (my_var & 1) >> 2
:doh: :muro: :mc:
DanieleC88
02-07-2008, 20:40
Quasi:
int a = (n & 1) >> 0; /* qui lo shift è superfluo */
int b = (n & 2) >> 1;
int c = (n & 4) >> 2;
fbcyborg
02-07-2008, 20:57
Ok, ti ringrazio.. adesso invece vorrei capire bene cosa accade.
Provo con un esempio.
rappresentazione in bit
n = 111
n, è di 32 bit, ma rappresento solo i primi 3 meno significativi.
dunque, facendo
a = (n & 1) verifico il bit 0
facendo
b = (n & 2) >> 1
(quì mi perdo un po'... )
ora vedo quanto vale il secondo bit ma non capisco lo shift che dovrebbe portarmi ad una situazione del tipo n=011
e poi shiftando di 2???
Cacchio! :muro:
DanieleC88
02-07-2008, 21:34
No, tu vuoi conoscere solo e soltanto il valore del bit (che può assumere i soli valori 0 e 1), quindi tu hai:
n = 11101110111111 in base 2
Prendi il secondo bit usando un filtro come quello di prima:
mask = (1 << 1) = 2 = 10 in base 2
Otterrai:
bit = (n & mask) = 10 in base 2
Come noti, per riportare il valore a 0 o 1, devi operare uno shift:
n = (bit >> 1) = 1
ciao ;)
Quasi:
int a = (n & 1) >> 0; /* qui lo shift è superfluo */
int b = (n & 2) >> 1;
int c = (n & 4) >> 2;
in tutti i casi lo shift è superfluo, perché il C riconosce come true qualsiasi valore diverso da 0 (non importa portarli in prima posizione, basta fare bene il test).
fbcyborg, per evitare di fare confusione hai due scelte: o ti impari il binario per bene e ti fai le maschere ad-hoc o segui questa semplicissima regola:
//isolo il bit con indice i (considerando 0 l'indice del LSB) di un intero k
int k=...;
int i=...;
bool bit=k & 1<<i;
DanieleC88
02-07-2008, 23:20
in tutti i casi lo shift è superfluo
Non in tutti i casi: lo è solo quando lui ha bisogno strettamente di testarlo (se lui fa ValoreBit = (n & 5) su n = 7, otterrà 5 invece di 1, che è ciò che lui vuole).
fbcyborg
03-07-2008, 06:46
Non in tutti i casi: lo è solo quando lui ha bisogno strettamente di testarlo (se lui fa ValoreBit = (n & 5) su n = 7, otterrà 5 invece di 1, che è ciò che lui vuole).
No scusate.. io ho 32 bit, di cui mi interessano solo i 3 meno significativi. Io devo vedere il valore di ciascun bit, se è 0 o 1. A seconda di quanto vale, 0 o 1 (e non 5) devo fare una determinata cosa.
Potete consigliarmi qualcosa dove studiarlo??? Io faccio confusione anche perché quì si uniscono due concetti, l'and bit a bit e lo shift. Nonostante sappia cosa siano entrambi, quando li vado a combinare mi incasino.:confused:
fbcyborg
03-07-2008, 07:17
Allora!
Forse ho capito.
Il sistema dovrebbe essere analogo a quello della gestione dei permessi su Linux (777=111 111 111 111 = rwx ... )
Quello che mi fa confondere è il fatto che devo (ritornando al nostro discorso) vedere quanto valgono i primi tre bit meno significativi di 32 forniti come input.
Forse lo shift mi serve proprio per spostarmi sulla prima, seconda e terza cifra
però questo non lo capisco benissimo:
int a = (n & 1) >> 0; /* qui lo shift è superfluo */
int b = (n & 2) >> 1;
int c = (n & 4) >> 2;
Dunque... supponiamo che n sia 00000000000000000000000000000111 (32 bit).
I primi 29 bit non mi interessa che siano pari a 0, tanto non li considero.
l'AND fra n e 1 dovrebbe essere questo:
00000000000000000000000000000111 AND
00000000000000000000000000000001 =
-------------------------------------------
00000000000000000000000000000001 OK, il primo bit è pari a 1
secondo caso:
00000000000000000000000000000111 AND
00000000000000000000000000000010 =
-------------------------------------------
00000000000000000000000000000010 OK il secondo bit è pari a 1
Ma quì, shiftare a destra di 1 a che mi serve? Il risultato diventerebbe appunto:
00000000000000000000000000000001
Mi serve forse per restituire un 1 e memorizzarlo in b ? (suppongo di sì)
Terzo caso:
00000000000000000000000000000111 AND
00000000000000000000000000000100 =
-------------------------------------------
00000000000000000000000000000100 OK il terzo bit è pari a 1
Se ho capito bene, shifto di 2 posizioni per avere di nuovo
00000000000000000000000000000001 e memorizzarlo in c.
Spero di aver capito adesso!!!
:doh:
DanieleC88
03-07-2008, 08:54
Sì, hai capito bene: lo shift serve solo, dopo aver isolato il bit che ti serve, a riportare il valore nell'intervallo [0; 1]. Se hai bisogno solo di testarlo, cioè di verificare che quel bit non sia zero, allora puoi anche non fare lo shift, come precisava Furla. :)
fbcyborg
03-07-2008, 13:40
Grazie mille!
Ora sì che è chiaro allora.
In effetti ho solo bisogno di testarlo, e a questo punto non credo che sia necessario fare lo shift. In realtà non mi è ancora chiarissimo il motivo per cui dovrei riportare il valore nell'intervallo [0;1].
DanieleC88
03-07-2008, 14:31
Può servirti se tu vuoi (ad esempio usando quella formula dentro una macro) conoscere soltanto il valore del bit, che per definizione, è o 1 o 0. Può essere utile in condizioni dove i branch sono da evitare (non è il tuo caso). ;)
fbcyborg
03-07-2008, 14:34
forse mi serve.. a pensarci bene.
Perché a seconda di questi 3 bit devo distinguere 8 casi differenti. Quindi penso di metterci lo shift.
DanieleC88
03-07-2008, 14:41
Be', no allora non ti serve, perché io intendevo se vuoi ricavare il valore del bit espresso come 0 o 1, ma se devi solo testarlo (vedere se è attivo o meno e agire di conseguenza) non hai bisogno di riportarlo tra 0 e 1.
Non in tutti i casi: lo è solo quando lui ha bisogno strettamente di testarlo (se lui fa ValoreBit = (n & 5) su n = 7, otterrà 5 invece di 1, che è ciò che lui vuole).
ovviamente mi riferivo a tutti i casi che hai citato, e più in generale a tutti quelli in cui si vuole isolare un solo bit (quando cioè la maschera è una potenza di 2); che mi sembra sia proprio il caso di fbcyborg, al quale consiglio di evitare le maschere scritte in base 10 perché poco leggibili. per rendere chiaro il loro funzionamento o usi lo shift o le scrivi in esadecimale che permette una conversione immediata (adatto specialmente a quelle più complicate, quando ti serve più di un bit).
int k=...;
//le seguenti istruzioni fanno la stessa cosa:
bool bit=k & 1<<27;
bool bit=k & 134217728;
bool bit=k & 0x08000000;
//inutile dire quale sia la più chiara
fbcyborg
03-07-2008, 18:12
Ho un problema di compilazione.
Ho modificato un file sorgente del kernel di Linux e si lamenta proprio su queste tre righe che ho aggiunto:
int a = (n & 1);
int b = (n & 2) >> 1;
int c = (n & 4) >> 2;
L'errore che riporta è questo:
error: expected ')' before ';' token
per tutte e tre le righe che ho messo.
Perché???
fbcyborg
03-07-2008, 19:32
Ok, mi sono deciso a cercare di capire il problema in modo diverso, facendo delle prove con un programmino stupido:
#include <stdio.h>
#define MASK_0 = 1;
#define MASK_1 = 2;
#define MASK_2 = 4;
void verifica(unsigned long n){
int a,b,c;
if(n & MASK_0)
printf("Il primo bit meno significativo è a 1\n");
if(n & MASK_1)
printf("Il secondo bit meno significativo è a 1\n");
if(n & MASK_2)
printf("Il terzo bit meno significativo è a 1\n");
}
int main(int argc, char* argv[]){
unsigned long n = (unsigned long)argv[0];
verifica(n);
return 0;
}
Se vado a compilare il programma ottengo:
masking.c: In function 'verifica':
masking.c:10: error: expected expression before '=' token
masking.c:12: error: expected expression before '=' token
masking.c:14: error: expected expression before '=' token
E io che credevo di aver capito qualcosa... ma non c'ho capito nulla allora!:mc: :mc: :mc:
Oltretutto non vedo alcun '=' token...
odio gcc!!!! :|
DanieleC88
03-07-2008, 19:36
Nel primo caso sbagli la dichiarazione (non è codice C++ o C99, devi dichiarare tutte le variabili in testa alle funzioni, poi le puoi inizializzare dove vuoi).
Nel secondo caso invece sbagli la macro, se tu dopo il nome della macro scrivi "= 0;", il preprocessore sostituirà = 0; ad ogni occorrenza del nome della macro. Quello che volevi fare tu invece era definire una costante.
ciao ;)
fbcyborg
03-07-2008, 19:55
Grazie! :)
Come avrete capito non sono un drago nel programmare in C... :D
Ho modificato il sorgente come segue:
#include <stdio.h>
#define MASK_0 1
#define MASK_1 2
#define MASK_2 4
void verifica(unsigned long n){
int a,b,c;
a = (n & MASK_0);
b = (n & MASK_1);
c = (n & MASK_2);
printf("a: %d;\nb: %d;\nc: %d;\n",a,b,c);
if(a==1)
printf("Il primo bit meno significativo è a 1\n");
if(b==1)
printf("Il secondo bit meno significativo è a 1\n");
if(c==1)
printf("Il terzo bit meno significativo è a 1\n");
}
int main(int argc, char* argv[]){
unsigned long n = (unsigned long)argv[0];
verifica(n);
return 0;
}
Passo avanti: ora compila. che :ciapet: !!!
Solo che quando vado ad eseguire il programma, ad esempio passandogli 7,
mi aspetterei che il programma stampasse che
"Il primo bit meno significativo è a 1
Il secondo bit meno significativo è a 1
Il terzo bit meno significativo è a 1"
Visto che http://operaez.net/mimetex/7_{10}=111_2
inoltre, quando stampa i valori esce:
a: 0;
b: 0;
c: 4;
sia che gli passi un 7, sia che gli passi un 4.
:help: :help:
DanieleC88
03-07-2008, 20:03
Non usare confronti come if (a == 1), ma usa solo la forma if (a), altrimenti devi fare lo shift.
fbcyborg
03-07-2008, 20:08
Non usare confronti come if (a == 1), ma usa solo la forma if (a), altrimenti devi fare lo shift.
Dunque.. intanto a me serve sapere se ciascun bit preso in considerazione è = 1, per questo faccio il confronto ==1, ma se uso solo if (a) non ottengo quello che cerco... verificare il valore di ciascuno dei primi tre bit.
DanieleC88
03-07-2008, 20:21
No, se la maschera ti permette di isolare il bit e vedi che è attivo, ti basta usare if (a) se il tuo unico interesse è testare il bit. Provaci. ;)
fbcyborg
03-07-2008, 20:29
Allora.. forse ci sono quasi.
Ecco il codice aggiornato:
#include <stdio.h>
#define MASK_0 1
#define MASK_1 2
#define MASK_2 4
void verifica(unsigned long n){
int a,b,c;
a = (n & MASK_0);
b = (n & MASK_1);
c = (n & MASK_2);
printf("a: %d;\nb: %d;\nc: %d;\n",a,b,c);
if(a)
printf("Il primo bit meno significativo è a 1\n");
if(b)
printf("Il secondo bit meno significativo è a 1\n");
if(c)
printf("Il terzo bit meno significativo è a 1\n");
}
int main(int argc, char* argv[]){
unsigned long n = (unsigned long)argv[0];
verifica(5);
return 0;
}
Il problema è questo: 'unsigned long'.
Ho messo appunto unsigned long perché mi serve di testare un unsigned long.
Se invece gli passo un int, tutto combacia.
Ad esempio se gli passo 5, giustamente mi dice:
a: 1;
b: 0;
c: 4;
Il primo bit meno significativo è a 1
Il terzo bit meno significativo è a 1
Ora il problema è che uso l'unsigned long, se ad esempio gli passo un 7, lui me lo traduce in un valore casuale (ad esempio -2043457652). Magari la volta successiva il suo valore sarà un altro...
Grazie ancora.
fbcyborg
03-07-2008, 20:51
Ok, ci siamo.. sembra funzionare tutto bene:
#include <stdio.h>
#define MASK_0 1
#define MASK_1 2
#define MASK_2 4
void verifica(unsigned long n){
int a,b,c;
a = (n & MASK_0);
b = (n & MASK_1);
c = (n & MASK_2);
printf("a: %d;\nb: %d;\nc: %d;\n",a,b,c);
if(a)
printf("Il primo bit meno significativo è a 1\n");
if(b)
printf("Il secondo bit meno significativo è a 1\n");
if(c)
printf("Il terzo bit meno significativo è a 1\n");
}
int main(int argc, char* argv[]){
unsigned long n = 0;
for(n;n<8;n++){
printf("n: %d\n",n);
verifica(n);
}
return 0;
}
In pratica il problema era dovuto al fatto che avviavo il programma così:
./masking 7
Così facendo, quel 7 veniva interpretato male, e convertito in chissà quale cifra. Non so perché... anche se facevo il cast.
Ora invece gli faccio verificare da 0 a 7 tutti i numeri, direttamente all'interno del codice.. e pare andar bene!
DanieleC88
03-07-2008, 21:11
Il problema era nel fatto che il primo argomento del programma è la linea di comando: devi saltarlo prima di passare gli argomenti alle tue funzioni (in pratica gli passavi l'indirizzo della stringa che rappresenta il primo argomento, non un numero). :p
Io di solito metto un:
--argc;
++argv;
all'inizio della mia main(), se quel primo argomento non mi serve. :)
fbcyborg
03-07-2008, 21:17
E' vero! Poi mi ero accorto che argv[0] fosse sbagliato.
Anche mettendo argv[1], il problema permane.
Mamma mia quanto tempo è passato dall'ultima volta che ho toccato il C!!! (almeno 5 anni!!!) :D
DanieleC88
03-07-2008, 21:32
Anche mettendo argv[1], il problema permane.
Ma gli stai passando sempre l'indirizzo di una stringa, devi prima convertirla (atoi())! :p
fbcyborg
03-07-2008, 21:37
Cacchio!!! :doh: :doh: :doh: :doh: :doh:
:(
DanieleC88
03-07-2008, 21:44
Su che è normale. ;)
fbcyborg
03-07-2008, 21:52
Su che è normale. ;)
Grazie per l'incoraggiamento!!! ;)
Ora torno a combattere con altri tipi di errori di compilazione!!!
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.