Buscar..


Introducción

Las estructuras proporcionan una forma de agrupar un conjunto de variables relacionadas de diversos tipos en una sola unidad de memoria. La estructura en su conjunto puede ser referenciada por un solo nombre o puntero; También se puede acceder a los miembros de la estructura individualmente. Las estructuras se pueden pasar a funciones y se pueden devolver desde funciones. Se definen utilizando la palabra clave struct .

Estructuras de datos simples.

Los tipos de datos de estructura son una forma útil de empaquetar datos relacionados y hacer que se comporten como una sola variable.

Declarar una struct simple que contiene dos miembros int :

struct point 
{
    int x;
    int y; 
};

x e y se denominan miembros (o campos ) de la estructura de point .

Definiendo y usando estructuras:

struct point p;    // declare p as a point struct
p.x = 5;           // assign p member variables
p.y = 3;

Las estructuras se pueden inicializar en la definición. Lo anterior es equivalente a:

struct point p = {5, 3};

Las estructuras también se pueden inicializar usando inicializadores designados .

El acceso a los campos también se realiza utilizando el . operador

printf("point is (x = %d, y = %d)", p.x, p.y);

Estructuras Typedef

Combinar typedef con struct puede hacer que el código sea más claro. Por ejemplo:

typedef struct 
{
    int x, y;
} Point;

Opuesto a:

struct Point 
{
    int x, y;
};

Podría ser declarado como:

Point point;

en lugar de:

struct Point point;

Aún mejor es usar lo siguiente

typedef struct Point Point;

struct Point 
{
    int x, y;
};

Para tener ventaja de ambas definiciones posibles de point . Tal declaración es más conveniente si aprendió C ++ primero, donde puede omitir la palabra clave struct si el nombre no es ambiguo.

typedef nombres de typedef para estructuras pueden estar en conflicto con otros identificadores de otras partes del programa. Algunos consideran esto como una desventaja, pero para la mayoría de las personas que tienen una struct y otro identificador, el mismo es bastante perturbador. Notorio es, por ejemplo, la stat POSIX

int stat(const char *pathname, struct stat *buf);

donde se ve una función stat que tiene un argumento que es struct stat .

typedef 'd structs sin un nombre de etiqueta siempre impone que toda la declaración de struct sea ​​visible para el código que la usa. La declaración de struct completa se debe colocar en un archivo de encabezado.

Considerar:

#include "bar.h"

struct foo 
{
    bar *aBar;
};

Así que con una struct typedef que no tiene nombre de etiqueta, el archivo bar.h siempre tiene que incluir la definición completa de bar . Si usamos

typedef struct bar bar;

en bar.h , los detalles de la estructura de la bar se pueden ocultar.

Ver Typedef

Punteros a estructuras

Cuando tiene una variable que contiene una struct , puede acceder a sus campos utilizando el operador de punto ( . ). Sin embargo, si tiene un puntero a una struct , esto no funcionará. Tienes que usar el operador de flecha ( -> ) para acceder a sus campos. Aquí hay un ejemplo de una implementación terriblemente simple (algunos podrían decir "terrible y simple") de una pila que usa punteros para struct y muestra el operador de flecha.

#include <stdlib.h>
#include <stdio.h>

/* structs */
struct stack
{
    struct node *top;
    int size;
};

struct node
{
    int data;
    struct node *next;
};

/* function declarations */
int push(int, struct stack*);
int pop(struct stack*);
void destroy(struct stack*);

int main(void)
{
    int result = EXIT_SUCCESS;

    size_t i;

    /* allocate memory for a struct stack and record its pointer */
    struct stack *stack = malloc(sizeof *stack);
    if (NULL == stack)
    {
        perror("malloc() failed");
        return EXIT_FAILURE;
    }

    /* initialize stack */
    stack->top = NULL;
    stack->size = 0;

    /* push 10 ints */
    {
        int data = 0;
        for(i = 0; i < 10; i++)
        {
            printf("Pushing: %d\n", data);
            if (-1 == push(data, stack))
            {
                perror("push() failed");
                result = EXIT_FAILURE;
                break;
            }

            ++data;
        }
    }

    if (EXIT_SUCCESS == result)
    {
        /* pop 5 ints */
        for(i = 0; i < 5; i++)
        {
            printf("Popped: %i\n", pop(stack));
        }
    }

    /* destroy stack */
    destroy(stack);

    return result;
}

/* Push a value onto the stack. */
/* Returns 0 on success and -1 on failure. */
int push(int data, struct stack *stack)
{
    int result = 0;

    /* allocate memory for new node */
    struct node *new_node = malloc(sizeof *new_node);
    if (NULL == new_node)
    {
        result = -1;
    }
    else
    {
        new_node->data = data;
        new_node->next = stack->top;
        stack->top = new_node;
        stack->size++;
    }

    return result;
}

/* Pop a value off of the stack. */
/* Returns the value popped off the stack */
int pop(struct stack *stack)
{
    struct node *top = stack->top;
    int data = top->data;
    stack->top = top->next;
    stack->size--;
    free(top);
    return data;
}

/* destroy the stack */
void destroy(struct stack *stack)
{
    /* free all pointers */
    while(stack->top != NULL)
    {
        pop(stack);
    }
}

Miembros de la matriz flexible

C99

Declaración de tipo

Una estructura con al menos un miembro puede contener adicionalmente un único miembro de la matriz de longitud no especificada al final de la estructura. Esto se llama un miembro de matriz flexible:

struct ex1 
{
    size_t foo;
    int flex[];
};

struct ex2_header 
{
    int foo;
    char bar;
};

struct ex2 
{
    struct ex2_header hdr;
    int flex[];
};

/* Merged ex2_header and ex2 structures. */
struct ex3 
{
    int foo;
    char bar;
    int flex[];
};

Efectos sobre el tamaño y el relleno

Se considera que un miembro de matriz flexible no tiene tamaño cuando se calcula el tamaño de una estructura, aunque todavía puede existir un relleno entre ese miembro y el miembro anterior de la estructura:

/* Prints "8,8" on my machine, so there is no padding. */
printf("%zu,%zu\n", sizeof(size_t), sizeof(struct ex1));

/* Also prints "8,8" on my machine, so there is no padding in the ex2 structure itself. */
printf("%zu,%zu\n", sizeof(struct ex2_header), sizeof(struct ex2));

/* Prints "5,8" on my machine, so there are 3 bytes of padding. */
printf("%zu,%zu\n", sizeof(int) + sizeof(char), sizeof(struct ex3));

Se considera que el miembro de la matriz flexible tiene un tipo de matriz incompleta, por lo que su tamaño no se puede calcular utilizando sizeof .

Uso

Puede declarar e inicializar un objeto con un tipo de estructura que contiene un miembro de matriz flexible, pero no debe intentar inicializar el miembro de matriz flexible ya que se trata como si no existiera. Está prohibido intentar hacer esto, y se producirán errores de compilación.

De manera similar, no debe intentar asignar un valor a ningún elemento de un miembro de matriz flexible al declarar una estructura de esta manera ya que puede que no haya suficiente relleno al final de la estructura para permitir cualquier objeto requerido por el miembro de matriz flexible. Sin embargo, el compilador no necesariamente evitará que hagas esto, por lo que esto puede llevar a un comportamiento indefinido.

/* invalid: cannot initialize flexible array member */
struct ex1 e1 = {1, {2, 3}};
/* invalid: hdr={foo=1, bar=2} OK, but cannot initialize flexible array member */
struct ex2 e2 = {{1, 2}, {3}};
/* valid: initialize foo=1, bar=2 members */
struct ex3 e3 = {1, 2};

e1.flex[0] = 3; /* undefined behavior, in my case */
e3.flex[0] = 2; /* undefined behavior again */
e2.flex[0] = e3.flex[0]; /* undefined behavior */

En su lugar, puede optar por usar malloc , calloc o realloc para asignar la estructura con almacenamiento adicional y luego liberarla, lo que le permite usar el miembro de la matriz flexible como desee:

/* valid: allocate an object of structure type `ex1` along with an array of 2 ints */
struct ex1 *pe1 = malloc(sizeof(*pe1) + 2 * sizeof(pe1->flex[0]));

/* valid: allocate an object of structure type ex2 along with an array of 4 ints */
struct ex2 *pe2 = malloc(sizeof(struct ex2) + sizeof(int[4]));

/* valid: allocate 5 structure type ex3 objects along with an array of 3 ints per object */
struct ex3 *pe3 = malloc(5 * (sizeof(*pe3) + sizeof(int[3])));

pe1->flex[0] = 3; /* valid */
pe3[0]->flex[0] = pe1->flex[0]; /* valid */
C99

La 'estructura hack'

Los miembros de la matriz flexible no existían antes de C99 y se tratan como errores. Una solución común es declarar una matriz de longitud 1, una técnica llamada 'estructura':

struct ex1 
{
    size_t foo;
    int flex[1];
};

Sin embargo, esto afectará el tamaño de la estructura, a diferencia de un verdadero miembro de matriz flexible:

/* Prints "8,4,16" on my machine, signifying that there are 4 bytes of padding. */
printf("%d,%d,%d\n", (int)sizeof(size_t), (int)sizeof(int[1]), (int)sizeof(struct ex1));

Para usar el miembro flex como un miembro de matriz flexible, lo asignaría con malloc como se muestra arriba, excepto que sizeof(*pe1) (o el sizeof(*pe1) equivalente de sizeof(struct ex1) ) se reemplazaría con offsetof(struct ex1, flex) o el más largo, expresión de tipo agnóstico sizeof(*pe1)-sizeof(pe1->flex) . Alternativamente, puede restar 1 de la longitud deseada de la matriz "flexible" ya que ya está incluido en el tamaño de la estructura, suponiendo que la longitud deseada es mayor que 0. La misma lógica puede aplicarse a los otros ejemplos de uso.

Compatibilidad

Si se desea compatibilidad con compiladores que no admiten miembros de matriz flexible, puede usar una macro definida como FLEXMEMB_SIZE continuación:

#if __STDC_VERSION__ < 199901L
#define FLEXMEMB_SIZE 1
#else
#define FLEXMEMB_SIZE /* nothing */
#endif

struct ex1 
{
    size_t foo;
    int flex[FLEXMEMB_SIZE];
};

Al asignar objetos, debe usar la forma offsetof(struct ex1, flex) para referirse al tamaño de la estructura (excluyendo el miembro de la matriz flexible) ya que es la única expresión que permanecerá consistente entre los compiladores que admiten miembros de la matriz flexible y los compiladores que sí lo hacen. no:

struct ex1 *pe10 = malloc(offsetof(struct ex1, flex) + n * sizeof(pe10->flex[0]));

La alternativa es usar el preprocesador para restar condicionalmente 1 de la longitud especificada. Debido al aumento en el potencial de inconsistencia y error humano general en esta forma, moví la lógica a una función separada:

struct ex1 *ex1_alloc(size_t n)
{
    struct ex1 tmp;
#if __STDC_VERSION__ < 199901L
    if (n != 0)
        n--;
#endif
    return malloc(sizeof(tmp) + n * sizeof(tmp.flex[0]));
}
...

/* allocate an ex1 object with "flex" array of length 3 */
struct ex1 *pe1 = ex1_alloc(3);

Pasando estructuras a funciones

En C, todos los argumentos se pasan a las funciones por valor, incluidas las estructuras. Para estructuras pequeñas, esto es algo bueno, ya que significa que no hay sobrecarga por acceder a los datos a través de un puntero. Sin embargo, también hace que sea muy fácil pasar accidentalmente una estructura enorme que resulta en un rendimiento deficiente, especialmente si el programador está acostumbrado a otros lenguajes donde los argumentos se pasan por referencia.

struct coordinates
{
    int x;
    int y;
    int z;
};

// Passing and returning a small struct by value, very fast
struct coordinates move(struct coordinates position, struct coordinates movement)
{
    position.x += movement.x;
    position.y += movement.y;
    position.z += movement.z;
    return position;
}

// A very big struct
struct lotsOfData
{
    int param1;
    char param2[80000];
};

// Passing and returning a large struct by value, very slow!
// Given the large size of the struct this could even cause stack overflow
struct lotsOfData doubleParam1(struct lotsOfData value)
{
    value.param1 *= 2;
    return value;
}

// Passing the large struct by pointer instead, fairly fast
void doubleParam1ByPtr(struct lotsOfData *value)
{
    value->param1 *= 2;
}

Programación basada en objetos utilizando estructuras.

Las estructuras pueden usarse para implementar código de una manera orientada a objetos. Una estructura es similar a una clase, pero faltan las funciones que normalmente también forman parte de una clase, podemos agregarlas como variables de miembro de puntero a función. Para quedarse con nuestro ejemplo de coordenadas:

/* coordinates.h */

typedef struct coordinate_s 
{
    /* Pointers to method functions */
    void (*setx)(coordinate *this, int x);
    void (*sety)(coordinate *this, int y);
    void (*print)(coordinate *this);
    /* Data */
    int x;
    int y;
} coordinate;

/* Constructor */
coordinate *coordinate_create(void);
/* Destructor */
void coordinate_destroy(coordinate *this);

Y ahora el archivo C de implementación:

/* coordinates.c */

#include "coordinates.h"
#include <stdio.h>
#include <stdlib.h>

/* Constructor */
coordinate *coordinate_create(void)
{
    coordinate *c = malloc(sizeof(*c));
    if (c != 0)
    {
        c->setx = &coordinate_setx;
        c->sety = &coordinate_sety;
        c->print = &coordinate_print;
        c->x = 0;
        c->y = 0;
    }
    return c;
}

/* Destructor */
void coordinate_destroy(coordinate *this)
{
    if (this != NULL)
    {
        free(this);  
    }  
}

/* Methods */
static void coordinate_setx(coordinate *this, int x)
{
    if (this != NULL)
    {    
        this->x = x;
    }
}

static void coordinate_sety(coordinate *this, int y)
{
    if (this != NULL)
    {
        this->y = y;
    }
}

static void coordinate_print(coordinate *this)
{
    if (this != NULL)
    {
        printf("Coordinate: (%i, %i)\n", this->x, this->y);
    }
    else
    {
        printf("NULL pointer exception!\n");
    }
}

Un ejemplo de uso de nuestra clase de coordenadas sería:

/* main.c */

#include "coordinates.h"
#include <stddef.h>

int main(void) 
{
    /* Create and initialize pointers to coordinate objects */
    coordinate *c1 = coordinate_create();
    coordinate *c2 = coordinate_create();
    
    /* Now we can use our objects using our methods and passing the object as parameter */
    c1->setx(c1, 1);
    c1->sety(c1, 2);

    c2->setx(c2, 3);
    c2->sety(c2, 4);

    c1->print(c1);
    c2->print(c2);

    /* After using our objects we destroy them using our "destructor" function */
    coordinate_destroy(c1);
    c1 = NULL;
    coordinate_destroy(c2);
    c2 = NULL;

    return 0;
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow