En programación informática , un arreglo de longitud variable ( VLA ), también llamado de tamaño variable o de tamaño en tiempo de ejecución , es una estructura de datos de arreglo cuya longitud se determina en tiempo de ejecución , en lugar de en tiempo de compilación . [ 1 ] En el lenguaje C , se dice que el VLA tiene un tipo de datos modificado variablemente que depende de un valor (véase Tipo dependiente ).
El objetivo principal de las VLA es simplificar la programación de algoritmos numéricos .
Los lenguajes de programación que admiten VLA incluyen Ada , ALGOL 68 (para filas no flexibles), APL , C# (como matrices asignadas en pila en modo no seguro), COBOL , Fortran 90, J y Object Pascal (el lenguaje utilizado en Delphi y Lazarus , que utiliza FPC). C99 introdujo soporte para VLA, aunque posteriormente fueron relegados en C11 a una característica condicional, que las implementaciones no están obligadas a admitir; [ 2 ] [ 3 ] en algunas plataformas, los VLA podían implementarse anteriormente con alloca()o funciones similares.
Los arreglos expandibles (también llamados arreglos dinámicos ) suelen ser más útiles que los arreglos de longitud variable (VLA) porque pueden hacer todo lo que hacen los VLA y, además, permiten expandir el arreglo en tiempo de ejecución. Por esta razón, muchos lenguajes de programación ( JavaScript , Java , Python , R , etc.) solo admiten arreglos expandibles. Incluso en lenguajes que admiten arreglos de longitud variable, a menudo se recomienda evitar el uso de arreglos de longitud variable (basados en pila) y, en su lugar, usar arreglos dinámicos ( basados en montón ). [ 4 ]
Memoria
Asignación
- La colección de compiladores GNU (GCC) para C asigna memoria para VLA con duración de almacenamiento automática en la pila . [ 5 ] Esta es la opción más rápida y directa en comparación con la asignación en el montón, y es utilizada por la mayoría de los compiladores.
- Las VLA también se pueden asignar en el montón y acceder a ellas internamente mediante un puntero a este bloque.
Implementación
Ada
El siguiente es el mismo ejemplo en Ada . Los arreglos de Ada conservan sus límites, por lo que no es necesario pasar la longitud a la función Process.
El tipo Vals_Type es una matriz ( rango positivo <>) de tipo Float ;función Read_And_Process ( N : Integer ) return Float is Vals : Vals_Type ( 1 .. N ); begin for I in 1 .. N loop Vals ( I ) := Read_Val ; end loop ; return Process ( Vals ); end Read_And_Process ;Fortran 90
La función equivalente de Fortran 90 es
función leer_y_procesar ( n ) resultado ( o ) entero , intención ( in ) :: n real :: oreal , dimensión ( n ) :: vals entero :: ihacer i = 1 , n vals ( i ) = read_val () fin hacer o = process ( vals ) fin función read_and_processal utilizar la función de Fortran 90 de comprobar las interfaces de los procedimientos en tiempo de compilación; por otro lado, si las funciones utilizan la interfaz de llamada anterior a Fortran 90, las funciones (externas) deben declararse primero y la longitud del array debe pasarse explícitamente como argumento (como en C):
función leer_y_procesar ( n ) resultado ( o ) entero , intención ( in ) :: n real :: oreal , dimensión ( n ) :: vals real :: read_val , proceso entero :: ihacer i = 1 , n vals ( i ) = read_val () fin hacer o = process ( vals , n ) fin función read_and_processdo
Ciertos estándares del lenguaje C requieren compatibilidad con arreglos de longitud variable. Los arreglos de longitud variable nunca formaron parte del estándar del lenguaje C++ .
La siguiente función C99 asigna un array de longitud variable de un tamaño especificado, lo llena con valores de punto flotante y luego lo pasa a otra función para su procesamiento. Dado que el array se declara como una variable automática, su ciclo de vida finaliza cuando la readAndProcess()función regresa.
float readAndProcess ( int n ) { float vals [ n ];for ( int i = 0 ; i < n ; ++ i ) { scanf ( "%f" , & vals [ i ]); }devolver proceso ( n , vals ); }En C99, el parámetro length debe ir antes del parámetro de matriz de longitud variable en las llamadas a funciones. [ 1 ] En C11, __STDC_NO_VLA__se define una macro si no se admite VLA. [ 6 ] El estándar C23 vuelve a hacer obligatorios los tipos VLA. Solo la creación de objetos VLA con duración de almacenamiento automática es opcional. [ 7 ] GCC tenía VLA como una extensión antes de C99, una que también se extiende a su dialecto C++.
Linus Torvalds ha expresado su descontento en el pasado por el uso de VLA para matrices con tamaños pequeños predeterminados porque genera código ensamblador de menor calidad. [ 8 ] Con el kernel Linux 4.20, el kernel Linux está prácticamente libre de VLA. [ 9 ]
Aunque C11 no nombra explícitamente un límite de tamaño para los VLA, algunos creen que debería tener el mismo tamaño máximo que todos los demás objetos, es decir, SIZE_MAXbytes. [ 10 ] Sin embargo, esto debe entenderse en el contexto más amplio de los límites del entorno y la plataforma, como el tamaño típico de página de protección de pila de 4 KiB, que es muchos órdenes de magnitud menor que SIZE_MAX.
Es posible tener un objeto VLA con almacenamiento dinámico utilizando un puntero a una matriz.
#include <stdio.h> #include <stdlib.h>float readAndProcess ( int n ) { float ( * vals )[ n ] = malloc ( sizeof ( float [ n ]));for ( int i = 0 ; i < n ; ++ i ) { scanf ( "%f" , & ( * vals )[ i ]); }float ret = process ( n , * vals ); free ( vals ); return ret ; }C++
Si bien C++ no admite arreglos de longitud variable asignados en la pila (a diferencia de C, que sí los admite), algunas extensiones del compilador, como las de GCC y Clang , pueden permitirlos . En caso contrario, un arreglo se asigna en el montón, aunque un tipo de colección como ` std::vector<array>` probablemente sea mejor. Esto se debe a que los tipos de colección existentes en C++ utilizan automáticamente la inicialización de adquisición de recursos (RAII) y se liberan automáticamente al salir del ámbito.
int * createIntArray ( size_t n ) { return new int [ n ]; }int main ( int argc , char * argv []) { int * a = createIntArray ( 5 ); // hacer algo con el arrayeliminar [] a ; }DO#
El siguiente fragmento de código C# declara una matriz de enteros de longitud variable. Antes de la versión 7.2 de C#, se requería un puntero a la matriz, lo que exigía un contexto "unsafe". La palabra clave "unsafe" requiere que el ensamblado que contiene este código se marque como no seguro.
void inseguro DeclareStackBasedArrayUnsafe ( int size ) { int * p = stackalloc int [ size ]; p [ 0 ] = 123 ; }C# versión 7.2 y posteriores permiten asignar el array sin la palabra clave "unsafe", mediante el uso de la System.Span<T>característica. [ 11 ]
usando el sistema ;void DeclareStackBasedArraySafe ( int size ) { Span < int > a = stackalloc int [ size ]; a [ 0 ] = 123 ; }COBOL
El siguiente fragmento COBOL declara una matriz de longitud variable de registros DEPT-PERSONcuya longitud (número de miembros) está especificada por el valor de PEOPLE-CNT:
DIVISIÓN DE DATOS . SECCIÓN DE ALMACENAMIENTO DE TRABAJO . 01 PERSONAS DEPARTAMENTO . 05 CANTIDAD DE PERSONAS PIC S9(4) BINARIO . 05 PERSONA DEPARTAMENTO OCURRE DE 0 A 20 VECES DEPENDIENDO DE CANTIDAD DE PERSONAS . 10 NOMBRE DE PERSONA PIC X(20) . 10 SALARIO DE PERSONA PIC S9(7)V99 DECIMAL EMPAQUETADO .La VLA de COBOL , a diferencia de la de otros lenguajes mencionados aquí, es segura porque COBOL requiere especificar el tamaño máximo de la matriz. En este ejemplo, DEPT-PERSONno puede tener más de 20 elementos, independientemente del valor de PEOPLE-CNT.
Java
Java fija el tamaño de los arrays una vez creados, pero su tamaño puede determinarse en tiempo de ejecución.
public class Example { public static int [] createIntArray ( int size ) { return new int [ size ] ; }public static void main ( String [] args ) { try { String s = IO . readln ( "Ingrese un entero para un tamaño de arreglo: " ); int size = Integer . parseInt ( s ); int [] a = createArray ( size ); System . out . printf ( "int[] de tamaño %d creado" , size ); } catch ( NumberFormatException e ) { System . err . printf ( "Entero leído no válido: %s%n" , e . getMessage ()); } } }Objeto Pascal
Los arreglos dinámicos de Object Pascal se asignan en el montón. [ 12 ]
En este lenguaje, se denomina arreglo dinámico. La declaración de dicha variable es similar a la de un arreglo estático, pero sin especificar su tamaño. El tamaño del arreglo se define al momento de su uso.
programa CrearArrayDynamicDeNúmeros ( Tamaño : Entero ) ; var NumberArray : array de LongWord ; begin EstablecerLongitud ( NumberArray , Tamaño ) ; NumberArray [ 0 ] := 2020 ; end .Para eliminar el contenido de una matriz dinámica, basta con asignarle un tamaño de cero.
... EstablecerLongitud ( NumberArray , 0 ) ; ...Referencias
- 1 2 "Matrices de longitud variable" . Archivado del original el 26/01/2018.
- ↑ "Longitud variable: uso de la colección de compiladores GNU (GCC)" .
- ↑ ISO 9899:2011 Lenguajes de programación – C 6.7.6.2 4.
- ↑ Raymond, Eric S. (2000). "Raymond Software Release Practice Howto: 6. Buenas prácticas de desarrollo" . The Linux Documentation Project .
- ↑ "Opciones de generación de código - El compilador GNU Fortran" .
- ↑ § 6.10.8.3 de la norma C11 (n1570.pdf)
- ↑ § 6.10.9.3 de la norma C23 (n3054.pdf)
- ↑ Torvalds, Linus (7 de marzo de 2018). "LKML: Linus Torvalds: Re: Eliminación de VLA (antes Re: [ RFC 2/2 ] lustre: usar VLA_SAFE)" . Núcleo de Linux (Lista de correo).
- ↑ "El kernel de Linux ahora está libre de VLA: una victoria para la seguridad, menos sobrecarga y mejor para Clang - Phoronix" . www.phoronix.com .
- ↑ §6.5.3.4 y §7.20.3 de la norma C11 (n1570.pdf)
- ↑ "operador stackalloc (referencia de C#)" . Microsoft. 10 de julio de 2024.
- ↑ Michaël Van Canneyt. "Guía de referencia de Free Pascal: Matrices dinámicas" .
- Matrices
- Comparación de lenguajes de programación