Namespaces
Variants

Struct declaration

From cppreference.net

Una estructura es un tipo que consiste en una secuencia de miembros cuyo almacenamiento se asigna en una secuencia ordenada (a diferencia de una unión, que es un tipo que consiste en una secuencia de miembros cuyo almacenamiento se superpone).

El especificador de tipo para una estructura es idéntico al union especificador de tipo excepto por la palabra clave utilizada:

Contenidos

Sintaxis

struct attr-spec-seq  (opcional) name  (opcional) { struct-declaration-list } (1)
struct attr-spec-seq  (opcional) name (2)
1) Definición de struct: introduce el nuevo tipo struct name y define su significado
2) Si se utiliza en una línea por sí mismo, como en struct name ; , declara pero no define la estructura name (ver declaración anticipada abajo). En otros contextos, nombra la estructura previamente declarada, y attr-spec-seq no está permitido.
name - el nombre de la estructura que se está definiendo
struct-declaration-list - cualquier número de declaraciones de variables, campos de bits , y aserciones estáticas . No se permiten miembros de tipo incompleto ni miembros de tipo función (excepto para el miembro de arreglo flexible descrito a continuación)
attr-spec-seq - (C23) lista opcional de atributos , aplicados al tipo de estructura

Explicación

Dentro de un objeto struct, las direcciones de sus elementos (y las direcciones de las unidades de asignación de campos de bits) aumentan en el orden en que los miembros fueron definidos. Un puntero a un struct puede convertirse a un puntero a su primer miembro (o, si el miembro es un campo de bits, a su unidad de asignación). Del mismo modo, un puntero al primer miembro de un struct puede convertirse a un puntero al struct que lo contiene. Puede haber relleno sin nombre entre dos miembros cualesquiera de un struct o después del último miembro, pero no antes del primer miembro. El tamaño de un struct es al menos tan grande como la suma de los tamaños de sus miembros.

Si una estructura define al menos un miembro con nombre, se le permite declarar adicionalmente su último miembro con tipo de arreglo incompleto. Cuando se accede a un elemento del miembro de arreglo flexible (en una expresión que utiliza el operador . o -> con el nombre del miembro de arreglo flexible como operando del lado derecho), entonces la estructura se comporta como si el miembro de arreglo tuviera el tamaño más largo que cabe en la memoria asignada para este objeto. Si no se asignó almacenamiento adicional, se comporta como si fuera un arreglo con 1 elemento, excepto que el comportamiento es indefinido si se accede a ese elemento o se produce un puntero un paso más allá de ese elemento. La inicialización y el operador de asignación ignoran el miembro de arreglo flexible. sizeof lo omite, pero puede tener más relleno final de lo que implicaría la omisión. Las estructuras con miembros de arreglo flexible (o uniones que tienen un miembro de estructura recursivo-posible con miembro de arreglo flexible) no pueden aparecer como elementos de arreglo o como miembros de otras estructuras.

struct s { int n; double d[]; }; // s.d is a flexible array member
struct s t1 = { 0 };          // OK, d is as if double d[1], but UB to access
struct s t2 = { 1, { 4.2 } }; // error: initialization ignores flexible array
// if sizeof (double) == 8
struct s *s1 = malloc(sizeof (struct s) + 64); // as if d was double d[8]
struct s *s2 = malloc(sizeof (struct s) + 40); // as if d was double d[5]
s1 = malloc(sizeof (struct s) + 10); // now as if d was double d[1]. Two bytes excess.
double *dp = &(s1->d[0]);    // OK
*dp = 42;                    // OK
s1->d[1]++;                  // Undefined behavior. 2 excess bytes can't be accessed
                             // as double.
s2 = malloc(sizeof (struct s) + 6);  // same, but UB to access because 2 bytes are
                                     // missing to complete 1 double
dp = &(s2->d[0]);            // OK, can take address just fine
*dp = 42;                    // undefined behavior
*s1 = *s2; // only copies s.n, not any element of s.d
           // except those caught in sizeof (struct s)
(desde C99)

Similar a union, un miembro sin nombre de una struct cuyo tipo es una struct sin nombre se conoce como estructura anónima . Cada miembro de una estructura anónima se considera miembro de la estructura o unión contenedora, manteniendo su diseño de estructura. Esto se aplica recursivamente si la estructura o unión contenedora también es anónima.

struct v
{
   union // anonymous union
   {
      struct { int i, j; }; // anonymous structure
      struct { long k, l; } w;
   };
   int m;
} v1;
v1.i = 2;   // valid
v1.k = 3;   // invalid: inner structure is not anonymous
v1.w.k = 5; // valid

Similar a union, el comportamiento del programa es indefinido si struct se define sin ningún miembro nombrado (incluyendo aquellos obtenidos mediante estructuras o uniones anidadas anónimas).

(desde C11)

Declaración anticipada

Una declaración de la siguiente forma

struct attr-spec-seq  (opcional) name ;

oculta cualquier significado previamente declarado para el nombre name en el espacio de nombres de etiquetas y declara name como un nuevo nombre de struct en el ámbito actual, que será definido posteriormente. Hasta que aparezca la definición, este nombre de struct tiene tipo incompleto .

Esto permite que las estructuras se refieran entre sí:

struct y;
struct x { struct y *p; /* ... */ };
struct y { struct x *q; /* ... */ };

Tenga en cuenta que un nuevo nombre de struct también puede introducirse simplemente usando una struct tag dentro de otra declaración, pero si ya existe un struct previamente declarado con el mismo nombre en el name space de etiquetas, la etiqueta se referiría a ese nombre

struct s* p = NULL; // la etiqueta que nombra una estructura desconocida la declara
struct s { int a; }; // definición de la estructura apuntada por p
void g(void)
{
    struct s; // declaración anticipada de una nueva estructura s local
              // esto oculta la estructura s global hasta el final de este bloque
    struct s *p;  // puntero a la estructura s local
                  // sin la declaración anticipada anterior,
                  // esto apuntaría a la s de ámbito de archivo
    struct s { char* p; }; // definición de la estructura s local
}

Palabras clave

struct

Notas

Consulte inicialización de estructuras para conocer las reglas sobre los inicializadores de estructuras.

Debido a que no se permiten miembros de tipo incompleto, y un tipo de estructura no está completo hasta el final de la definición, una estructura no puede tener un miembro de su propio tipo. Se permite un puntero a su propio tipo, y se utiliza comúnmente para implementar nodos en listas enlazadas o árboles.

Debido a que una declaración de struct no establece un scope , los tipos anidados, enumeraciones y enumeradores introducidos por declaraciones dentro de la struct-declaration-list son visibles en el scope circundante donde se define el struct.

Ejemplo

#include <stddef.h>
#include <stdio.h>
int main(void)
{
    // Declarar el tipo de estructura.
    struct car
    {
        char* make;
        int year;
    };
    // Declarar e inicializar un objeto de un tipo de estructura previamente declarado.
    struct car c = {.year = 1923, .make = "Nash"};
    printf("1) Car: %d %s\n", c.year, c.make);
    // Declarar un tipo de estructura, un objeto de ese tipo y un puntero al mismo.
    struct spaceship
    {
        char* model;
        int max_speed;
    } ship = {"T-65 X-wing starfighter", 1050},
    *pship = &ship;
    printf("2) Spaceship: %s. Max speed: %d km/h\n\n", ship.model, ship.max_speed);
    // La dirección aumenta en orden de definición. Se puede insertar relleno.
    struct A { char a; double b; char c; };
    printf(
        "3) Offset of char a = %zu\n"
        "4) Offset of double b = %zu\n"
        "5) Offset of char c = %zu\n"
        "6) Size of struct A = %zu\n\n",
        offsetof(struct A, a),
        offsetof(struct A, b),
        offsetof(struct A, c),
        sizeof(struct A)
    );
    struct B { char a; char b; double c; };
    printf(
        "7) Offset of char a = %zu\n"
        "8) Offset of char b = %zu\n"
        "9) Offset of double c = %zu\n"
        "A) Size of struct B = %zu\n\n",
        offsetof(struct B, a),
        offsetof(struct B, b),
        offsetof(struct B, c),
        sizeof(struct B)
    );
    // Un puntero a una estructura puede convertirse a un puntero
    // a su primer miembro y viceversa.
    char** pmodel = (char **)pship;
    printf("B) %s\n", *pmodel);
    pship = (struct spaceship *)pmodel;
}

Salida posible:

1) Car: 1923 Nash
2) Spaceship: T-65 X-wing starfighter. Max speed: 1050 km/h
3) Offset of char a = 0
4) Offset of double b = 8
5) Offset of char c = 16
6) Size of struct A = 24
7) Offset of char a = 0
8) Offset of char b = 1
9) Offset of double c = 8
A) Size of struct B = 16
B) T-65 X-wing starfighter

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
DR 499 C11 los miembros de structs/uniones anónimos se consideraban miembros del struct/unión contenedor conservan su diseño de memoria

Referencias

  • Estándar C23 (ISO/IEC 9899:2024):
  • 6.7.2.1 Especificadores de estructura y unión (p: TBD)
  • Estándar C17 (ISO/IEC 9899:2018):
  • 6.7.2.1 Especificadores de estructura y unión (p: 81-84)
  • Estándar C11 (ISO/IEC 9899:2011):
  • 6.7.2.1 Especificadores de estructura y unión (p: 112-117)
  • Estándar C99 (ISO/IEC 9899:1999):
  • 6.7.2.1 Especificadores de estructura y unión (p: 101-104)
  • Estándar C89/C90 (ISO/IEC 9899:1990):
  • 3.5.2.1 Especificadores de estructura y unión

Véase también

Documentación de C++ para Declaración de clase