¿Los miembros de la matriz flexible son válidos en C++?

10 minutos de lectura

En C99, puede declarar un miembro de matriz flexible de una estructura como tal:

struct blah
{
    int foo[];
};

Sin embargo, cuando alguien aquí en el trabajo intentó compilar algún código usando Clang en C++, esa sintaxis no funcionó. (Había estado trabajando con MSVC). Tuvimos que convertirlo a:

struct blah
{
    int foo[0];
};

Mirando a través del estándar C ++, no encontré ninguna referencia a matrices de miembros flexibles; siempre pense [0] fue una declaración inválida, pero aparentemente para una matriz de miembros flexibles es válida. ¿Las matrices de miembros flexibles son realmente válidas en C++? Si es así, es la declaración correcta [] o [0]?

  • ¿No puedes simplemente usar un std::vector<int> miembro y te preocupas por cosas más interesantes? ¿O es un problema de diseño?

    – fredo desbordamiento

    10 de diciembre de 2010 a las 19:59

  • Esa etiqueta de miembro de matriz flexible parece un poco… solitaria. Pero tal vez solo soy yo.

    –Marcus Borkenhagen

    10 de diciembre de 2010 a las 20:09


  • @FredOverflow: a veces es necesario tener estructuras que se puedan usar tanto en C como en C++ (las API del sistema son un ejemplo muy común).

    – Michael Burr

    10 de diciembre de 2010 a las 20:45

  • @FredOverflow, normalmente lo haría, pero en este caso, es necesario tener una asignación contigua para blah con un tamaño variable foo. Sin duda, es una buena pregunta de diseño en cuanto a por qué lo necesitamos en primer lugar, que no puedo abordar aquí.

    – MSN

    10 de diciembre de 2010 a las 21:11

  • En realidad, esa construcción específica es ilegal según el estándar C99como dice: “como caso especial, el último elemento de una estructura con más de uno el miembro nombrado puede tener un tipo de matriz incompleta; esto se llama un miembro de matriz flexible.” Entonces blah necesita un miembro extra, uno antes foopara ser válido en C99.

    – Hola Adios

    18 oct 2020 a las 20:24


¿Los miembros de la matriz flexible son validos en C
Martín contra Löwis

C++ se estandarizó por primera vez en 1998, por lo que es anterior a la adición de miembros de matriz flexibles a C (que era nuevo en C99). Hubo un corrigendum para C++ en 2003, pero eso no agregó ninguna característica nueva relevante. La próxima revisión de C++ (C++2b) aún está en desarrollo, y parece que aún no se le han agregado miembros de matriz flexibles.

  • ¿Puedes actualizar esta respuesta? Parece que C++11 no agregó miembros de matriz flexibles (§9.2/9), y parece que C++14 será el mismo.

    – Adam Rosenfield

    12 de noviembre de 2014 a las 7:03

  • Y C ++ 17 tampoco los tiene, pero creo que todavía se están investigando, ¿así que tal vez C ++ 2a?

    – Daniel H.

    7 de noviembre de 2017 a las 23:01

  • Cualquier forma de hacer GCC g++ ¿dar un aviso? -std=c++xx -Wall -Wextra no es suficiente. Aterrador 🙁

    – Ciro Santilli Путлер Капут 六四事

    25 de octubre de 2018 a las 16:49


  • @CiroSantilli新疆改造中心六四事件法轮功 Si no recuerdo mal -Wpedantic o -pedantic activará la advertencia.

    – Werner Henze

    04/01/2019 a las 14:50

  • Todavía nada en C++ 20, aunque existía un documento: thephd.github.io/vendor/future_cxx/papers/d1039.html

    – Llama de fuego

    7 oct 2019 a las 15:36

1646971873 699 ¿Los miembros de la matriz flexible son validos en C
miguel rebabas

C++ no admite miembros de arreglos flexibles de C99 al final de las estructuras, ya sea usando una notación de índice vacía o una 0 notación de índice (salvo extensiones específicas del proveedor):

struct blah
{
    int count;
    int foo[];  // not valid C++
};

struct blah
{
    int count;
    int foo[0]; // also not valid C++
};

Hasta donde yo sé, C++0x tampoco agregará esto.

Sin embargo, si dimensiona la matriz a 1 elemento:

struct blah
{
    int count;
    int foo[1];
};

el código se compilará y funcionará bastante bien, pero es un comportamiento técnicamente indefinido. Puede asignar la memoria adecuada con una expresión que es poco probable que tenga errores de uno:

struct blah* p = (struct blah*) malloc( offsetof(struct blah, foo[desired_number_of_elements]);
if (p) {
    p->count = desired_number_of_elements;

    // initialize your p->foo[] array however appropriate - it has `count`
    // elements (indexable from 0 to count-1)
}

Por lo tanto, es portátil entre C90, C99 y C++ y funciona tan bien como los miembros de matriz flexibles de C99.

Raymond Chen hizo un buen artículo sobre esto: ¿Por qué algunas estructuras terminan con una matriz de tamaño 1?

Nota: En el artículo de Raymond Chen, hay un error tipográfico/error en un ejemplo que inicializa la matriz ‘flexible’. Debería leer:

for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
  TokenGroups->Groups[Index] = ...;
}

  • Sin embargo, incluso si asigna un exceso de memoria, aún no puede acceder de manera válida a los miembros fuera de los límites de la matriz de un elemento. El comportamiento no está definido; una implementación de C ++ estaría dentro de sus derechos para agregar verificación de límites de acuerdo con el tipo real del objeto construido.

    –CB Bailey

    10 de diciembre de 2010 a las 21:14

  • @Charles: no creo que tengas razón en eso (incluso de manera pedante), de lo contrario, el siguiente sería un comportamiento indefinido: int* p = malloc(sizeof(int)*4); p[3] = 0;.

    – Michael Burr

    10 de diciembre de 2010 a las 22:01

  • @Michael: Creo que la razón por la que Charles dijo una elemento no es porque piense que es imposible asignar una matriz, sino porque 1 es la longitud de la matriz en su estructura particular, bla. La afirmación es que desde p->foo es de tipo blah[1]luego p->foo[1] es UB. Sin embargo aunque p->foo[1] está fuera del objeto foono está fuera de la matriz de char que se asignó con mallocpor lo que es dentro de un objeto. Con yesos adecuados vía char* el acceso de lectura al menos estaría bien. Sin embargo, no puedo recordar cómo cae la jerga legal estándar.

    –Steve Jessop

    10 de diciembre de 2010 a las 22:23

  • Bueno, supongo que tendré que tragarme mis palabras. Nada menos que WG14 ha declarado que se trata de UB (Informe de defecto 51: open-std.org/Jtc1/sc22/wg14/www/docs/dr_051.html). Sin embargo, sostengo que 1) el ‘idioma más seguro’ sugerido por WG14 en DR51 es totalmente ridículo, 2) el UB se comporta como se esperaba en todas las plataformas que son importantes para mí, y 3) alternativas (que tampoco son UB ) son menos convenientes y/o más propensos a errores de uso (y, por lo tanto, es más probable que causen errores observables), por lo que probablemente continuaré usándolo. Pero ahora al menos sabré que estoy rompiendo una regla…

    – Michael Burr

    10 de diciembre de 2010 a las 22:59

  • Sí, no estaba afirmando que no sea una técnica útil, solo que no se ajusta estrictamente. Desafortunadamente, el idioma más seguro también es UB por una razón diferente. Solo puede realizar aritmética de punteros en un objeto que realmente existe y un objeto POD solo comienza a existir una vez que la memoria tiene suficiente alineación y Talla está asignado. Si algo se declara con una matriz muy grande y no asigna suficiente espacio para ello, no puede empezar a existir.

    –CB Bailey

    10 de diciembre de 2010 a las 23:46


El segundo no contendrá elementos sino que apuntará justo después blah. Así que si tienes una estructura como esta:

struct something
{
  int a, b;
  int c[0];
};

puedes hacer cosas como esta:

struct something *val = (struct something *)malloc(sizeof(struct something) + 5 * sizeof(int));
val->a = 1;
val->b = 2;
val->c[0] = 3;

En este caso c se comportará como una matriz con 5 ints pero los datos en la matriz estarán después de la something estructura.

El producto en el que estoy trabajando usa esto como una cadena de tamaño:

struct String
{
  unsigned int allocated;
  unsigned int size;
  char data[0];
};

Debido a las arquitecturas admitidas, esto consumirá 8 bytes más allocated.

Por supuesto, todo esto es C, pero g ++, por ejemplo, lo acepta sin problemas.

  • Eso es bastante interesante. Me imagino que nunca podrías pasar esta estructura a una función como valor, ¿verdad? ya que eso probablemente solo pasaría el sizeof(String) que no tendría en cuenta el tamaño que asignó para data. Pero debería funcionar siempre y cuando solo lo pases como referencia o puntero, ¿no es así?

    – filipe

    10 de diciembre de 2010 a las 20:15

  • Esto no es verdad. T[0] no es un especificador de tipo válido en C ni en C++. tienes que usar T[].

    – Johannes Schaub – litb

    11 de diciembre de 2010 a las 10:35

  • @Johannes es válido en C99, ver open-std.org/jtc1/sc22/wg14/www/newinc9x.htm

    – terminal

    11 de diciembre de 2010 a las 11:10

  • Of course all this is C but g++ for example accepts it without a hitch. Eso es bueno para g++. Según el Estándar, sigue siendo UB y, por lo tanto, debe matarse con fuego. @terminus Ese enlace solo menciona la existencia de miembros de matriz flexibles en C99 (pero nunca en C++); no apoya su afirmación de que [0] es una sintaxis válida para declararlos, que no lo es.

    – subrayado_d

    3 de julio de 2016 a las 5:09


  • Las matrices de longitud cero nunca fueron válidas en ninguna versión de C. No puede tener VLA de longitud cero. Es una extensión no estándar de gcc. Este código no se compilará en C estándar ni en C++ estándar.

    – Lundin

    16 dic 2016 a las 15:15

Si puede restringir su aplicación para que solo requiera unos pocos tamaños conocidos, entonces puede lograr efectivamente una matriz flexible con una plantilla.

template <typename BASE, typename T, unsigned SZ>
struct Flex : public BASE {
    T flex_[SZ];
};

si solo quieres

struct blah { int foo[]; };

entonces no necesita la estructura en absoluto y simplemente puede tratar con una matriz int malloc’ed/new’ed.

Si tienes algunos miembros al principio:

struct blah { char a,b; /*int foo[]; //not valid in C++*/ };

entonces en C++, supongo que podrías reemplazar foo con un foo función miembro:

struct blah { alignas(int) char a,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); } };

Ejemplo de uso:

#include <stdlib.h>
struct blah { 
    alignas(int) char a,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); }
};
int main()
{
    blah *b = (blah*)malloc(sizeof(blah)+10*sizeof(int));
    if(!b) return 1;
    b->foo()[1]=1;
}

Una propuesta está en marcha y podría convertirse en una futura versión de C++. Ver http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1039r0.html para más detalles (la propuesta es bastante nueva, por lo que está sujeta a cambios)

¿Los miembros de la matriz flexible son validos en C
San Antario

Enfrenté el mismo problema para declarar un miembro de matriz flexible que se puede usar desde el código C++. mirando a través glibc encabezados Descubrí que hay algunos usos de miembros de matriz flexibles, por ejemplo, en struct inotify que se declara de la siguiente manera (se omiten los comentarios y algunos miembros no relacionados):

struct inotify_event
{
  //Some members
  char name __flexarr;
};

los __flexarr macro, a su vez se define como

/* Support for flexible arrays.
   Headers that should use flexible arrays only if they're "real"
   (e.g. only if they won't affect sizeof()) should test
   #if __glibc_c99_flexarr_available.  */
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
# define __flexarr  []
# define __glibc_c99_flexarr_available 1
#elif __GNUC_PREREQ (2,97)
/* GCC 2.97 supports C99 flexible array members as an extension,
   even when in C89 mode or compiling C++ (any version).  */
# define __flexarr  []
# define __glibc_c99_flexarr_available 1
#elif defined __GNUC__
/* Pre-2.97 GCC did not support C99 flexible arrays but did have
   an equivalent extension with slightly different notation.  */
# define __flexarr  [0]
# define __glibc_c99_flexarr_available 1
#else
/* Some other non-C99 compiler.  Approximate with [1].  */
# define __flexarr  [1]
# define __glibc_c99_flexarr_available 0
#endif

no estoy familiarizado con MSVC compilador, pero probablemente tendría que agregar una macro condicional más dependiendo de MSVC versión.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad