Array declaration
Declara un objeto de tipo array.
Contenidos |
Sintaxis
Una declaración de array es cualquier declaración simple cuyo declarador tiene la forma
noptr-declarator
[
expr
(opcional)
]
attr
(opcional)
|
|||||||||
| noptr-declarator | - |
cualquier
declarator
válido, pero si comienza con
*
,
&
, o
&&
, debe estar rodeado por paréntesis (de lo contrario, todo el declarator se trata como un
pointer declarator
o
reference declarator
).
|
| expr | - | una integral constant expression (until C++14) una converted constant expression de tipo std::size_t (since C++14) , que evalúa a un valor mayor que cero |
| attr | - | (since C++11) lista de attributes |
Una declaración de la forma
T a
[
N
]
;
, declara
a
como un objeto array
object
que consiste en
N
objetos de tipo
T
asignados contiguamente. Los elementos de un array se numeran
0
, …,
N
-
1
, y pueden accederse con el
operador de subíndice []
, como en
a
[
0
]
, …,
a
[
N
-
1
]
.
Los arrays pueden construirse a partir de cualquier tipo fundamental (excepto void ), punteros , punteros a miembros , clases , enumeraciones , o desde otros arrays de tamaño conocido (en cuyo caso se dice que el array es multidimensional). En otras palabras, solo los tipos de objeto excepto los tipos array de tamaño desconocido pueden ser tipos de elemento de los tipos array. Los tipos array de tipo de elemento incompleto también son tipos incompletos.
|
El especificador
posiblemente
restringido
(desde C++20)
|
(desde C++11) |
No existen arrays de referencias ni arrays de funciones.
Aplicar cv-qualifiers a un tipo de array (a través de typedef o manipulación de tipos de plantilla) aplica los calificadores al tipo de elemento, pero cualquier tipo de array cuyos elementos sean de tipo calificado-cv se considera que tiene la misma calificación-cv.
// a y b tienen el mismo tipo calificado como const "array de 5 const char" typedef const char CC; CC a[5] = {}; typedef char CA[5]; const CA b = {};
Cuando se utiliza con new[]-expression , el tamaño de un array puede ser cero; dicho array no tiene elementos:
int* p = new int[0]; // acceder a p[0] o *p es indefinido delete[] p; // la limpieza aún es requerida
Asignación
Los objetos de tipo array no pueden ser modificados en su totalidad: aunque son lvalues (por ejemplo, se puede tomar la dirección de un array), no pueden aparecer en el lado izquierdo de un operador de asignación:
int a[3] = {1, 2, 3}, b[3] = {4, 5, 6}; int (*p)[3] = &a; // correcto: se puede tomar la dirección de a a = b; // error: a es un array struct { int c[3]; } s1, s2 = {3, 4, 5}; s1 = s2; // correcto: el operador de asignación de copia definido implícitamente // puede asignar miembros de datos de tipo array
Decaimiento de array a puntero
Existe una conversión implícita de lvalues y rvalues de tipo array a rvalues de tipo puntero: construye un puntero al primer elemento de un array. Esta conversión se utiliza siempre que los arrays aparecen en contextos donde no se esperan arrays, pero sí punteros:
#include <iostream> #include <iterator> #include <numeric> void g(int (&a)[3]) { std::cout << a[0] << '\n'; } void f(int* p) { std::cout << *p << '\n'; } int main() { int a[3] = {1, 2, 3}; int* p = a; std::cout << sizeof a << '\n' // imprime el tamaño del array << sizeof p << '\n'; // imprime el tamaño de un puntero // donde los arrays son aceptables pero los punteros no, solo pueden usarse arrays g(a); // correcto: la función toma un array por referencia // g(p); // error for (int n : a) // correcto: los arrays pueden usarse en bucles range-for std::cout << n << ' '; // imprime los elementos del array // for (int n : p) // error // std::cout << n << ' '; std::iota(std::begin(a), std::end(a), 7); // correcto: begin y end toman arrays // std::iota(std::begin(p), std::end(p), 7); // error // donde los punteros son aceptables pero los arrays no, ambos pueden usarse: f(a); // correcto: la función toma un puntero f(p); // correcto: la función toma un puntero std::cout << *a << '\n' // imprime el primer elemento << *p << '\n' // mismo << *(a + 1) << ' ' << a[1] << '\n' // imprime el segundo elemento << *(p + 1) << ' ' << p[1] << '\n'; // mismo }
Arreglos multidimensionales
Cuando el tipo de elemento de un array es otro array, se dice que el array es multidimensional:
// arreglo de 2 arreglos de 3 enteros cada uno int a[2][3] = {{1, 2, 3}, // puede verse como una matriz 2 × 3 {4, 5, 6}}; // con disposición row-major
Tenga en cuenta que cuando se aplica la conversión de array a puntero, un array multidimensional se convierte a un puntero a su primer elemento (por ejemplo, un puntero a su primera fila o a su primer plano): la conversión de array a puntero se aplica solo una vez.
int a[2]; // array de 2 int int* p1 = a; // a decae a un puntero al primer elemento de a int b[2][3]; // array de 2 arrays de 3 int // int** p2 = b; // error: b no decae a int** int (*p2)[3] = b; // b decae a un puntero a la primera fila de 3 elementos de b int c[2][3][4]; // array de 2 arrays de 3 arrays de 4 int // int*** p3 = c; // error: c no decae a int*** int (*p3)[3][4] = c; // c decae a un puntero al primer plano de 3 × 4 elementos de c
Arreglos de límite desconocido
Si expr se omite en la declaración de un array, el tipo declarado es "array de límite desconocido de T", que es un tipo de tipo incompleto , excepto cuando se utiliza en una declaración con un inicializador de agregado :
extern int x[]; // el tipo de x es "array de límite desconocido de int" int a[] = {1, 2, 3}; // el tipo de a es "array de 3 int"
Debido a que los elementos de un array no pueden ser arrays de límite desconocido, los arrays multidimensionales no pueden tener límite desconocido en una dimensión distinta a la primera:
extern int a[][2]; // correcto: array de límite desconocido de arrays de 2 int extern int b[2][]; // error: array tiene tipo de elemento incompleto
Si hay una declaración previa de la entidad en el mismo ámbito en el que se especificó el límite, un límite de arreglo omitido se toma igual que en esa declaración anterior, y de manera similar para la definición de un miembro de datos estático de una clase:
extern int x[10]; struct S { static int y[10]; }; int x[]; // CORRECTO: el límite es 10 int S::y[]; // CORRECTO: el límite es 10 void f() { extern int x[]; int i = sizeof(x); // error: tipo de objeto incompleto }
Se pueden formar referencias y punteros a arrays de límite desconocido, pero no pueden (until C++20) y pueden (since C++20) ser inicializados o asignados desde arrays y punteros a arrays de límite conocido. Nótese que en el lenguaje de programación C, los punteros a arrays de límite desconocido son compatibles con punteros a arrays de límite conocido y por lo tanto son convertibles y asignables en ambas direcciones.
extern int a1[]; int (&r1)[] = a1; // correcto int (*p1)[] = &a1; // correcto int (*q)[2] = &a1; // error (pero correcto en C) int a2[] = {1, 2, 3}; int (&r2)[] = a2; // correcto (desde C++20) int (*p2)[] = &a2; // correcto (desde C++20)
Los punteros a arrays de límite desconocido no pueden participar en aritmética de punteros y no pueden usarse en el lado izquierdo del operador de subíndice , pero pueden ser desreferenciados.
Valores r de array
Aunque los arrays no pueden ser devueltos de funciones por valor y no pueden ser objetivos de la mayoría de expresiones de cast, los prvalues de array pueden formarse usando un alias de tipo para construir un temporal de array usando cast funcional con inicialización entre llaves .
|
Al igual que los prvalues de clase, los prvalues de array se convierten en xvalues mediante materialización temporal cuando se evalúan. |
(since C++17) |
Array xvalues pueden formarse directamente accediendo a un miembro array de un rvalue de clase o utilizando std::move u otra conversión o llamada a función que devuelva una referencia a rvalue.
#include <iostream> #include <type_traits> #include <utility> void f(int (&&x)[2][3]) { std::cout << sizeof x << '\n'; } struct X { int i[2][3]; } x; template<typename T> using identity = T; int main() { std::cout << sizeof X().i << '\n'; // tamaño del array f(X().i); // correcto: se enlaza a xvalue // f(x.i); // error: no se puede enlazar a lvalue int a[2][3]; f(std::move(a)); // correcto: se enlaza a xvalue using arr_t = int[2][3]; f(arr_t{}); // correcto: se enlaza a prvalue f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // correcto: se enlaza a prvalue }
Salida:
24 24 24 24 24
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 393 | C++98 |
un puntero o referencia a un array de límite
desconocido no podía ser un parámetro de función |
permitido |
| CWG 619 | C++98 |
cuando se omitía, el límite de un array no podía
inferirse de una declaración previa |
inferencia permitida |
| CWG 2099 | C++98 |
el límite de un miembro de datos estático de array no
podía omitirse incluso si se proporcionaba un inicializador |
omisión permitida |
| CWG 2397 | C++11 | auto no podía usarse como tipo de elemento | permitido |
Véase también
|
Documentación de C
para
Declaración de array
|