Saltar a contenido

Datos Compuestos

El Ejemplo 1stC presenta las tres categor铆as de tipos de datos en C (b谩sicos o primitivos, derivados y definidos por el usuario). Se denomina user-defined data types a los tipos definidos por el propio usuario a partir de los tipos de datos b谩sicos y derivados
Los user-defined data types permiten a los usuarios escribir c贸digo m谩s eficiente y legible a la vez que proporcionan una mejor abstracci贸n
Existen cuatro elementos para definir tipos: Struct, Union, Enum y Typedef

Memoria y tipos de datos

Cada tipo de datos establece unos requisitos de memoria diferentes (entero 4 bytes, char 1 byte, etc.) que adem谩s dependen de factores como el lenguaje, el sistema operativo o la arquitectura del ordenador. En particular, para la computaci贸n en punto flotante (tipos double y float) se sigue el est谩ndar |15x15IEEE 754. Dependiendo si la precisi贸n es simple o doble una variable punto flotante en C ocupar谩 en memoria 4 bytes (tipo float) o 8 bytes (tipo double)
A continuaci贸n, la figura de la izquierda muestra un volcado de memoria que almacena 4 variables denominadas iii (tipo int), sss (short), ddd (double) y ptr (int *). El valor hexadecimal de ddd es 0x1FFFFFFFFFFFFFFF (1.491668146240042e-154). Los detalles de la conversi贸n se pueden analizar en el enlace calculadora IEEE 754

Autoevaluaci贸n:

  1. 驴Qu茅 valor tiene *ptr?
  2. 驴De qu茅 forma se podr铆a asegurar que las cuatro variables anteriores ocuparan siempre posiciones consecutivas en memoria?

Tipo struct

Los struct, al igual que los arrays, ocupan un n煤mero de celdas de memoria consecutivas. La diferencia es que, en este caso, los datos no tienen que ser obligatoriamente homog茅neos. Esto es, que todos sus campos sean del mismo tipo sino que pueden ser de cualquiera, ya sea b谩sico o compuesto. De hecho, un struct podr铆a incluir otro struct de forma anidada

Tipo struct empleado

Declaraci贸n del tipo
empleado.h
1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <string.h>

struct empleado 
{
  int id;
  char name[10];
  float salary;
};
Struct en memoria

MemStruct.png

Operador .

El siguiente c贸digo muestra la utilizaci贸n del operador . para acceder a cada uno de los campos de la variable con tipo struct student denominada record1

StudentData.c
 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
#include <stdio.h>
#include <string.h>

/* Declaraci贸n del tipo */
struct student 
{
    int id1;
    int id2;
    char a;
    char b;
    float percentage;
};

int main() 
{
    int i;
    /* Asignaci贸n: declaraci贸n + definici贸n */
    struct student record1 = {1, 2, 'A', 'B', 90.5};

    printf("size of structure in bytes : %d\n", 
        sizeof(record1));

    printf("\nAddress of id1        = %u", &record1.id1 );
    printf("\nAddress of id2        = %u", &record1.id2 );
    printf("\nAddress of a          = %u", &record1.a );
    printf("\nAddress of b          = %u", &record1.b );
    printf("\nAddress of percentage = %u",&record1.percentage);

    return 0;
}

Autoevaluaci贸n

  1. 驴Cu谩nta memoria (bytes) ocupa la variable record1?
  2. 驴Si en la declaraci贸n del tipo struct student del ejemplo precedente hubiera un sexto campo char *stu_name ser铆a correcta la asignaci贸n record1.stu_name = "Steve"?

struct_layout.png

Como puede verse en la soluci贸n, el tama帽o ocupado en memoria por el struct es mayor que la suma de los tama帽os que requiere cada uno de sus campos. Esto es porque en C las estructuras son consideradas paquetes de datos y por razones de rendimiento que dependen de la arquitectura de la CPU del ordenador cada tipo de dato tiene lo que se denomina un requisito de alineamiento. En el caso presentado, es m谩s eficiente acceder a memoria en bloques de 4 bytes que de uno en uno. Es por ello que se dejan dos bytes vac铆os para que, de este modo, todos los accesos sean de 4 en 4 bytes. En consecuencia, el tama帽o total finalmente ocupado son 16 bytes

