Campos de bits
Estructura de archivos:
📁 04_Bitfields
└── 📁 Bitfields-NN
├── 📄 main.c
└── 📄 Makefile
Ejercicios:
Bitfields 01
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
#include <stdio.h>
/* Declaracion de una estructura con campos de bits basados en unsigned int,
* El comportamiento ante overflow en campos unsigned es aritmetica modular (wrap-around),
* es decir, al superar maximo vuelven a cero.
*/
struct CB
{
unsigned int A : 3; // Puede almacenar valores de 0 a 7
unsigned int B : 1; // Puede almacenar valores de 0 a 1
unsigned int C : 2; // Puede almacenar valores de 0 a 3
};
int main(void)
{
/* Declaracion de una variable de tipo struct CB y una variable auxiliar para el bucle */
struct CB bit;
int i;
/* Inicializacion de los campos de bits de la estructura, se podria reducir con struct CB bit = {0}; */
bit.A = 0;
bit.B = 0;
bit.C = 0;
/* Bucle que se ejcuta 10 veces.
* En cada iteracion se incrementan los tres campos de bits.
* Debido a la cantidad limitada de bits:
* - A (3 bits) desborda al superar 7
* - B (1 bit) alterna entre 0 y 1
* - C (2 bits) desborda al superar 3
* El desbordamiento (overflow) en tipos unsigned produce wrap-around (al superar el maximo vuelve a cero)
*/
for(i = 0; i < 10; i++)
{
bit.A++; // 3 bits = 2^n - 1 = 7. 0 => 1 => 2 => 3 => 4 => 5 => 6 => 7 => 0 => 1 => 2
bit.B++; // 1 bit = 2^n - 1 = 1. 0 => 1 => 0 => 1 => 0 => 1 => 0 => 1 => 0 => 1 => 0
bit.C++; // 2 bits = 2^n - 1 = 3. 0 => 1 => 2 => 3 => 0 => 1 => 2 => 3 => 0 => 1 => 2
}
/* El programa original no hace nada mas, podria imprimirse los valores finales de los campos y la dimension en memoria de la estructura */
printf("A = %u\n", bit.A);
printf("B = %u\n", bit.B);
printf("C = %u\n", bit.C);
printf("Dimension del struct = %zu\n", sizeof(struct CB));
return 0;
}
Bitfields 02
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
#include <stdio.h>
#include <conio.h> // Se usa getch();
#include <stdlib.h> // Se usa system("cls");
/* Declaracion de estructuras con miembros campos de bits
* basados en unsigned char.
*
* El rango de cada campo depende de la cantidad de bits
* El valor maximo representable es 2^n -1 donde n es la cantidad de bits.
* - 1 bit => 0 a 1
* - 2 bits => 0 a 3
* - 3 bits => 0 a 7
*/
struct alfa
{
unsigned char A:1;
unsigned char B:2;
unsigned char C:3;
};
struct beta
{
unsigned char A:3;
unsigned char B:2;
unsigned char C:1;
};
struct gamma
{
unsigned char A:5; // 0 a 31
unsigned char B:2;
unsigned char C:3;
unsigned char D:4; // 0 a 15
};
/* Declaracion de union cuyos miembros son estructuras con campos de bits y un vector de 2 bytes
* Todos los miembros comparten la misma region de memoria.
* La dimension de la union sera la del miembro mas grande.
*/
union delta
{
struct alfa D;
struct beta E;
struct gamma F;
char G[2];
};
int main(void)
{
/* Declaracion de variable auxiliar char para control de bucles, variable de tipo struct alfa y de tipo union delta */
char a;
struct alfa b;
union delta e;
/* Limpiar pantalla */
system("CLS");
/* Se inicializa el campo de bit A en 0 y se lo incrementa 4 veces.
* Como A tiene 1 bit, opera modulo 2 (0 y 1)
* Se observa el comportamiento de la aritmetica modular (wrap-around)
*/
for (b.A = 0, a = 0; a < 4; b.A++, a++)
{
printf("%d ", b.A);
printf("\n");
}
/* Se inicializa el campo de bit B en 0 y se lo incrementa 8 veces.
* Como B tiene 2 bits, opera modulo 4 (0 a 3)
* El incremento produce desbordamiento modular.
*/
for (b.B = 0, a = 0; a < 8; b.B++, a++)
{
printf("%d ", b.B);
printf("\n");
}
/* Se inicializa el campo de bit C en 0 y se lo incrementa 16 veces.
* Como C tiene 3 bits, opera modulo 8 (0 a 7)
* Se imprime cada valor antes de esperar una tecla.
*/
for (b.C = 0, a = 0; a < 16; b.C++, a++)
{
printf("%d ", b.C);
printf("\n\nPresione una tecla para continuar");
}
getch(); // Espera una tecla
system("CLS");
/* Inicializacion del miembro G de la union.
* Se escriben ambos bytes en 0.
* En este momento, el miembro activo es G.
*/
e.G[0] = 0;
e.G[1] = 0;
/* Mostrar valores del vector de la union */
printf("\ne.G[0] = %d\te.G[1] = %d", e.G[0], e.G[1]);
/* Se escriben los campos del miembro D (struct alfa).
* A partir de este momento, el miembro activo de la union es D.
* Como todos comparten memoria, G reflejara los mismos bits.
*/
e.D.A = 1;
e.D.B = 2;
e.D.C = 5;
/* Mostrar los campos de bits de D.
* Acceder a otro miembro distinto del ultimo escrito (D)
* puede ser comportamiento indefinido (UB) segun el estandar,
* excepto cuando se accede mediante char para inspeccion de la representacion en memoria.
* Por esto se puede mostrar e.G[0] y e.G[1] en hexadecimal sin que produzca (UB).
*/
printf("\ne.D.A = %d\te.D.B = %d\te.D.C = %d", e.D.A, e.D.B, e.D.C);
printf("\ne.G[0] = %X\te.G[1] = %X", e.G[0], e.G[1]);
/* Se escriben los campos del miembro E.
* Ahora el miembro activo pasa a ser E.
* El contenido previo se sobreescribe en la misma memoria.
*/
e.E.C = 1;
e.E.B = 2;
e.E.A = 5;
printf("\ne.E.A = %d\te.E.B = %d\te.E.C = %d", e.E.A, e.E.B, e.E.C);
printf("\ne.G[0] = %X\te.G[1] = %X", e.G[0], e.G[1]);
/* Se escriben los campos del miembro F (struct gamma),
* que probablemente sea el miembro de mayor dimension.
*/
e.F.A = 18;
e.F.B = 2;
e.F.C = 5;
e.F.D = 11;
printf("\ne.F.A = %X\te.F.B = %X\te.F.C = %X\te.F.D = %X", e.F.A, e.F.B, e.F.C, e.F.D);
printf("\ne.G[0] = %X\te.G[1] = %X\n\n", e.G[0], e.G[1]);
return 0;
}
Bitfields 03
Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic
PROG = main
OBJ = main.o
$(PROG): $(OBJ)
$(CC) $(OBJ) -o $(PROG)
$(OBJ): main.c
$(CC) $(CFLAGS) -c main.c
.PHONY: clean
clean:
del $(OBJ) $(PROG).exe
Código:
// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main
#include <stdio.h>
#include <stdlib.h> // Se usa rand()
/* Declaracion de estructura con campos de bits. */
struct alfa
{
unsigned d:5; // 0 a 31
unsigned s:2; // 0 a 3
unsigned v:1; // 0 o 1
};
struct beta
{
unsigned d:5;
unsigned s:2;
};
int main(void)
{
/* inp se utiliza como estructura auxiliar para generar datos.
* datos es un vector de estructuras donde se almacenan los valores generados
*/
struct alfa inp;
struct beta dat[20];
int a;
/* Se genera un valor aleatorio 0 o 1.
* rand()%2 produce 0 o 1.
* Se almacena en el campo de 1 bit v.
*/
inp.v = rand()%2;
/* Se repoite 20 veces:
* - Se generan valores aleatorios para s (0 a 3) y d (0 a 31)
* - Mientras v sea 0, se sigue generando hasta que sea 1
* - Se copian los valores de d y s al vector dat
* - Se vuelve a generar un nuevo valor para v
*/
for (a = 0; a < 20; a++)
{
inp.s = rand()%4; // 2 bits => 0 a 3
inp.d = rand()%32; // 5 bits => 0 a 31
while (!inp.v)
{
inp.v = rand()%2;
}
dat[a].d = inp.d;
dat[a].s = inp.s;
inp.v = rand()%2;
}
/* Mostrar mensaje como titulos de una tabla */
printf("CAN\tDAT\tCAN\tDAT\n");
/* Se imprimen los valores almacenados.
* ATENCION: cuando a == 19 se accede a dat[20],
* lo que esta fuera de los limites del vector y produce (UB).
*/
for (a = 0; a < 20; a++)
{
printf("%d\t%d\t%d\t%d\n", dat[a].s, dat[a].d, dat[a + 1].s, dat[a + 1].d);
}
return 0;
}