Vectores, Matrices y Punteros en c++
with 62 comments
Estimados lectores y fans, les pido disculpas por este humilde y sencillo post, se que este
tutorial es una humillacin a su inteligencia y profesionalismo, pero comprendan que esto va
dedicado a personas que como yo recin empiezan con sus primeros pininos en C++. Espero
de verdad le sea til a alguien.
Escribir todo esto, me cost una noche entera y mucho caf. Parte de los conceptos aqu los
aprend en clases de la U y mucho tambin lo aprend por cuenta propia. As que
agradecimientos al teacher Patricio del cual aprend muchsimo. Una vez ms a ustedes y a los
tantos miles de seguidores que me leen a diario las disculpas del caso.
Vamos ir avanzando de a pocos as que no se preocupen y desesperen porfavor, paciencia. La
idea es que esta seccin crezca con su ayuda, feedback y motivacin. Luego vamos a ver
ejemplos con objetos, clases, sobrecarga de operadores, mtodos de bsqueda, ms
ordenacin, punteros, listas, pilas, colas, templates por ahora sigo practicando y haciendo
simples cosas con este lenguaje.
VECTORES
Un vector, tambin llamado array(arreglo) unidimensional, es una estructura de datos que
permite agrupar elementos del mismo tipo y almacenarlos en un solo bloque de memoria
juntos, uno despues de otro. A este grupo de elementos se les identifica por un mismo nombre
y la posicin en la que se encuentran. La primera posicin del array es la posicin 0.
Podramos agrupar en un array una serie de elementos de tipo enteros, flotantes, caracteres,
objetos, etc.
Crear un vector en C++ es sencillo, seguimos la siguiente sintaxix: Tipo nombre[tamanyo];
Ejm:
int a[5]; // Vector de 5 enteros
float b[5]; // vector de 5 flotantes
Producto product[5]; // vector de 5 objetos de tipo Producto
Podramos tambin inicializar el vector en la declaracin:
int a[] = {5, 15, 20, 25, 30};
float b[] = {10.5, 20.5, 30.5, 12.5, 50.5}
Producto product[] = {celular, calculadora, camara, ipod, usb}
Como hay 5 elementos en cada array, automticamente se le asignar 5 espacios de memoria a
cada vector, pero si trato de crear el vector de la forma int a[] , el compilador mostrar un error,
porque no indiqu el tamao del vector ni tampoco inicializ sus elementos.
Asigno valores a los elementos de un vector indicando su posicin:
int a[4] = 30; // le asigno el valor 30 a la posicin 4 del vector.
product[2].setPrecio(300) // le asigno un precio de 300 al producto en la
posicin 2.
Obviamente el mtodo setPrecio() debe de estar implementado. Para llenar, recorrer e imprimir
un vector podemos utilizar un bucle for:
#include <iostream>
using namespace std;
int main()
{
int dim;
cout << "Ingresa la dimension del vector" << endl;
cin >> dim; // Supongamos que ingrese 10
int vector[dim]; // mi vector es de tamanyo 10
for(int i = 0; i < dim; i++){
vector[i] = i * 10;
cout << vector[i] << endl;
}
return 0;
}
La salida del programa mostrar: 0 10 20 30 40 50 60 70 80 90 Fcil verdad? Bien ahora creen
2 o ms vectores y empiecen a hacer funciones bsicas como sumar, restar, buscar, ordenar,
moda, etc que ayudan mucho a ir desarrollando la lgica. No vale copiar y pegar, mejor es
practicar, practicar y practicar.
Aqu una funcin simple para sumar 2 vectores a y b y poner el resultado en un tercer vector c:
#include <iostream>
using namespace std;
void sumar(int a[], int b[], int c[],int dim) {
for (int i = 0; i < dim; i++) {
c[i] = a[i] + b[i];
}
}
void imprimir(int v[], int dim)
{
for(int i = 0; i < dim; i++) {
cout << v[i] << endl;
}
cout << endl << endl;
}
int main()
{
int dim;
cout << "Ingresa la dimensin" << endl;
cin >> dim;
int a[dim];
int b[dim];
int c[dim];
for(int i = 0; i < dim; i++) {
a[i] = i * 10;
b[i] = i * 5;
}
cout << "Vector A " << endl;
imprimir(a, dim);
cout << "Vector B " << endl;
imprimir(b, dim);
sumar(a, b, c, dim);
cout << "Vector C " << endl;
imprimir(c, dim);
return 0;
}
Si ingreso una dimensin de 10, este programa me dara:
Vector A
0 10 20 30 40 50 60 70 80 90
VECTOR B
0 5 10 15 20 25 30 35 40 45
VECTOR C
0 15 30 45 60 75 90 105 120 135
Entonces para tomar en cuenta:
Todo vector debe tener definido un tipo de dato.
Todo vector necesita de una dimensin o tamanyo.
El cdigo de arriba se puede mejorar muchsimo con objetos y clases, este es solo un pequeo
ejemplo.
MATRICES
Una matriz es un vector de vectores o un tambin llamado array bidimensional. La manera de
declarar una matriz es C++ es similar a un vector:
int matrix[rows][cols];
int es el tipo de dato, matrix es el nombre del todo el conjunto de datos y debo de especificar
el numero de filas y columnas.
Las matrices tambin pueden ser de distintos tipos de datos como char, float, double, etc. Las
matrices en C++ se almacenan al igual que los vectores en posiciones consecutivas de
memoria.
Usualmente uno se hace la idea que una matriz es como un tablero, pero internamente el
manejo es como su definicin lo indica, un vector de vectores, es decir, los vectores estn uno
detrs del otro juntos.
La forma de acceder a los elementos de la matriz es utilizando su nombre e indicando los 2
subndices que van en los corchetes.
Si coloco int matriz[2][3] = 10; estoy asignando al cuarto elemento de la tercera fila el valor 10.
No olvidar que tanto filas como columnas se enumeran a partir de 0. Bueno y para recorrer una
matriz podemos usar igualmente un bucle. En este caso usando 2 for:
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
matrix[i][j] = i % j;
}
}
PUNTEROS
El valor de todas las variales que manejamos en nuestros programas se almacenan en memoria
y tienen una direccin. Un puntero es una variable especial que apunta a la direccin de
memoria de una variable.
El puntero tiene a su vez su propia direccin. Todas estas direcciones tienen un formato
hexadecimal.
Los punteros son herramientas muy poderosas con muchas utilidades y enormes ventajas como
veremos ms adelante. A grandes rasgos, un puntero me permite desplazarme en la memoria,
apuntar, redireccionar a ciertas variables, funciones, mtodos, objetos, etc sin necesidad de
mover grandes bloques de datos, lo cual nos ahorra muchsimo el consumo de memoria en los
programas.
Un puntero se debe declarar de acuerdo al tipo de dato al que apunta. Ejem:
int *var; // un puntero que puede apuntar a cualquier variable de tipo
entero.
char *u; // puntero de tipo char
Persona *per; // puntero de tipo persona
Para determinar,asignar la direccin de una variable en c++, se usa el operador & y para
obtener el contenido de un puntero utilizamos el operador * Ejem:
int a; // entero
int *b; // puntero a entero
a = 20; // a tiene 20
b = &a; // asigno la direccin de a al puntero b
cout << b << endl; // imprime la direccin de memoria de a;
cout << *b; // imprime 20, osea el contenido de a
Ahora analicemos las siguientes instrucciones y veamos como las variables van cambiando de
valor en tiempo de ejecucin:
#include <iostream>
using namespace std;
int main()
{
int a;
int b;
cout << "Ingresa el valor de a: ";
cin >> a;
cout << endl;
cout << "Ingresa el valor de b: ";
cin >> b;
cout << endl;
// Punteros de tipo entero
int *p;
int *p2;
cout << "Antes" << endl;
cout << "Variable a = " << a << endl;
cout << "Direccion de a = " << &a << endl << endl;
cout << "Variable b = " << b << endl;
cout << "Direccion de b = " << &b << endl << endl;
// Tiene basura al principio, podria inicializar con *p=0
cout << "Contenido de p (Basura)= " << *p << endl;
cout << "Direccion de p = " << &p << endl << endl;
cout << "Despues" << endl;
a++;
p= &a;
cout << "Contenido de p = " << *p << endl;
// p ahora tiene la direccin de b
p = &b;
// Le sumo 20 al contenido de p, es decir, estoy incrementando el valor
de b
*p +=20;
cout << "Variable a = " << a << endl;
cout << "Variable b = " << b << endl << endl;
// p ahora tiene la direccin de a
p=&a;
// Contenido de p es igual al contenido de a * 5
*p = a * 5;
cout << "Contenido de p = " << *p << endl;
cout << "Variable a = " << a << endl << endl;
// Tiene basura al principio, podria inicializar con *p2=0
cout << "Contenido de p2 (Basura) = " << *p2 << endl;
cout << "Direccion de p2 = " << &p2 << endl << endl;
// El contenido de p es asignado al contenido de p2
p2 = p;
// Incremento 15 al contenido de p2
*p2 += 15;
cout << "Contenido de p2 = " << *p2 << endl;
// p apunta a otra direccin de memoria,se desplaza 4 bytes en memoria
p++;
// El contenido de esa nueva direccin
cout << "Contenido de p (Basura) = " << *p << endl;
return 0;
}
La salida del programa:
ANTES
Variable a = 10
Direccion de a = 0x22ff74
Variable b = 2
Direccion de b = 0x22ff70
Contenido de p (BASURA) = -1017291943
Direccion de p = 0x22ff6c
DESPUES
Contenido de p = 11
Variable a = 11
Variable b = 40
Contenido de p = 55
Variable a = 55
Contenido de p2 (BASURA) = 2293680
Direccion de p2 = 0x22ff68
Contenido de p2 = 70
Contenido de p (BASURA) = 2293680
El contenido de p y p2 al principio es basura porque no tienen ningun valor asignado aun.
Podriamos asignar el valor NULL a un puntero para luego posteriormente en algun problema
que se me presente saber el estado del puntero y saber si contiene algo o no, as:
int *p = NULL;
ARITMTICA DE PUNTEROS
En las ltimas sentencias del programa anterior: p++; cout << *p pueden visualizar que estoy
incrementando el puntero p en 1. Esto quiere decir que el puntero se desplazara 4 bytes en
memoria (en este caso por ser entero) y entonces apuntar a otra direccin.
Por eso es que el nuevo contenido de p es basura o bueno el contenido de lo que tiene esa
nueva direccin a la que apunta. Supongamos que definimos un entero y puntero de tipo char:
char c;
char *d;
d= &c; // asigno la direccion de c a d
c = 'u'; // asigno el valor u a mi variable c
c--; // desplazo una posicion a c
cout << *d;
No Imprimira u porque fijense que desplaz c en sentido negativo 1 byte (los char ocupan a 1
byte). Es decir, que si d estaba apuntado a una direccin como por ejemplo 0x22ff99, despues
del c estar apuntando a algo como 0x22ff98
Para tomar en cuenta, cosas que no puedo hacer con punteros:
int a = 15;
int *p;
double *q;
void *r;
// No puedo hacer lo siguiente:
p = a; // estoy asignando una variable a un puntero y un puntero es una
direccin.
p = &50; // 50 es un valor constante y no una variable, por lo tanto no tiene
direccin.
p = &(a+1); // una expresin no tiene direccin.
p = 30; // igual que el primer error, 30 es un entero.
&a = p; // no puedo cambiar la direccin de una variable.
p = q; // p es puntero de tipo entero y q de tipo double.
Un puntero de tipo void, es un puntero al cual le podemos asignar cualquier tipo de puntero.
Por lo tanto si podriamos hacer esto: r = p;
VECTORES Y PUNTEROS
Cuando declaramos un vector int v[10] el nombre del vector, o sea v, es un puntero al primer
elemento del vector, es decir a v[0].
Entonces como un vector es un puntero al primer elemento del mismo, tambin podramos
hacer aritmtica de punteros con el vector.
(v + 1) ; // apunta a v[1];
*(v + 5); // me refiero al contenido de v[5]
// Y tambin puede colocar ndices a los punteros:
int *p; // puntero de tipo entero
p = &v[0]; // p apunta a la direccin del vector v[0] o tambin a v. p = v
p[8] = 80; // le asigno el valor 80 al puntero en la posicion 8, es decir a
v[8]
VECTORES DINMICOS
Lo que vimos en el inicio de este post, son vectores estticos, puesto que tienen una cantidad
fija de memoria asignada y tamao definido que no podemos modificarlo. Sin embargo, un
vector podra tener una cantidad variable de datos, a este se le llama un vector dinmico.
Para usar vectores dinmicos necesitamos gestionar memoria dinmica. Si bien es cierto que es
trae enormes ventajas, el hacer un mal uso de la memoria dinmica nos podra traer problemas
desastrozos.
Por eso es importante que que cuando creemos vectores dinmicos tambin liberemos la
memoria utilizada. Obviamente eliminaremos la memoria utilizada cuando ya no necesitamos
ms usar, en este caso, un determinadao vector.
El operador new sirve para reservar memoria dinmica. El operador delete se usa para liberar la
memoria dinmica reservada con new.
Para liberar memoria de un array dinmico usamos delete[] El espacio de memoria que hemos
reservado con new tendr vida hasta que finalize la ejecucin del programa o cuando liberemos
ese espacio con delete.
Siempre es recomendable liberar memoria para posteriormente no tener problemas con
excesivo consumo de memoria. Un simple ejemplo:
#include <iostream>
using namespace std;
int main()
{
int *pv;
int dim;
cout << "Ingresa el tamanyo del vector" << endl;
cin >> dim;
pv = new int[dim];
for(int i = 0; i < dim; i++){
pv[i] = i * i;
cout << pv[i] << endl;
}
delete[] pv;
return 0;
}
MATRICES Y PUNTEROS
Supongamos que declaro una matriz int m[5][5] Como dijimos anteriormente, el nombre o
identificador de un vector es un puntero al primer elemento del vector.
En el caso de matrices el nombre de la matriz, en este ejemplo v, es un puntero que apunta al
primer elemento del primer vector de la matriz.
Entonces m es un doble puntero.m es igual a &m[0] que es igual a la direccion de &m[0][0].
Si declaramos un puntero int *pm y luego igualamos pm = m, p ahora puede desplazarse por
los valores de m. *p; // contenido de m[0], el cual apunta al primer elemento de ese vector, es
decir, m[0][0]
Tambin puedo referirme a los contenidos con aritmtica de punteros:
/* desplazo una posicin a p, se refiere al contenido de m[1],
el cual apunta al primer elemento de ese vector, es decir, m[1][0] */
*(p + 1);
/* desplazo una posicin en el vector principal y este a su vez se desplaza
una posicin en ese vector,
es decir, me refiero al contenido de m[1][1];*/
*(*(p + 1) + 1);
p[2][4] = 20; // asigno el valor 20 a la posicin 2,4 de la matriz
*(*(p + 2) + 4) = 20 // es lo mismo que la asignacin anterior
*(pm[2] + 4) = 20 // tambin lo mismo
// En conclusin:
p[i][j] = *(*(p + i) + j) = *(pm[i] + j)
MATRICES DINMICAS
Para crear una matriz dinmica debemos de crear un doble puntero int **pm y samos al igual
que los vectores el operador new para reservar memoria y delete para liberar. Primero tenemos
que crear el vector que contendr a otros vectores especificando el numero de vectores que
tendra este vector principal. Ejem:
pm = new int* [rows]; // creo el vector de punteros principal
for(int i = 0; i < rows; i++) {
pm[i] = new int[cols]; // para crear los vectores dentro del vector
principal
}
Ahora s veamos un simple programa que crea una matriz dinmica, asigna valores, muestra el
contenido de cada uno de los elementos los elementos as como sus direcciones de memoria.
Tambin mostramos la matriz usando aritmtica de punteros:
#include <iostream>
using namespace std;
int main()
{
// Puntero a una matriz
int **pm;
int cols;
int rows;
cout << "Ingresa nro de filas: ";
cin >> rows;
cout << endl;
cout << "Ingresa nro de columnas: ";
cin >> cols;
pm = new int* [rows];
for (int i = 0; i < rows; i++) {
pm[i] = new int[cols];
}
cout << "Elementos de la Matriz con sus direcciones: " << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
pm[i][j] = i + j;
cout << pm[i][j] << "--> ";
cout << &pm[i][j] << endl;
}
cout << endl;
}
cout << endl;
cout << "Elementos de la Matriz con sus direcciones, con aritmtica de
punteros: " << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// Aritmtica de punteros
*(*(pm + i) + j) = i + j;
cout << *(*(pm + i) + j) << "--> ";
cout << &pm[i][j] << endl;
}
cout << endl;
}
// Elimino cada vector de la matriz
for (int i = 0; i < rows; i++) {
delete[] pm[i];
}
// Elimino el vector principal
delete[] pm;
return 0;<!--EndFragment-->
}
La salida del programa: Ingresa el nro de filas: 6 Ingresa el nro de columnas: 4
//Elementos de la Matriz con sus direcciones
0> 03d2c90 1> 03d2c94 2> 03d2c98 3> 03d2c9c
1> 03d2ca8 2> 03d2cac 3> 03d2cb0 4> 03d2cb4
2> 03d2cc0 3> 03d2cc4 4> 03d2cc8 5> 03d2ccc
3> 03d3ab8 4> 03d3abc 5> 03d3ac0 6> 03d3ac4
4> 03d3ad0 5> 03d3ad4 6> 03d3ad8 7> 03d3adc
5> 03d3ae8 6> 03d3aec 7> 03d3af0 8> 03d3af4
//La matriz con aritmtica de
0> 03d2c90 1> 03d2c94 2>
1> 03d2ca8 2> 03d2cac 3>
2> 03d2cc0 3> 03d2cc4 4>
3> 03d3ab8 4> 03d3abc 5>
4> 03d3ad0 5> 03d3ad4 6>
punteros
03d2c98
03d2cb0
03d2cc8
03d3ac0
03d3ad8
3>
4>
5>
6>
7>
03d2c9c
03d2cb4
03d2ccc
03d3ac4
03d3adc
En mi caso esa son las direcciones de memoria.
El cdigo fuente de este y otros ejercicios de C++ est disponible en
Github: [Link]
Gracias por tu visita al Blog. Puedes seguirme en Twitter haciendo click en el siguiente
enlace: Follow @ronnyml