Struct vs Union

La figura muestra el diferente tratamiento que hace el lenguaje C de ambos tipos de datos
struct vs union|700x400

Diferencias principales entre struct y union

Struct Union
Tipo de dato definido por el usuario que agrupa en una sola entidad variables de diferentes tipos Tipo de dato definido por el usuario que habilita el almacenamiento de variables de diferentes tipos en la misma direcci贸n de memoria (compartida por todos)
Tama帽o mayor (depende del alineamiento o padding) o igual que la suma de los tama帽os de todos los miembros El tama帽o es siempre el del miembro con mayor demanda de almacenamiento
Se asigna un area de memor铆a especifica para cada miembro La memoria es compartida entre todos los miembros
Nunca hay solapamiento de datos El solapamiento es completo dado que el 谩rea es 煤nica para todos
Acceso individual a cada miembro en todo momento Solamente un miembro puede ser accedido en cada momento

Typedef

Es una palabra clave o reservada del lenguaje que se puede utilizar para proporcionar un nuevo nombre (o alias) a los tipos de datos existentes. Por tanto, en este caso sirve para redefinir el nombre de los tipos de datos ya existentes
Otro caso de uso es el mostrado en el programa siguiente. A menudo los nombres de los tipos de datos resultan dif铆ciles de utilizar en los programas y resulta m谩s conveniente emplear otro m谩s sencillo

Ejemplo

struct_union_typedef.c
 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/*
 * Comparativa sencilla struct vs union
 * Compilaci贸n: gcc -Wall -Wextra -pedantic struct_union_typedef.c -o ./bin/struni
 */

#include <stdio.h> 
#include <string.h> 

/*
 * OJO: declaraci贸n del tipo mi_struct
 */
typedef struct ejemplo_struct { 
    int f1; 
    float f2; 
    char f3[20]; 
} mi_struct; 

/*
 * OJO: declaraci贸n del tipo mi_union
 */
typedef union ejemplo_union { 
    int f1; 
    float f2; 
    char f3[20]; 
} mi_union; 

int main() 
{ 
    /*
     * Definici贸n de un struct s
     */
    mi_struct s = { 10, 3.8, "mi_struct" }; 

    /*
     * Definici贸n INCORRECTA!!! de un union u
     */ 
    mi_union u = { 20, 5.4, "mi_union" }; 

    printf("mi_struct:\tf1: %d; f2: %.2f; f3: %s\n", s.f1, s.f2, s.f3); 
    printf("sizeof s:\t%ld bytes\n", sizeof(mi_struct)); 
    printf("mi_union(*):\tf1: %d; f2: %.2f; f3: %s\n",  u.f1, u.f2, u.f3);
    printf("sizeof u:\t%ld bytes\n", sizeof(mi_union));
    printf("(*) OJO: no es correcto\n"); 
    printf("------------------------------\n\n"); 

    /*
     * struct: todos los campos son accesibles
     */ 
    s.f1 = 183; 
    s.f2 = 9.7; 
    strcpy(s.f3, "PPS 3S2T (struct)"); 

    printf("mi_struct:\tf1: %d; f2: %.2f; f3: %s\n", s.f1, s.f2, s.f3); 

    /*
     * union: en cada instante s贸lo un campo es accesible
     */ 
    strcpy(u.f3, "PPS 3S2T (union)"); 
    u.f2 = 9.7; 

    printf("mi_union:\tf1: %d; f2: %.2f; f3: %s\n", u.f1, u.f2, u.f3); 
    printf("------------------------------\n\n"); 

   memset((char *) &u, 0, sizeof(u));
    u.f1 = 24; 
    printf("mi_union:\tf1: %d; f2: %.2f; f3: %s\n", u.f1, u.f2, u.f3); 

    u.f2 = 12.0; 
    printf("mi_union:\tf1: %d; f2: %.2f; f3: %s\n", u.f1, u.f2, u.f3); 

    strcpy(u.f3, "PPS 3S2T (union)"); 
    printf("mi_union:\tf1: %d; f2: %.2f; f3: %s\n", u.f1, u.f2, u.f3); 
}

Tarea

Laboratorio de recapitulaci贸n

Librer铆a b谩sica