Pregunta

No he usado mucho C en los últimos años.Cuando leo esta pregunta Hoy me encontré con una sintaxis de C con la que no estaba familiarizado.

aparentemente en C99 la siguiente sintaxis es válida:

void foo(int n) {
    int values[n]; //Declare a variable length array
}

Esta parece una característica bastante útil.¿Hubo alguna vez una discusión sobre agregarlo al estándar C++ y, de ser así, por qué se omitió?

Algunas posibles razones:

  • Difícil de implementar para los proveedores de compiladores
  • Incompatible con alguna otra parte del estándar.
  • La funcionalidad se puede emular con otras construcciones de C++.

El estándar C++ establece que el tamaño de la matriz debe ser una expresión constante (8.3.4.1).

Sí, por supuesto me doy cuenta de que en el ejemplo del juguete se podría utilizar std::vector<int> values(m);, pero esto asigna memoria del montón y no de la pila.Y si quiero una matriz multidimensional como:

void foo(int x, int y, int z) {
    int values[x][y][z]; // Declare a variable length array
}

el vector La versión se vuelve bastante torpe:

void foo(int x, int y, int z) {
    vector< vector< vector<int> > > values( /* Really painful expression here. */);
}

Los sectores, filas y columnas también se distribuirán potencialmente por toda la memoria.

Mirando la discusión en comp.std.c++ Está claro que esta pregunta es bastante controvertida con algunos nombres muy importantes en ambos lados del argumento.Ciertamente no es obvio que un std::vector Siempre es una mejor solución.

¿Fue útil?

Solución

Recientemente se produjo una discusión sobre este comenzó en Bajar: ¿por qué no VLA en C ++ 0x .

Estoy de acuerdo con aquellas personas que parecen estar de acuerdo que tener que crear un potencial de gran variedad en la pila, que por lo general tiene poco espacio disponible, no es bueno. El argumento es que, si se conoce el tamaño de antemano, se puede utilizar una matriz estática. Y si usted no sabe el tamaño de antemano, que va a escribir código no seguro.

C99 VLA podría proporcionar una pequeña ventaja de ser capaz de crear arreglos pequeños, sin desperdiciar espacio o llamar a los constructores de elementos no utilizados, pero se introducirá en lugar grandes cambios en el sistema de tipos (que necesita para ser capaz de especificar los tipos en función de tiempo de ejecución valores -. esto todavía no existe en C ++ actual, a excepción de operador new tipo especificadores, pero se tratan de forma especial, de modo que el tiempo de ejecución-dad no escapa del alcance del operador new)

Puede utilizar std::vector, pero no es exactamente lo mismo, ya que utiliza memoria dinámica, y lo que es usar la propia pila-asignador no es precisamente fácil (alineación es un problema, también). También no resuelve el mismo problema, ya que un vector es un contenedor de tamaño variable, mientras que VLA son de tamaño fijo. La propuesta de la C ++ dinámico matriz está destinado introducir una solución basada en la biblioteca, como alternativa a un lenguaje basado VLA. Sin embargo, no va a ser parte de C ++ 0x, por lo que yo sé.

Otros consejos

(Antecedentes:. Tengo un poco de experiencia en la implementación de C y C ++ compiladores)

arrays de longitud variable en C99 eran básicamente un paso en falso. Con el fin de apoyar VLA, C99 tuvo que hacer las siguientes concesiones al sentido común:

  • sizeof x ya no es siempre una constante de tiempo de compilación; el compilador debe generar código a veces para evaluar una expresión sizeof en tiempo de ejecución.

  • Permitir bidimensional VLA (int A[x][y]) requiere una nueva sintaxis para declarar funciones que toman 2D VLA como parámetros:. void foo(int n, int A[][*])

  • A menos importante en el mundo de C ++, pero extremadamente importante para el público objetivo de C-programadores de sistemas embebidos, declarar una VLA significa masticando un arbitrariamente grande trozo de la pila. Se trata de un garantizada pila de desbordamiento y de choque. (Cada vez que se declara int A[n], que está implícitamente afirmando que tiene 2 GB de pila de sobra. Después de todo, si usted sabe "n es, sin duda menos de 1000 aquí", entonces usted acaba de declarar int A[1000]. Sustituyendo la n entero de 32 bits para 1000 es una admisión de que no tiene idea de lo que el comportamiento de su programa debe ser.)

Muy bien, así que vamos a pasar a hablar de C ++ ahora. En C ++, tenemos la misma fuerte distinción entre "sistema de tipo" y "sistema de valores" que hace C89 ... pero realmente hemos empezado a confiar en ella de una manera que C no tiene. Por ejemplo:

template<typename T> struct S { ... };
int A[n];
S<decltype(A)> s;  // equivalently, S<int[n]> s;

Si n no eran una constante de tiempo de compilación (es decir, si A eran de tipo modificado variablemente), a continuación, lo que en la tierra sería el tipo de S? Sería S de escriba también determinarse sólo en tiempo de ejecución?

¿Qué hay de esto:

template<typename T> bool myfunc(T& t1, T& t2) { ... };
int A1[n1], A2[n2];
myfunc(A1, A2);

El compilador debe generar código por alguna instancia de myfunc. Lo que debe ese código parece? ¿Cómo podemos generar de forma estática ese código, si no sabemos el tipo de A1 en tiempo de compilación?

Peor aún, ¿y si resulta que en tiempo de ejecución que n1 != n2, de manera que !std::is_same<decltype(A1), decltype(A2)>()? En ese caso, la llamada a myfunc ni siquiera debería compilar , porque el tipo de plantilla deducción debe fallar! ¿Cómo es posible emular ese comportamiento en tiempo de ejecución?

Básicamente, C ++ se está moviendo en la dirección de empujar cada vez más decisiones en tiempo de compilación : plantilla de generación de código, evaluación de la función constexpr, y así sucesivamente. Mientras tanto, C99 estaba ocupado tradicionalmente empujando en tiempo de compilación decisiones (por ejemplo sizeof) en el tiempo de ejecución . Con esto en mente, es lo que realmente siquiera tiene sentido gastar ningún esfuerzo tratando para integrar C99-VLA estilo en C ++?

Como todos los demás responde ya se ha señalado, C ++ proporciona una gran cantidad de mecanismos montón de asignación (std::unique_ptr<int[]> A = new int[n]; o std::vector<int> A(n); siendo los más obvios) cuando realmente quiere transmitir la idea de "no tengo idea de la cantidad de RAM que podría necesitar." Y C ++ proporciona un modelo de control de excepciones ingenioso para hacer frente a la inevitable situación que la cantidad de RAM que necesita es mayor que la cantidad de memoria que tiene. Pero es de esperar este respuesta que da una buena idea de qué estilo C99-VLA fueron no un buen ajuste para C ++ - y no realmente incluso un buen ajuste para el C99. ;)


Para más información sobre el tema, véase Alternativas N3810" para las extensiones de matriz ", documento de octubre 2013 Bjarne Stroustrup en Vlas. Punto de vista de Bjarne es muy diferente de la mía; N3810 se centra más en la búsqueda de un buen C ++ ish sintaxis de las cosas, y en desalentar el uso de matrices primas en C ++, mientras que me he centrado más en las implicaciones para metaprogramming y el sistema de tipos. No sé si él considera los metaprogramación impl / sistema de tiposicaciones resuelto, que tienen solución, o simplemente no interesantes.

Siempre se puede utilizar alloca () para asignar memoria en la pila en tiempo de ejecución, si hubiese deseado:

void foo (int n)
{
    int *values = (int *)alloca(sizeof(int) * n);
}

Al ser asignado en la pila implica que automáticamente se libera cuando la pila se desenrolla.

Nota rápida: Como se menciona en la página del manual de Mac OS X para alloca (3), "La función alloca () es la máquina y el compilador dependiente, su uso es dis-couraged." Para que lo sepas.

En mi propio trabajo, me he dado cuenta de que cada vez que he querido algo así como matrices automáticos de longitud variable o alloca (), que no le importaba que la memoria se encuentra físicamente en la pila de la CPU, sólo que se trataba de algún asignador de pila que no incurrió en viajes lentos a la pila general. Así que tengo un objeto para cada subproceso que posee parte de la memoria de la que puede empujar / pop buffers de tamaño variable. En algunas plataformas que permiten que esto crezca a través de la MMU. Otras plataformas tienen un tamaño fijo (por lo general acompañado por una pila cpu tamaño fijo, así porque no mmu). Una plataforma de trabajo con (una consola de juegos portátil) tiene poca pila CPU preciosa de todas formas, ya que reside en la escasa memoria, rápido.

No estoy diciendo que empujar buffers de tamaño variable en la pila de la CPU no es necesaria. Sinceramente me sorprendió cuando yo descubrí que no era normal, ya que sin duda parece que el concepto encaja en el idioma lo suficientemente bien. Para mí, sin embargo, los requisitos "de tamaño variable" y "debe estar ubicado físicamente en la pila de la CPU" nunca han llegado juntos. Ha sido cuestión de velocidad, por lo que hizo que mi propia especie de "pila paralelo para buffers de datos".

Hay situaciones en las que la asignación de memoria de pila es muy caro en comparación con las operaciones realizadas. Un ejemplo es matemáticas matriz. Si se trabaja con matrices más bien pequeñas dicen 5 a 10 elementos y hace un montón de aritmética la sobrecarga malloc va a ser muy significativa. Al mismo tiempo, haciendo que el tamaño de una constante de tiempo de compilación parece un gran desperdicio e inflexible.

Creo que C ++ es tan peligroso en sí mismo que el argumento para "tratar de no añadir más características inseguras" no es muy fuerte. Por otra parte, como C ++ que es posiblemente las características del lenguaje de programación de mayor eficiencia en tiempo de ejecución que lo hace más por lo que son siempre útiles: Las personas que escriben programas críticos de rendimiento será en gran medida el uso de C ++, y necesitan tanto el rendimiento como sea posible. Moviendo cosas del montón de apilar es una de esas posibilidades. Reducir el número de bloques de montón es otra. Permitiendo VLA como miembros de objetos serían una forma de lograr esto. Estoy trabajando en tal sugerencia. Es un poco complicado de implementar, es cierto, pero parece bastante factible.

parece que estará disponible en C ++ 14:

https://en.wikipedia.org/wiki/C%2B % 2B14 # Tiempo de ejecución-sized_one_dimensional_arrays

Actualización: No tenía en C ++ 14

.

Esto fue considerado para su inclusión en C ++ / 1x, pero fue caído (esto es una corrección a lo que dije antes).

Sería menos útil en C ++ de todos modos ya que ya tenemos std::vector para llenar este papel.

Utilice std :: vector para esto. Por ejemplo:

std::vector<int> values;
values.resize(n);

La memoria se asignará en el montón, pero esto es sólo un pequeño inconveniente rendimiento. Además, es aconsejable no asignar grandes bloques de datos en la pila, ya que es bastante limitado en tamaño.

C99 permite VLA. Y pone algunas restricciones sobre cómo declarar VLA. Para más detalles, consulte 6.7.5.2 de la norma. C ++ no permite VLA. Pero g ++ permite.

Arrays como este son parte de C99, pero no es parte de C ++ estándar. como otros han dicho, un vector es siempre una solución mucho mejor, que es probablemente la razón por arrays de tamaño variable no están en standatrd el C ++ (o en el estándar C ++ 0x propuesto).

Por cierto, para las preguntas sobre "qué" estándar de C ++ es la forma en que está, Usenet moderado grupo de noticias ++ comp.std.c es el lugar a donde ir.

Si conoce el valor en tiempo de compilación, puede hacer lo siguiente:

template <int X>
void foo(void)
{
   int values[X];

}

Editar: Puede crear un vector que utiliza un asignador de pila (alloca), ya que el asignador es un parámetro de plantilla

.

Tengo una solución que realmente trabajó para mí. No quería asignar memoria a causa de la fragmentación en una rutina que se necesita para ejecutar muchas veces. La respuesta es extremadamente peligroso, a fin de utilizarlo en su propio riesgo, pero se aprovecha de ensamblaje para reservar espacio en la pila. Mi ejemplo siguiente se utiliza una matriz de caracteres (obviamente otra variable tamaño requeriría más memoria).

void varTest(int iSz)
{
    char *varArray;
    __asm {
        sub esp, iSz       // Create space on the stack for the variable array here
        mov varArray, esp  // save the end of it to our pointer
    }

    // Use the array called varArray here...  

    __asm {
        add esp, iSz       // Variable array is no longer accessible after this point
    } 
}

Los peligros aquí son muchas, pero voy a explicar algunas de ellas: 1. Cambio del tamaño de la mitad del camino variable a través mataría a la posición de pila 2. Sobrepasar los límites de la matriz destruiría otras variables y posibles código 3. Esto no funciona en una versión de 64 bits ... necesita montaje diferente para que uno (pero una macro podría resolver ese problema). 4. compilador específico (puede tener problemas para moverse entre los compiladores). No he probado, así que realmente no sé.

Se necesita una expresión constante a declarar una matriz en C / C ++.

Para las matrices de tamaño dinámicas, es necesario asignar memoria en el montón, y luego administrar la liftime de esta memoria.

void foo(int n) {
    int* values = new int[n]; //Declare a variable length array
    [...]
    delete [] values;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top