Punteros y matrices

En C++ existe una relación entre punteros y matrices de manera que cualquier operación que se pueda realizar mediante la indexación de una matriz se puede hacer también con punteros.

Analicemos el siguiente programa para una mejor comprensión:

Al ejecutar el programa obtenemos:

En este ejemplo se ha utilizado la indexación, expresión lista[ind], para acceder a los elementos de la matriz lista.

Cuando C++ interpreta esa expresión sabe que a partir de la dirección de comienzo de la matriz, esto es, a partir de lista, tiene que avanzar ind elementos para acceder al contenido del elemento especificado por ese índice.

Dicho de otra forma, con la expresión lista[ind] se accede al contenido de la dirección lista + ind.

Veamos otra versión con punteros:

Esta versión es idéntica a la anterior, excepto que la expresión para acceder a los elementos de la matriz es ahora *(plista + ind).

La asignación plista = [0] hace que plista apunte al primer elemento, que coincide con la dirección de comienzo de la matriz; esto es, con lista. Por lo tanto en lugar de la expresión *(plista + ind), podríamos utilizar también la expresión *(lista + ind). Según lo expuesto la siguientes expresiones darán los mismos resultados.

Obtendríamos el mismo resultado con esta otra versión del progtrama:

La aqsignación plista = lista hace que plista apunte al comienzo de la matriz lista, es decir, al elemento primero de la matriz,…

…y la expresión *plista++ da lugar a las dos operaciones siguientes según el orden descrito: *plista que es el valor apuntado por plista, y a plista++, que hace que plista pase a apuntar al siguiente elemento de la matriz.

Podríamos escribir el bucle for tamCaptura de pantalla de 2023-08-21 12-33-54bién así:

Sin embargo, hay una diferencia entre el identificador de una matriz y un puntero. El identificador de una matriz es una constante y un puntero es una variable. Esto significa que el siguiente bucle daría lugar a un error, porque lista es constante y no puede cambiar de valor.

En cambio, un parámetro de una función que sea una matriz se considera una variable (un puntero):

Otro detalle a tener en cuenta es el resultado que devuelve el operador sizeof aplicado a una matriz o a un puntero que apunte a una matriz:

Captura de pantalla de 2023-08-21 11-49-58

El operador sizeof() devuelve el tamaño en bytes de su operador. Por lo tanto aplicado a la matriz lista devuelve el tamaño en bytes de la matriz, en el ejemplo 20, y aplicado al puntero plista que apunta a una matriz devuelve el tamaño en bytes del puntero, en el ejemplo 8 (según sea el PC).

Aplicando lo expuesto vamos a realizar una función llamada CopiarMatriz que permita copiar una matriz de cualquier tipo de datos y de cualquier número de dimensiones en otra matriz (será una función análoga a la función memcpy de la biblioteca de C). Dicha función tendrá el siguiente prototipo:

El parámetro dest es un puntero genérico que almacenará la dirección de la matriz destino de los datos…

… y orig es un puntero también genérico que almacenará la dirección de la matriz origen.

El tamaño en bytes de ambas matrices es proporcionado a través del parámetro nbytes.

Según lo estudiado anteriormente en el apartaqdo Punteros genéricos, la utilización de éstos como parámetros permitirá pasar argumentos que sean matrices de cualquier tipo. Así mismo, según lo estudiado en el mismo apartado, el nombre de una matriz es siempre su dirección de comienzo, independientemente del tamaño y el número de dimensiones que tenga.

Con lo expuesto anteriormente el programa quedaría así:

Esta función copia nbytes desde orig a dest. Piense que una matriz, sin pensar en el tipo de datos que contiene, no es más que un bloque de bytes consecutivos en la memoria. Pensando así, orig es la dirección de comienzo del bloque de bytes a copiar y dest es la dirección del bloque donde se quieren copiar.

Veamos un ejemplo de programa que utiliza la función CopiarMatriz para copiar una matriz m1 de dos dimensiones de tipo int en otra matriz llamada m2 de iguales características.

Ejecutando el programa obtenemos:

Punteros a cadena de caracteres

Puesto que una cadena de caracteres es una matriz de caracteres, podemos pensar que la teoría expuesta anteriormente es perfectamente aplicable a cadenas de caracteres. Un puntero a una cadena de caracteres puede definirse de alguna de las dos for4mas siguientes:

La dirección de memoria donde comienza una cadena viene dada por el nombre de la matriz que la contiene y el final, por el caracter \0 con el que C++ finaliza todas las cadenas. El siguiente ejemplo define e inicia la cadena de caracteres plato.

El ejemplo anterior plato es un puntero a una cadena de caracteres. El compilador C++ asignaq la dirección de comienzo del literal «Arroz con pollo» al puntero plato y finaliza la cadena con el caracter \0. Por lo tanto, el operador << sabe que la cadena de caracteres que tiene que visualizar empieza en la dirección plato y que a partir de aquí, tiene que ir ascendiendo a posiciones sucesivas de memoria hasta encontrar el caracter \0.

Es importante tomar nota de que plato no contiene una copia de la cadena asignada, sino la dirección de memoria del lugar donde la cadena está almacenada, que coincide con la dirección del primer carácter; los demás caracteres estaran almacenados consecutivamente. Según lo dicho, en el siguiente ejemplo plato apunta inicialmente a la cadena de caracteres «Arroz con pollo» y, a continuación, reasigna el puntero para que apunte a una nueva cadena. La cadena anterior se pierde porque el contenido de la variable plato se ha sobreescrito con una nueva dirección, la de la cadena «Judias«.

Al ejecutar el programa tendriamos:

Un literal, por tratarse de una constante de caracteres, no se puede modificar. Por ejemplo, si intenta ejecutar el código siguiente obtendrá un error:

Por lógica el error comentado anteriormente no ocurre cuando la cadena de caracteres viene dada por una matriz que no haya sido declarada constante (const), según vemos en el ejemplo siguiente:

El siguiente ejemplo presenta una función que copia una cadena en otra. Para ello, utiliza una función copicad que contiene dos parámetros: la matriz destino y la matriz de origen que contiene la cadena a copiar (recuerde que la biblioteca de C nos proporciona la función ctrcpy para realizar esta misma operación). Según esto la llamada a la función podría quedar así:

Evidentemente la función copicad tiene que recibir como parámetros las direcciones de las matrices destino y fuente. Por lo tanto, la solución al problema planteado puede ser así:

Al ejecutar el programa obtendríamos: