426 votos

¿Cómo puedo utilizar las matrices en C++?

C++ heredado matrices de C, donde se utilizan prácticamente en todas partes. C++ proporciona abstracciones que son más fáciles de usar y menos propenso a errores (std::vector<T> debido a que C++y 98 std::array<T, n> desde C++11), por lo que la necesidad de matrices no surge tan a menudo como se hace en C. sin Embargo, cuando se lee el código heredado o interactuar con una biblioteca escrita en C, usted debe tener una firme comprensión sobre cómo funcionan los arreglos.

Este FAQ está dividida en cinco partes:

  1. matrices en el tipo de nivel y acceder a elementos
  2. la creación de la matriz y de inicialización
  3. la asignación y transferencia de parámetros
  4. multidimensionales los arrays y matrices de punteros
  5. errores comunes al utilizar matrices

Si usted siente que algo importante faltaba en esta sección de preguntas frecuentes, escribir una respuesta y el enlace aquí como una parte adicional.

En el texto siguiente, "matriz" significa "matriz C", no de la plantilla de clase std::array. Conocimientos básicos de la C declarador de sintaxis que se asume. Tenga en cuenta que el uso del manual de new y delete como se muestra a continuación es extremadamente peligroso en la cara de excepciones, pero eso es tema de otra de las preguntas frecuentes.

(Nota: Este está destinado a ser una entrada de Stack Overflow en C++ de preguntas frecuentes. Si usted quiere a la crítica de la idea de ofrecer un documento de preguntas frecuentes en este formulario, a continuación de la publicación en el meta, la que comenzó todo este sería el lugar para hacerlo. Las respuestas a esa pregunta son monitoreados en el C++ sala de chat, donde las preguntas frecuentes idea comenzó en el primer lugar, entonces, la respuesta es muy probable que se lea por los que vinieron con la idea).

265voto

FredOverflow Puntos 88201

Matrices en el nivel de tipo de

Un tipo de matriz se denota como T[n] donde T es el tipo de elemento y n , es positivo el tamaño, el número de elementos en la matriz. El tipo de matriz es un tipo de producto de el tipo de elemento y el tamaño. Si uno o ambos de estos ingredientes diferentes, se obtiene un tipo distinto:

#include <type_traits>

static_assert(!std::is_same<int[8], float[8]>::value, "distinct element type");
static_assert(!std::is_same<int[8],   int[9]>::value, "distinct size");

Tenga en cuenta que el tamaño es parte del tipo, esto es, la matriz de tipos de diferente tamaño son incompatibles tipos que no tienen absolutamente nada que ver uno con otro. sizeof(T[n]) es equivalente a n * sizeof(T).

Matriz-a-puntero de la caries

La única "relación" entre T[n] y T[m] es que ambos tipos pueden implícitamente ser convertidos a T*, y el resultado de esta transformación es un puntero al primer elemento de la matriz. Es decir, en cualquier lugar de una T* es necesario, puede proporcionar un T[n], y el compilador se silenciosamente proporcionar ese puntero:

                  +---+---+---+---+---+---+---+---+
the_actual_array: |   |   |   |   |   |   |   |   |   int[8]
                  +---+---+---+---+---+---+---+---+
                    ^
                    |
                    |
                    |
                    |  pointer_to_the_first_element   int*

Esta conversión se conoce como "matriz-a-puntero de la corrupción", y es una importante fuente de confusión. El tamaño de la matriz se pierde en este proceso, puesto que ya no forma parte del tipo (T*). Pro: el Olvido del tamaño de una matriz en el nivel del tipo permite un puntero para que apunte al primer elemento de una matriz de cualquier tamaño. Con: Dado un puntero a la primera (o cualquier otro) de los elementos de una matriz, no hay manera de detectar cómo es de grande que la matriz es o dónde está exactamente el puntero que señala a la relativa a los límites de la matriz. Los punteros son extremadamente estúpido.

Matrices no son punteros

El compilador silenciosamente generar un puntero al primer elemento de una matriz, siempre que lo considere útil, que es, cada vez que una operación podría fallar en un array, pero tener éxito en un puntero. Esta conversión de la matriz de puntero es trivial, ya que el puntero de valor es simplemente la dirección de la matriz. Tenga en cuenta que el puntero no se almacena como parte de la propia matriz (o en cualquier otro lugar en la memoria). Un array es un puntero.

static_assert(!std::is_same<int[8], int*>::value, "an array is not a pointer");

Un importante contexto en el que una matriz no no decaer en un puntero a su primer elemento es cuando la & operador es aplicado. En ese caso, la & operador obtiene un puntero a la totalidad de la matriz, no sólo un puntero a su primer elemento. Aunque en ese caso, los valores (las direcciones) son el mismo, un puntero al primer elemento de un array y un puntero a la totalidad de la matriz son completamente distintos tipos:

static_assert(!std::is_same<int*, int(*)[8]>::value, "distinct element type");

Los siguientes arte ASCII explica esta distinción:

      +-----------------------------------+
      | +---+---+---+---+---+---+---+---+ |
+---> | |   |   |   |   |   |   |   |   | | int[8]
|     | +---+---+---+---+---+---+---+---+ |
|     +---^-------------------------------+
|         |
|         |
|         |
|         |  pointer_to_the_first_element   int*
|
|  pointer_to_the_entire_array              int(*)[8]

Nota cómo el puntero al primer elemento sólo apunta a un entero (representado como un pequeño recuadro), mientras que el puntero a la totalidad de la matriz de puntos de una matriz de 8 enteros (representado como un gran cuadro).

La misma situación se produce en las clases y es quizá más evidente. Un puntero a un objeto y un puntero a su primer miembro de datos tienen el mismo valor (la misma dirección), sin embargo, son completamente distintos tipos.

Si usted no está familiarizado con el C declarador de sintaxis, el paréntesis en el tipo int(*)[8] son esenciales:

  • int(*)[8] es un puntero a un array de 8 enteros.
  • int*[8] es una matriz de 8 punteros, cada uno de los elemento del tipo int*.

El acceso a los elementos

C++ dispone de dos variaciones sintácticas para acceder a los elementos individuales de una matriz. Ninguno de ellos es superior al otro, y debe familiarizarse con ambos.

Aritmética de punteros

Dado un puntero p para el primer elemento de un array, la expresión p+i obtiene un puntero al i-ésimo elemento de la matriz. Por eliminar ese puntero después, uno se puede acceder a elementos individuales:

std::cout << *(x+3) << ", " << *(x+7) << std::endl;

Si x denota una matriz, entonces la matriz-a-puntero caries patada, ya que la adición de una matriz y un entero es de sentido (no hay más que la operación en las matrices), pero la adición de un puntero y un entero tiene sentido:

   +---+---+---+---+---+---+---+---+
x: |   |   |   |   |   |   |   |   |   int[8]
   +---+---+---+---+---+---+---+---+
     ^           ^               ^
     |           |               |
     |           |               |
     |           |               |
x+0  |      x+3  |          x+7  |     int*

(Tenga en cuenta que implícitamente se genera puntero no tiene nombre, así que escribí x+0 con el fin de identificar.)

Si, por otro lado, x denota un puntero a la primera (o cualquier otro) de los elementos de una matriz, entonces la matriz-a-puntero de la caries no es necesario, porque el puntero en que i va a ser añadido ya existe:

   +---+---+---+---+---+---+---+---+
   |   |   |   |   |   |   |   |   |   int[8]
   +---+---+---+---+---+---+---+---+
     ^           ^               ^
     |           |               |
     |           |               |
   +-|-+         |               |
x: | | |    x+3  |          x+7  |     int*
   +---+

Tenga en cuenta que en la representación de caso, x es un puntero a variable (perceptible por la pequeña caja junto a x), pero también puede ser el resultado de una función que devuelve un puntero (o cualquier otra expresión de tipo T*).

Operador de indexación

Ya que la sintaxis *(x+i) es un poco torpe, C++ proporciona la sintaxis alternativa x[i]:

std::cout << x[3] << ", " << x[7] << std::endl;

Debido al hecho de que la suma es conmutativa, el siguiente código hace exactamente lo mismo:

std::cout << 3[x] << ", " << 7[x] << std::endl;

La definición de la indexación operador conduce a la siguiente interesante de equivalencia:

&x[i]  ==  &*(x+i)  ==  x+i

Sin embargo, &x[0] generalmente no equivalente a x. El primero es un puntero, el último de una matriz. Sólo cuando el contexto de los desencadenantes de la matriz-a-puntero desintegrarse x y &x[0] utilizarse de manera intercambiable. Por ejemplo:

T* p = &array[0];  // rewritten as &*(array+0), decay happens due to the addition
T* q = array;      // decay happens due to the assignment

En la primera línea, el compilador detecta una asignación de un puntero a un puntero, que trivialmente se realiza correctamente. En la segunda línea, se detecta una asignación a partir de una matriz a un puntero. Ya que esto es de sentido (pero puntero a puntero de asignación de sentido), la matriz-a-puntero de caries activa como de costumbre.

Rangos de

Una matriz de tipo T[n] ha n elementos, indexado desde 0 a n-1; no hay ningún elemento n. Y, sin embargo, para apoyar a la mitad-abra rangos (donde el principio es inclusiva y el final es exclusivo), C++ permite el cálculo de un puntero a la (inexistente) de n-ésimo elemento, pero es ilegal para eliminar la referencia que el puntero:

   +---+---+---+---+---+---+---+---+....
x: |   |   |   |   |   |   |   |   |   .   int[8]
   +---+---+---+---+---+---+---+---+....
     ^                               ^
     |                               |
     |                               |
     |                               |
x+0  |                          x+8  |     int*

Por ejemplo, si desea ordenar una matriz, ambos de los siguientes podría funcionar igual de bien:

std::sort(x + 0, x + n);
std::sort(&x[0], &x[0] + n);

Tenga en cuenta que es ilegal proporcionar &x[n] , como segundo argumento, ya que esto es equivalente a &*(x+n), y la sub-expresión *(x+n) técnicamente invoca un comportamiento indefinido en C++ (pero no en C99).

También tenga en cuenta que usted simplemente podría proveer x como primer argumento. Que es un poco demasiado breve para mi gusto, y también hace que el argumento de plantilla de la deducción de un poco más difícil para el compilador, porque en ese caso el primer argumento es un array, pero el segundo argumento es un puntero. (De nuevo, la matriz-a-puntero de caries activa.)

127voto

FredOverflow Puntos 88201

Los programadores a menudo se confunden las matrices multidimensionales con arrays de punteros.

Matrices multidimensionales

La mayoría de los programadores están familiarizados con el nombre de matrices multidimensionales, pero muchos no son conscientes del hecho de que la matriz multidimensional también puede ser creado de forma anónima. Matrices multidimensionales se refiere a menudo como "matrices de matrices" o "verdadero matrices multidimensionales".

Nombre de matrices multidimensionales

Cuando se utiliza denomina matrices multidimensionales, todas las dimensiones debe ser conocido en tiempo de compilación:

int H = read_int();
int W = read_int();

int connect_four[6][7];   // okay

int connect_four[H][7];   // ISO C++ forbids variable length array
int connect_four[6][W];   // ISO C++ forbids variable length array
int connect_four[H][W];   // ISO C++ forbids variable length array

Así es como el nombre de una matriz multidimensional se ve como en la memoria:

              +---+---+---+---+---+---+---+
connect_four: |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+

Tenga en cuenta que en 2D cuadrículas como el anterior son meramente útil visualizaciones. Desde el punto de vista de C++, la memoria es un "plano" de la secuencia de bytes. Los elementos de una matriz multidimensional se almacenan en la fila principal de la orden. Es decir, connect_four[0][6] y connect_four[1][0] son vecinos en la memoria. De hecho, connect_four[0][7] y connect_four[1][0] denotar el mismo elemento! Esto significa que usted puede tomar múltiples dimensiones de las matrices y los tratan como grandes dimensiones de las matrices:

int* p = &connect_four[0][0];
int* q = p + 42;
some_int_sequence_algorithm(p, q);

Anónimo matrices multidimensionales

Anónimo de las matrices multidimensionales, todas las dimensiones excepto en la primera, debe ser conocido en tiempo de compilación:

int (*p)[7] = new int[6][7];   // okay
int (*p)[7] = new int[H][7];   // okay

int (*p)[W] = new int[6][W];   // ISO C++ forbids variable length array
int (*p)[W] = new int[H][W];   // ISO C++ forbids variable length array

Esto es como un anónimo matriz multidimensional se ve como en la memoria:

              +---+---+---+---+---+---+---+
        +---> |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |
      +-|-+
   p: | | |
      +---+

Tenga en cuenta que la propia matriz está siendo asignado como un solo bloque en la memoria.

Arrays de punteros

Usted puede superar la restricción de ancho fijo, mediante la introducción de otro nivel de direccionamiento indirecto.

Nombre matrices de punteros

Aquí es el nombre de una matriz de cinco punteros que se inicializan con el anónimo de las matrices de diferentes longitudes:

int* triangle[5];
for (int i = 0; i < 5; ++i)
{
    triangle[i] = new int[5 - i];
}

// ...

for (int i = 0; i < 5; ++i)
{
    delete[] triangle[i];
}

Y aquí es como parece en la memoria:

          +---+---+---+---+---+
          |   |   |   |   |   |
          +---+---+---+---+---+
            ^
            | +---+---+---+---+
            | |   |   |   |   |
            | +---+---+---+---+
            |   ^
            |   | +---+---+---+
            |   | |   |   |   |
            |   | +---+---+---+
            |   |   ^
            |   |   | +---+---+
            |   |   | |   |   |
            |   |   | +---+---+
            |   |   |   ^
            |   |   |   | +---+
            |   |   |   | |   |
            |   |   |   | +---+
            |   |   |   |   ^
            |   |   |   |   |
            |   |   |   |   |
          +-|-+-|-+-|-+-|-+-|-+
triangle: | | | | | | | | | | |
          +---+---+---+---+---+

Ya que cada línea se asigna individualmente ahora, visualización 2D matrices como 1D matrices no funciona.

Anónimo matrices de punteros

Aquí es un anónimo matriz de 5 (o cualquier otro número) de los punteros que se inicializan con el anónimo de las matrices de diferentes longitudes:

int n = calculate_five();   // or any other number
int** p = new int*[n];
for (int i = 0; i < n; ++i)
{
    p[i] = new int[n - i];
}

// ...

for (int i = 0; i < n; ++i)
{
    delete[] p[i];
}
delete[] p;   // note the extra delete[] !

Y aquí es como parece en la memoria:

          +---+---+---+---+---+
          |   |   |   |   |   |
          +---+---+---+---+---+
            ^
            | +---+---+---+---+
            | |   |   |   |   |
            | +---+---+---+---+
            |   ^
            |   | +---+---+---+
            |   | |   |   |   |
            |   | +---+---+---+
            |   |   ^
            |   |   | +---+---+
            |   |   | |   |   |
            |   |   | +---+---+
            |   |   |   ^
            |   |   |   | +---+
            |   |   |   | |   |
            |   |   |   | +---+
            |   |   |   |   ^
            |   |   |   |   |
            |   |   |   |   |
          +-|-+-|-+-|-+-|-+-|-+
          | | | | | | | | | | |
          +---+---+---+---+---+
            ^
            |
            |
          +-|-+
       p: | | |
          +---+

Conversiones

Matriz-a-puntero de la caries naturalmente se extiende a las matrices de matrices y arrays de punteros:

int array_of_arrays[6][7];
int (*pointer_to_array)[7] = array_of_arrays;

int* array_of_pointers[6];
int** pointer_to_pointer = array_of_pointers;

Sin embargo, no existe una conversión implícita de T[h][w] a T**. Si tal conversión implícita no existía, el resultado sería un puntero al primer elemento de una matriz de h punteros T (cada una apuntando al primer elemento de una línea en el original de la matriz 2D), sino que el puntero de la matriz no existe en ninguna parte de la memoria todavía. Si quieres una conversión de este tipo, debe crear y rellenar los necesarios puntero de la matriz de forma manual:

int connect_four[6][7];

int** p = new int*[6];
for (int i = 0; i < 6; ++i)
{
    p[i] = connect_four[i];
}

// ...

delete[] p;

Tenga en cuenta que esto genera una visión original de la matriz multidimensional. Si usted necesita una copia en su lugar, debe crear un exceso de matrices y copia los datos por usted mismo:

int connect_four[6][7];

int** p = new int*[6];
for (int i = 0; i < 6; ++i)
{
    p[i] = new int[7];
    std::copy(connect_four[i], connect_four[i + 1], p[i]);
}

// ...

for (int i = 0; i < 6; ++i)
{
    delete[] p[i];
}
delete[] p;

82voto

FredOverflow Puntos 88201

Asignación de

Por ninguna razón en particular, las matrices no puede ser asignado a uno de otro. Uso std::copy lugar:

#include <algorithm>

// ...

int a[8] = {2, 3, 5, 7, 11, 13, 17, 19};
int b[8];
std::copy(a + 0, a + 8, b);

Esto es más flexible de lo que es la verdadera matriz de asignación podría ofrecer, porque es posible copiar las rodajas de matrices más grandes en pequeñas matrices. std::copy es generalmente especializadas por tipos primitivos para dar el máximo rendimiento. Es poco probable que std::memcpy realiza mejor. En caso de duda, la medida.

Aunque no se puede asignar matrices directamente, usted puede asignar estructuras y clases que contienen los miembros de la matriz. Eso es porque los miembros de la matriz se copian memberwise por el operador de asignación que se proporciona por defecto, el compilador. Si se define el operador de asignación de forma manual para su propia estructura o los tipos de clase, usted debe retroceder a la copia manual para los miembros de la matriz.

Paso de parámetros

Matrices no pueden ser pasados por valor. Puedes pasar por ellos puntero o referencia.

Pasar por puntero

Puesto que las matrices sí mismas no pueden ser pasados por valor, normalmente un puntero a su primer elemento es pasado por valor. Esto a menudo se llama "pasar el puntero". Ya que el tamaño de la matriz no es recuperable a través de ese puntero, tiene que pasar un segundo parámetro que indica el tamaño de la matriz (el clásico C solución) o un segundo puntero que apunta después de que el último elemento de la matriz (el C++ iterador de la solución):

#include <numeric>
#include <cstddef>

int sum(const int* p, std::size_t n)
{
    return std::accumulate(p, p + n, 0);
}

int sum(const int* p, const int* q)
{
    return std::accumulate(p, q, 0);
}

Como sintáctico alternativa, también puede declarar parámetros como T p[], y significa exactamente lo mismo como T* p en el contexto de las listas de parámetros sólo:

int sum(const int p[], std::size_t n)
{
    return std::accumulate(p, p + n, 0);
}

Usted puede pensar que el compilador como reescribir T p[] a T *p en el contexto de las listas de parámetros sólo. Esta regla especial es en parte responsable de toda la confusión acerca de las matrices y punteros. En cualquier otro contexto, declarando que algo como una matriz o un puntero que hace una enorme diferencia.

Por desgracia, también puede proporcionar un tamaño en un parámetro de matriz que es ignorado por el compilador. Es decir, los siguientes tres firmas son exactamente equivalentes, según lo indicado por los errores del compilador:

int sum(const int* p, std::size_t n)

// error: redefinition of 'int sum(const int*, size_t)'
int sum(const int p[], std::size_t n)

// error: redefinition of 'int sum(const int*, size_t)'
int sum(const int p[8], std::size_t n)   // the 8 has no meaning here

Paso por referencia

Las matrices también pueden ser pasados por referencia:

int sum(const int (&a)[8])
{
    return std::accumulate(a + 0, a + 8, 0);
}

En este caso, el tamaño de la matriz es significativo. Desde escribir una función que sólo acepta las matrices de exactamente 8 elementos es de poco uso, los programadores suelen escribir funciones tales como plantillas:

template <std::size_t n>
int sum(const int (&a)[n])
{
    return std::accumulate(a + 0, a + n, 0);
}

Tenga en cuenta que sólo puede llamar a una función de este tipo de plantilla con una matriz de enteros, no con un puntero a un entero. El tamaño de la matriz automáticamente se infiere, y para cada tamaño n, una función diferente se crea una instancia de la plantilla. También puede escribir muy útil la función de plantillas que resumen tanto el tipo de elemento y del tamaño.

68voto

FredOverflow Puntos 88201

La creación de la matriz y de inicialización

Como con cualquier otro tipo de objetos de C++, las matrices pueden ser almacenados directamente en variables (el tamaño debe ser una constante en tiempo de compilación; C++ no admite VLAs), o pueden ser almacenados de forma anónima en el montón y al que se accede indirectamente a través de punteros (sólo entonces puede el tamaño se calcula en tiempo de ejecución).

Automática de matrices

Automática de matrices (matrices de vivir "en la pila") se crean cada vez que el flujo de control pasa a través de la definición de un no-local estática de la matriz de variable:

void foo()
{
    int automatic_array[8];
}

La inicialización se realiza en orden ascendente. Tenga en cuenta que los valores iniciales dependen del tipo de elemento T:

  • Si T es un POD (como int en el ejemplo anterior), no la inicialización tiene lugar.
  • De lo contrario, el default constructor de T inicializa todos los elementos.
  • Si T no proporciona accesible por defecto el constructor, el programa no compila.

Alternativamente, los valores iniciales puede ser especificado de forma explícita en el inicializador de matriz, una lista separada por comas rodeado por llaves:

    int primes[8] = {2, 3, 5, 7, 11, 13, 17, 19};

Ya que en este caso el número de elementos en el inicializador de matriz es igual al tamaño de la matriz, especificando el tamaño manualmente es redundante. Automáticamente se puede ser deducida por el compilador:

    int primes[] = {2, 3, 5, 7, 11, 13, 17, 19};   // size 8 is deduced

También es posible especificar el tamaño y la prestación de un menor de inicializador de matriz:

    int fibonacci[50] = {0, 1, 1};   // 47 trailing zeros are deduced

En ese caso, el resto de los elementos son inicializados a cero. Tenga en cuenta que C++ permite una matriz vacía inicializador (todos los elementos son inicializados a cero), mientras que C89 no (al menos un valor es requerido). También tenga en cuenta que los inicializadores de matriz sólo puede ser utilizado para inicializar matrices; no pueden ser posteriormente utilizados en las asignaciones.

Las matrices estáticas

Estática de matrices (matrices de vivir "en el segmento de datos") son locales de la matriz de variables definidas con el static de palabras clave y variables de matriz en el ámbito de espacio de nombres ("variables globales"):

int global_static_array[8];

void foo()
{
    static int local_static_array[8];
}

(Tenga en cuenta que las variables en el ámbito de espacio de nombres son implícitamente estáticas. Agregar el static de palabras clave para su definición tiene una completamente diferente, está obsoleto significado.)

Aquí es cómo matrices estáticas se comportan de manera diferente automática de las matrices:

  • Las matrices estáticas sin un inicializador de matriz son inicializados a cero antes de proseguir con el potencial de inicialización.
  • Estática POD matrices se inicializan exactamente una vez, y los valores iniciales son típicamente se cocía en el ejecutable, en cuyo caso no hay inicialización del costo en tiempo de ejecución. Este no es siempre el más eficiente con el espacio de la solución, sin embargo, y no es requerido por el estándar.
  • Estática no POD matrices se inicializa la primera vez que el flujo de control pasa a través de su definición. En el caso de locales de matrices estáticas, que nunca puede suceder si la función no se llama nunca.

(Ninguna de las anteriores es específico a las matrices. Estas normas se aplican de igual forma a los otros tipos de objetos estáticos.)

Matriz de datos de los miembros de la

Matriz de datos de los miembros son creados cuando su propietario se crea un objeto. Desafortunadamente, C++03 proporciona ningún medio para inicializar matrices en el miembro de la lista de inicializador, por lo que la inicialización debe ser falsificado con asignaciones:

class Foo
{
    int primes[8];

public:

    Foo()
    {
        primes[0] = 2;
        primes[1] = 3;
        primes[2] = 5;
        // ...
    }
};

Como alternativa, puede definir un sistema automático de matriz en el cuerpo del constructor de copia y los elementos más:

class Foo
{
    int primes[8];

public:

    Foo()
    {
        int local_array[] = {2, 3, 5, 7, 11, 13, 17, 19};
        std::copy(local_array + 0, local_array + 8, primes + 0);
    }
};

En C++0x, las matrices pueden ser inicializados en el miembro de la lista de inicializador gracias uniforme de inicialización:

class Foo
{
    int primes[8];

public:

    Foo() : primes { 2, 3, 5, 7, 11, 13, 17, 19 }
    {
    }
};

Esta es la única solución que funciona con los tipos de elementos que no tienen ningún constructor por defecto.

Matrices dinámicas

Dinámica de las matrices no tienen nombres, por lo tanto el único medio de acceder a ellos es a través de punteros. Porque no tienen nombres, me voy a referir a ellos como "anónimo matrices" a partir de ahora.

En C, anónimo matrices se crean a través de malloc y amigos. En C++, anónimo matrices se crean con la new T[size] de sintaxis que devuelve un puntero al primer elemento de un anónimo de la matriz:

std::size_t size = compute_size_at_runtime();
int* p = new int[size];

Los siguientes arte ASCII representa el diseño de memoria si el tamaño se calcula como 8 en tiempo de ejecución:

             +---+---+---+---+---+---+---+---+
(anonymous)  |   |   |   |   |   |   |   |   |
             +---+---+---+---+---+---+---+---+
               ^
               |
               |
             +-|-+
          p: | | |                               int*
             +---+

Obviamente, anónimo matrices requieren más memoria de la que denomina matrices debido a la mayor puntero que deben ser almacenados por separado. (También hay algunos sobrecarga adicional en la tienda.)

Tenga en cuenta que no hay ninguna matriz-a-puntero de la decadencia que está ocurriendo aquí. Aunque la evaluación de new int[size] no en el hecho de crear una matriz de enteros, el resultado de la expresión new int[size] es ya un puntero a un entero (el primer elemento), no un array de enteros o un puntero a un array de enteros de tamaño desconocido. Eso sería imposible, porque el tipo estático del sistema requiere de la matriz de tamaños para ser constantes en tiempo de compilación. (Por lo tanto, yo no anotar el anónimo de la matriz con la información del tipo estático en la imagen).

Relativa a los valores por defecto para los elementos, anónimo matrices se comportan similar a la automática de matrices. Normalmente, anónimo POD matrices no se inicializan, pero hay una sintaxis especial que provoca que el valor de inicialización:

int* p = new int[some_computed_size]();

(Nota al final el par de paréntesis a la derecha antes del punto y coma.) De nuevo, C++0x simplifica las reglas y permite especificar valores iniciales para el anónimo de las matrices gracias a los uniformes de la inicialización:

int* p = new int[8] { 2, 3, 5, 7, 11, 13, 17, 19 };

Si usted se realiza mediante un anónimo de la matriz, usted tiene que liberar de nuevo el sistema:

delete[] p;

Hay que liberar cada anónima matriz exactamente una vez y luego nunca lo toque de nuevo más tarde. No soltarla en todos los resultados en una pérdida de memoria (o, más en general, según el tipo de elemento, una pérdida de recursos), y tratando de liberar varias veces los resultados de un comportamiento indefinido. El uso de la no-formulario de matriz delete (o free) en lugar de delete[] para la liberación de la matriz es también un comportamiento indefinido.

64voto

5. Errores comunes al utilizar matrices.

5.1 Escollo: Confiar tipo inseguro de vinculación.

OK, que te han dicho, o ha encontrado a sí mismo, que globals (espacio de nombres alcance de las variables que puede obtenerse fuera de la unidad de traducción) Evil™. Pero, ¿sabemos verdaderamente qué Mal™ son? Considere la posibilidad de la siguiente programa, que consta de dos archivos [main.cpp] y [numbers.cpp]:

// [main.cpp]
#include <iostream>

extern int* numbers;

int main()
{
    using namespace std;
    for( int i = 0;  i < 42;  ++i )
    {
        cout << (i > 0? ", " : "") << numbers[i];
    }
    cout << endl;
}

// [numbers.cpp]
int numbers[42] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

En Windows 7 esto compila y enlaces bien tanto con MinGW g++ 4.4.1 y Visual C++ 10.0.

Dado que los tipos no coinciden, el programa se bloquea cuando se ejecuta.

The Windows 7 crash dialog

En-el-explicación formal: el programa tiene un Comportamiento Indefinido (UB), y en lugar de de estrellarse por lo tanto, puede simplemente pasar el rato, o tal vez no hacer nada, o puede enviar amenazando e-mails a los presidentes de los estados UNIDOS, Rusia, la India, China y Suiza, y hacer Nasal Demonios de la mosca de la nariz.

En la práctica explicación: en main.cpp la matriz es tratado como un puntero, colocado en la misma dirección de la matriz. Para ejecutable de 32 bits, esto significa que la primera int valor de la matriz, es tratada como un puntero. Es decir, en main.cppla numbers variable contiene, o parece contener, (int*)1. Esto hace que el programa de acceso de la memoria en la parte inferior del espacio de direcciones, que es convencionalmente reservados trampa y que causan. Resultado: obtendrá un bloqueo.

Los compiladores están totalmente en su derecho de no diagnosticar este error, debido a que C++11 §3.5/10 dice, sobre el requisito de tipos compatibles para las declaraciones,

[N3290 §3.5/10]
Una violación de esta regla en el tipo de identidad que no requiere de un diagnóstico.

El mismo párrafo detalles de la variación que se permite:

... declaraciones de un objeto array, puede especificar los tipos de matrices que se diferencian por la presencia o ausencia de una serie importante obligado (8.3.4).

Esto permitió que la variación no incluyen declarar un nombre de un array en una la unidad de traducción, y como un puntero en otra unidad de traducción.

5.2 Trampa: Hacer prematuro de optimización (memset & friends).

Aún no se ha escrito

5.3 Escollo: Utilizando el lenguaje C para obtener el número de elementos.

Con profunda de la C a la experiencia es natural escribir ...

#define COUNT_OF( array )   (sizeof( array )/sizeof( array[0] ))

Desde un array de desintegración puntero al primer elemento cuando sea necesario, la la expresión sizeof(a)/sizeof(a[0]) también puede ser escrito como sizeof(a)/sizeof(*a). Significa lo mismo, y no importa cómo se escrito es el lenguaje C para encontrar el número de elementos de la matriz.

Principal escollo: el lenguaje C no es typesafe. Por ejemplo, el código de ...

#include <stdio.h>

#define COUNT_OF( array ) (sizeof( array )/sizeof( *array ))

void display( int const a[7] )
{
    int const   n = COUNT_OF( a );          // Oops.
    printf( "%d elements.\n", n );
}

int main()
{
    int const   moohaha[]   = {1, 2, 3, 4, 5, 6, 7};

    printf( "%d elements, calling display...\n", COUNT_OF( moohaha ) );
    display( moohaha );
}

pasa un puntero a COUNT_OF, y por lo tanto más probable es que produce un mal resultado. Compilado como un ejecutable de 32 bits en Windows 7 se produce ...

7 elementos, visualización de llamada...
1 elementos.

  1. El compilador reescribe int const a[7] a solo int const a[].
  2. El compilador reescribe int const a[] a int const* a.
  3. COUNT_OF es, por tanto, se invoca con un puntero.
  4. Para un ejecutable de 32 bits sizeof(array) (el tamaño de un puntero) es de 4.
  5. sizeof(*array) es equivalente a sizeof(int), que para un ejecutable de 32 bits también es 4.

Con el fin de detectar este error en tiempo de ejecución se puede hacer ...

#include <assert.h>
#include <typeinfo>

#define COUNT_OF( array )       (                               \
    assert((                                                    \
        "COUNT_OF requires an actual array as argument",        \
        typeid( array ) != typeid( &*array )                    \
        )),                                                     \
    sizeof( array )/sizeof( *array )                            \
    )

7 elementos, visualización de llamada...
Error de aserción: ( "COUNT_OF requiere de un real de la matriz como argumento", typeid( a ) != typeid (&*)), archivo runtime_detect ion.cpp, línea 16

Esta aplicación ha solicitado que el tiempo de ejecución para terminar de una manera inusual.
Por favor, póngase en contacto con el equipo de soporte técnico para obtener más información.

El tiempo de ejecución de detección de errores es mejor que la falta de detección, pero pierde un poco de el tiempo de procesador, y quizás mucho más programador de tiempo. Mejor con la detección de tiempo de compilación! Y si usted es feliz no se admiten matrices de tipos locales con C++98, a continuación, puede hacerlo:

#include <stddef.h>

typedef ptrdiff_t   Size;

template< class Type, Size n >
Size countOf( Type (&)[n] ) { return n; }

#define COUNT_OF( array )       countOf( array )

La compilación de esta definición sustituido en el primer programa completo, con g++, Tengo ...

M:\count> g++ compile_time_detection.cpp
compile_time_detection.cpp: En la función 'void mostrar(int const*)':
compile_time_detection.cpp:14: error: no hay coincidencia de la función de llamada a 'countOf(const int*y)'

M:\count> _

Cómo funciona: la matriz se pasa por referencia a countOf, y así es no se deteriorará puntero al primer elemento, y la función puede devolver simplemente la el número de los elementos especificados por el tipo.

Con C++11 se puede utilizar también para las matrices de tipo local, y es el tipo de seguro C++ lenguaje para hallar el número de elementos de una matriz.

Hay un extra de refinamiento para el caso donde se desea un tiempo de compilación constante, pero ya que es así que se utilizan rara vez (si!) mostrando que sería un desperdicio de espacio.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X