¿Cuál es la necesidad de llaves vacías '{}' al final de la matriz de estructuras?


59

Golpeé un código en el kernel de Linux:

static struct ctl_table ip_ct_sysctl_table[] = {
    {
        .procname   = "ip_conntrack_max",
        .maxlen     = sizeof(int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec,
    },
    // ...
    {
        .procname   = "ip_conntrack_log_invalid",
        .maxlen     = sizeof(unsigned int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec_minmax,
        .extra1     = &log_invalid_proto_min,
        .extra2     = &log_invalid_proto_max,
    },
    { }
};

Aquí una matriz de estructuras termina con { }. ¿Para qué se agregó?
Por cierto, un poco por encima de este código hay otra matriz de estructuras , pero sin llaves vacías al final.

¿Cuándo debo usar llaves vacías al final de una matriz de estructuras?


1
Hmm, ¿qué pasa si se agrega para señalar el final de la matriz como 0 señalizar el final de la cadena? Solo adivinando.
Eraklon

44
Esta es una extensión GCC no estándar. Y como tal, es muy probable que venga con poca o ninguna documentación ... Acabo de leer todos los documentos y no puedo encontrar nada sobre las listas de inicializadores de estructuras vacías. Sin embargo, se compila, a menos que fuerce ISO estricto con -pedantic.
Lundin

99
De todos modos, es un valor "centinela", un elemento con todo configurado en cero / NULL para marcar el final de la matriz.
Lundin

Los centinelas también son comunes en los módulos de extensión CPython .
MaxPowers

Respuestas:


38

Este cambio en particular fue parte de la red sysctl: elimine la confirmación del código sysctl binario no utilizado por Eric W. Biederman, cambiando la inicialización del último elemento de la ip_ct_sysctl_tablematriz de {0}a {}(y realiza cambios similares a muchas otras inicializaciones de matriz).

Sin {0}embargo, el patrón parece haber existido durante mucho más tiempo, y ambos, {0}o {}la inicialización de elementos final, se conoce comúnmente (en el código fuente de Linux) como explícitamente Terminating entry, por lo que es probable que exista un patrón que permita consumir estas matrices sin conocer su longitud, consumo de terminación al golpear la entrada de terminación inicializada cero. Por ejemplo, para las matrices similares en sound/aoa/fabrics/snd-aoa-fabric-layout.cla intención de la inicialización cero, incluso se menciona explícitamente en un comentario, por ejemplo:

static struct codec_connection toonie_connections[] = {
  {
      .connected = CC_SPEAKERS | CC_HEADPHONE,
      .codec_bit = 0,
  },
  {} /* terminate array by .connected == 0 */
};

11
Sería interesante conocer su justificación para descartar el estándar C en favor de una extensión GCC que sea 100% equivalente en términos de funcionalidad. Todo lo que hace es evitar que el código se compile en compiladores C estándar. Es decir, supuestamente 100% equivalente porque gcc no parece documentar esta característica ... Esta no es una matriz de longitud cero, es una lista de inicializadores vacía.
Lundin

@Lundin no int arr[] = {}(dado que estamos usando la extensión de inicializador vacía GNU) da como resultado una matriz vacía; es decir, el tamaño del arrser 0?
dfri

1
@Lundin: Sin embargo, la página cppreference está en conflicto con la redacción de ISO / IEC 9899: 2011, que permite eso (§6.7.9 (21)). Sin duda, los inicializadores son "menos" que los miembros del agregado. Así que esa no es una extensión de compilador queer, sino legítima C.
Damon

2
@Damon No es C válido y es bien conocido ... compile con gcc -pedantic-errors. Para comprender por qué, debe leer la sintaxis real de una lista de inicializadores, la parte superior de 6.7.9. Debe haber al menos un inicializador. Explicado aquí: stackoverflow.com/questions/17589533/… . Específicamente { initializer-list }entonces la lista de inicializadores: designation(opt) initializeroinitializer-list , designation(opt) initializer
Lundin

2
@Lundin En esta instancia específica, no tengo idea. Pero las extensiones gcc se usan ampliamente en el kernel de Linux.
Bobsburner

20

Probablemente esté familiarizado con las cadenas terminadas en cero. ctl_table ip_ct_sysctl_table[]es una matriz terminada en cero, es decir, la última entrada de la matriz tiene miembros completamente cero.


1
Entonces, al recorrer la matriz, sabe que ha llegado al final cuando, por ejemplo, procnamees nulo o maxlenes cero.
Paul Ogilvie

1
@PaulOgilvie: Bueno, el ejemplo está incompleto. procnamepodría ser char[100]en cuyo caso es "", no nulo. Pero por lo demás sí.
MSalters

13

¿Cuál es la necesidad de llaves vacías '{}' al final de la matriz de estructuras?

Para ser claros: las "llaves vacías '{}' al final de la matriz de estructuras" no son necesarias para satisfacer los requisitos de sintaxis de C.

¿Cuándo debo usar llaves vacías al final de una matriz de estructuras?

Cuando el código quiere un valor centinela .

A veces es útil que el programa tenga un elemento de matriz final de todos los ceros, ciertamente para detectar el final. La necesidad proviene del uso de la aplicación de la matriz ctl_table ip_ct_sysctl_table[], no de una necesidad de lenguaje C.


9

Es un elemento inicializado al cero al final de la matriz para aumentar el número de elementos de la matriz en uno.

Considere esta pequeña demostración:

#include <stdio.h>

struct Test
{
  int x;
  int y;
} arr[] =
{
    {1,2},
    {3,4},
//  {}
};

int main(void) {
    printf("%zu\n", sizeof(arr) / sizeof(arr[0]));
    return 0;
}

El tamaño de la arrmatriz cambiará si elimina el comentario {}al final de la lista de inicialización de la matriz.

Salidas:

Con // {}(la matriz tiene 2 elementos)

2

Con {}(la matriz tiene 3 elementos)

3

Explicación adicional:

La ip_ct_sysctl_tablematriz solo se usa en un lugar, es decir, aquí:

in->ctl_table = kmemdup(ip_ct_sysctl_table,
                sizeof(ip_ct_sysctl_table),
                GFP_KERNEL);

El extra {}aumenta el tamaño total ip_ct_sysctl_table.


1
Eso no es "para aumentar el número de elementos de la matriz", sino para señalar el final de la matriz.
Paul Ogilvie

66
LOL, no. La idea es que hasta ahora nadie ha podido explicarlo completamente, con absoluta certeza. La declaración de certeza más cercana es simplemente que { }es un inicializador. Pero el por qué aún no está claro. Por lo tanto, por ahora de todos modos, la palabra probablemente sea ​​una buena idea. :)
ryyker
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.