Compartir a través de


Matrices (C++)

Una matriz es una secuencia de objetos del mismo tipo que ocupan un área contigua de memoria. Las matrices tradicionales de estilo C son el origen de muchos errores, pero siguen siendo comunes, especialmente en las bases de código anteriores. En C++moderno, se recomienda encarecidamente usar std::vector o std::array en lugar de matrices de estilo C descritas en esta sección. Ambos de estos tipos de biblioteca estándar almacenan sus elementos como un bloque contiguo de memoria. Sin embargo, proporcionan mayor seguridad de tipos y admiten iteradores que se garantizan para que apunten a una ubicación válida dentro de la secuencia. Para obtener más información, consulte Containers.

Declaraciones de pila

En una declaración de matriz de C++, el tamaño de la matriz se especifica después del nombre de la variable, no después del nombre de tipo como en otros lenguajes. El siguiente ejemplo declara una matriz de 1000 dobles que se asignarán en la pila. El número de elementos debe proporcionarse como un literal entero o como una expresión constante. Esto se debe a que el compilador tiene que saber cuánto espacio de pila asignar; no puede usar un valor calculado en tiempo de ejecución. A cada elemento de la matriz se le asigna un valor predeterminado de 0. Si no se asigna un valor predeterminado, cada elemento contiene inicialmente cualquier valor aleatorio que se encuentre en esa ubicación de memoria.

    constexpr size_t size = 1000;

    // Declare an array of doubles to be allocated on the stack
    double numbers[size] {0};

    // Assign a new value to the first element
    numbers[0] = 1;

    // Assign a value to each subsequent element
    // (numbers[1] is the second element in the array.)
    for (size_t i = 1; i < size; i++)
    {
        numbers[i] = numbers[i-1] * 1.1;
    }

    // Access each element
    for (size_t i = 0; i < size; i++)
    {
        std::cout << numbers[i] << " ";
    }

El primer elemento de la matriz es el elemento zeroth. El último elemento es el elemento (n-1), donde n es el número de elementos que la matriz puede contener. El número de elementos de la declaración debe ser de un tipo entero y debe ser mayor que 0. Es su responsabilidad asegurarse de que el programa nunca pasa un valor al operador de subíndice mayor que (size - 1).

Una matriz de tamaño cero es válida únicamente cuando la matriz es el último campo en un struct o union y cuando las extensiones de Microsoft están habilitadas (/Za o /permissive- no está establecido).

Las matrices basadas en pila son más rápidas para asignar y acceder a las matrices basadas en montón. Sin embargo, el espacio de pila es limitado. El número de elementos de matriz no puede ser tan grande que use demasiada memoria de pila. Cuánto es demasiado depende de su programa. Puede usar herramientas de generación de perfiles para determinar si una matriz es demasiado grande.

Declaraciones abiertas

Puede requerir una matriz demasiado grande para asignar en la pila o cuyo tamaño no se conoce en tiempo de compilación. Es posible asignar esta matriz en el montón mediante una expresión new[]. El operador devuelve un puntero al primer elemento. El operador de subíndice funciona en la variable de puntero de la misma manera que en una matriz basada en pila. También puede usar la aritmética de puntero para mover el puntero a cualquier elemento arbitrario de la matriz. Es su responsabilidad asegurarse de ello:

  • siempre mantiene una copia de la dirección del puntero original para que pueda eliminar la memoria cuando ya no necesite la matriz.
  • no incrementa o disminuye la dirección del puntero más allá de los límites de la matriz.

En el ejemplo siguiente se muestra cómo definir una matriz en el montón en tiempo de ejecución. Muestra cómo obtener acceso a los elementos de matriz mediante el operador de subíndice y mediante la aritmética de punteros:

void do_something(size_t size)
{
    // Declare an array of doubles to be allocated on the heap
    double* numbers = new double[size]{ 0 };

    // Assign a new value to the first element
    numbers[0] = 1;

    // Assign a value to each subsequent element
    // (numbers[1] is the second element in the array.)
    for (size_t i = 1; i < size; i++)
    {
        numbers[i] = numbers[i - 1] * 1.1;
    }

    // Access each element with subscript operator
    for (size_t i = 0; i < size; i++)
    {
        std::cout << numbers[i] << " ";
    }

    // Access each element with pointer arithmetic
    // Use a copy of the pointer for iterating
    double* p = numbers;

    for (size_t i = 0; i < size; i++)
    {
        // Dereference the pointer, then increment it
        std::cout << *p++ << " ";
    }

    // Alternate method:
    // Reset p to numbers[0]:
    p = numbers;

    // Use address of pointer to compute bounds.
    // The compiler computes size as the number
    // of elements * (bytes per element).
    while (p < (numbers + size))
    {
        // Dereference the pointer, then increment it
        std::cout << *p++ << " ";
    }

    delete[] numbers; // don't forget to do this!

}
int main()
{
    do_something(108);
}

Inicializar matrices

Se puede inicializar una matriz en un bucle, un elemento a la vez o en una sola instrucción. El contenido de las dos matrices siguientes es idéntico:

    int a[10];
    for (int i = 0; i < 10; ++i)
    {
        a[i] = i + 1;
    }

    int b[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Pasar matrices a funciones

Cuando se pasa una matriz a una función, se pasa como puntero al primer elemento, ya sea una matriz basada en pila o en montón. El puntero no contiene ninguna otra información de tamaño o tipo. Este comportamiento se denomina degradación del puntero. Al pasar una matriz a una función, siempre se debe especificar el número de elementos en un parámetro independiente. Este comportamiento también implica que los elementos de matriz no se copian cuando la matriz se pasa a una función. Para evitar que la función modifique los elementos, especifique el parámetro como puntero a los elementos const.

En el ejemplo siguiente se muestra una función que acepta una matriz y una longitud. El puntero apunta a la matriz original y no a una copia. Dado que el parámetro no es const, la función puede modificar los elementos de matriz.

void process(double *p, const size_t len)
{
    std::cout << "process:\n";
    for (size_t i = 0; i < len; ++i)
    {
        // do something with p[i]
    }
}

Declare y defina el parámetro de matriz p como const para que sea de solo lectura dentro del bloque de función:

void process(const double *p, const size_t len);

La misma función también se puede declarar de estas maneras, sin cambios en el comportamiento. La matriz se sigue pasando como puntero al primer elemento:

// Unsized array
void process(const double p[], const size_t len);

// Fixed-size array. Length must still be specified explicitly.
void process(const double p[1000], const size_t len);

Matrices multidimensionales

Las matrices construidas a partir de otras matrices son matrices multidimensionales. Estas matrices multidimensionales se especifican colocando en orden varias expresiones constantes entre corchetes. Por ejemplo, considere esta declaración:

int i2[5][7];

Especifica una matriz de tipo int, organizada conceptualmente en una matriz bidimensional de cinco filas y siete columnas, como se muestra en la ilustración siguiente:

Conceptual layout of a multidimensional array.

La imagen es una cuadrícula de 7 celdas de ancho y 5 celdas de alto. Cada celda contiene el índice de la celda. El primer índice de celda está etiquetado como 0,0. La siguiente celda de esa fila es 0,1, etc. en la última celda de esa fila, que es 0,6. La siguiente fila comienza con el índice 1,0. La celda después de eso tiene un índice de 1,1. La última celda de esa fila es 1,6. Este patrón se repite hasta la última fila, que comienza con el índice 4.0. La última celda de la última fila tiene un índice de 4,6. :::image-end

Puede declarar matrices multidimensionales que tienen una lista de inicializadores (como se describe en Inicializadores). En estas declaraciones se puede omitir la expresión constante que especifica los límites de la primera dimensión. Por ejemplo:

// arrays2.cpp
// compile with: /c
const int cMarkets = 4;
// Declare a float that represents the transportation costs.
double TransportCosts[][cMarkets] = {
   { 32.19, 47.29, 31.99, 19.11 },
   { 11.29, 22.49, 33.47, 17.29 },
   { 41.97, 22.09,  9.76, 22.55 }
};

La declaración anterior define una matriz de tres filas por cuatro columnas. Las filas representan fábricas y las columnas representan los mercados a los que distribuyen las fábricas. Los valores son los costos de transporte de las fábricas a los mercados. La primera dimensión de la matriz se omite, pero el compilador la completa examinando el inicializador.

El uso del operador de direccionamiento indirecto (*) en un tipo de matriz de n dimensiones produce una matriz de n-1 dimensiones. Si n es 1, se produce un elemento escalar (o de matriz).

Las matrices de C++ se almacenan por orden de fila principal. El orden de fila principal significa que el último subíndice es el que varía más rápidamente.

Ejemplo

También se puede omitir la especificación de límites de la primera dimensión de una matriz multidimensional en declaraciones de función, como se muestra aquí:

// multidimensional_arrays.cpp
// compile with: /EHsc
// arguments: 3
#include <limits>   // Includes DBL_MAX
#include <iostream>

const int cMkts = 4, cFacts = 2;

// Declare a float that represents the transportation costs
double TransportCosts[][cMkts] = {
   { 32.19, 47.29, 31.99, 19.11 },
   { 11.29, 22.49, 33.47, 17.29 },
   { 41.97, 22.09,  9.76, 22.55 }
};

// Calculate size of unspecified dimension
const int cFactories = sizeof TransportCosts /
                  sizeof( double[cMkts] );

double FindMinToMkt( int Mkt, double myTransportCosts[][cMkts], int mycFacts);

using namespace std;

int main( int argc, char *argv[] ) {
   double MinCost;

   if (argv[1] == 0) {
      cout << "You must specify the number of markets." << endl;
      exit(0);
   }
   MinCost = FindMinToMkt( *argv[1] - '0', TransportCosts, cFacts);
   cout << "The minimum cost to Market " << argv[1] << " is: "
       << MinCost << "\n";
}

double FindMinToMkt(int Mkt, double myTransportCosts[][cMkts], int mycFacts) {
   double MinCost = DBL_MAX;

   for( size_t i = 0; i < cFacts; ++i )
      MinCost = (MinCost < TransportCosts[i][Mkt]) ?
         MinCost : TransportCosts[i][Mkt];

   return MinCost;
}
The minimum cost to Market 3 is: 17.29

La función FindMinToMkt se escribe de forma que para agregar nuevas fábricas no sea necesario modificar el código, solo volver a compilarlo.

Inicializar matrices

El constructor inicializa las matrices de objetos que tienen un constructor de clase. Cuando hay menos elementos en la lista de inicializadores que en la matriz, se usa el constructor predeterminado para los elementos restantes. Si no se define ningún constructor predeterminado para la clase, la lista de inicializadores debe estar completa, es decir, debe haber un inicializador para cada elemento de la matriz.

Considere la clase Point, que define dos constructores:

// initializing_arrays1.cpp
class Point
{
public:
   Point()   // Default constructor.
   {
   }
   Point( int, int )   // Construct from two ints
   {
   }
};

// An array of Point objects can be declared as follows:
Point aPoint[3] = {
   Point( 3, 3 )     // Use int, int constructor.
};

int main()
{
}

El primer elemento de aPoint se construye utilizando el constructor Point( int, int ); los dos elementos restantes se crean con el constructor predeterminado.

Las matrices de miembros estáticos (tanto const o no) se pueden inicializar en sus definiciones (fuera de la declaración de clase). Por ejemplo:

// initializing_arrays2.cpp
class WindowColors
{
public:
    static const char *rgszWindowPartList[7];
};

const char *WindowColors::rgszWindowPartList[7] = {
    "Active Title Bar", "Inactive Title Bar", "Title Bar Text",
    "Menu Bar", "Menu Bar Text", "Window Background", "Frame"   };
int main()
{
}

Acceso a elementos de matriz

Se puede obtener acceso a los elementos individuales de una matriz mediante el operador de subíndice de matriz ([ ]). Si usa el nombre de una matriz unidimensional sin un subíndice, se evalúa como un puntero al primer elemento de la matriz.

// using_arrays.cpp
int main() {
   char chArray[10];
   char *pch = chArray;   // Evaluates to a pointer to the first element.
   char   ch = chArray[0];   // Evaluates to the value of the first element.
   ch = chArray[3];   // Evaluates to the value of the fourth element.
}

Cuando se utilizan matrices multidimensionales, se pueden emplear varias combinaciones en las expresiones.

// using_arrays_2.cpp
// compile with: /EHsc /W1
#include <iostream>
using namespace std;
int main() {
   double multi[4][4][3];   // Declare the array.
   double (*p2multi)[3];
   double (*p1multi);

   cout << multi[3][2][2] << "\n";   // C4700 Use three subscripts.
   p2multi = multi[3];               // Make p2multi point to
                                     // fourth "plane" of multi.
   p1multi = multi[3][2];            // Make p1multi point to
                                     // fourth plane, third row
                                     // of multi.
}

En el código anterior, multi es una matriz tridimensional de tipo double. El puntero p2multi apunta a una matriz de tipo double de tamaño tres. En este ejemplo, la matriz se utiliza con uno, dos y tres subíndices. Aunque es más frecuente especificar todos los subíndices, como en la instrucción cout, a veces es útil seleccionar un subconjunto concreto de elementos de la matriz, como se muestra en las instrucciones que hay después de cout.

Sobrecarga del operador de subíndice

Al igual que otros operadores, el operador de subíndice ([]) lo puede volver a definir el usuario. El comportamiento predeterminado del operador de subíndice, si no está sobrecargado, consiste en combinar el nombre de la matriz y el subíndice usando el método siguiente:

*((array_name) + (subscript))

Como en toda suma que implica tipos de puntero, la escala se realiza automáticamente para que se produzca el ajuste correspondiente al tamaño del tipo. El valor resultante no es n bytes del origen de array_name; en su lugar, es el elemento nde la matriz. Para más información sobre esta conversión, consulte Operadores de suma.

De igual forma, para las matrices multidimensionales, la dirección se deriva usando el método siguiente:

((array_name) + (subscript1 * max2 * max3 * ... * maxn) + (subscript2 * max3 * ... * maxn) + ... + subscriptn))

Matrices en expresiones

Cuando un identificador de un tipo de matriz aparece en una expresión distinta de sizeof, dirección de (&) o inicialización de una referencia, se convierte en un puntero al primer elemento de matriz. Por ejemplo:

char szError1[] = "Error: Disk drive not ready.";
char *psz = szError1;

El puntero psz apunta al primer elemento de la matriz szError1. Las matrices, a diferencia de los punteros, no son valores L modificables. Por eso la siguiente asignación no es válida:

szError1 = psz;

Consulte también

std::array