Es un lenguaje de programación de propósito general que combina distintos paradigmas o modelos de programación. El lenguaje nació en la década de los ochenta en los laboratorios Bell de AT&T, donde fue diseñado por el Profesor Bjarne Stroustrup. Inicialmente conocido como “C con clases”, empezó a ganar popularidad muy rápido y a principios de los años 90 comenzó su proceso de normalización.
IMPORTANCIA DEL C++
C++ es uno de los lenguajes más usados. Según las estimaciones realizadas por la consultora IDC, el número de desarrolladores que usan C++ está por encima de los 3 millones. Especialmente desde 2010 hay un interés renovado por el lenguaje, puesto que tanto en las aplicaciones móviles como en los grandes centros de datos el rendimiento por vatio (cantidad de cálculos realizados por cada vatio de energía consumido) se ha convertido en una métrica fundamental.
¿Qué ventajas tiene respecto a otros lenguajes de programación?
El lenguaje C++ tiene una orientación clara hacia el uso eficiente de los recursos y al mismo tiempo permite construir abstracciones complejas que hagan uso de los computadores. Si bien es cierto que existen otros lenguajes de programación que pueden competir en el uso eficiente de los recursos, normalmente fracasan en la gestión de la complejidad. En resumen, no existen muchas más alternativas para escribir software complejo si se desea obtener un buen rendimiento. Por esta razón, C++ triunfa especialmente en el desarrollo de sistemas de software complejo que necesita una alta velocidad o bien necesita ejecutarse en entornos muy restringidos. Esto hace que el lenguaje sea usado desde en aplicaciones de simulación financieras hasta en sistemas de control de vehículos. Como curiosidad, una parte relevante del software que controla la sonda Mars Rover está escrita en C++.
Todo principiante en el campo de la programación se enfrenta a numerosos interrogantes. Independientemente de si se trata de la codificación, de los métodos o de sus herramientas, sin experiencia es muy difícil tener una visión general. Aprender a programar desde cero es una tarea compleja, especialmente si quieres aprenderlo por tus propios medios. En estas secciones hemos resumido aspectos importantes y que necesitas saber para dar tus primeros pasos en el mundo del código, de los comandos y del lenguaje en C++.
Expresado formalmente,
un Array o Arreglo de datos, es una estructura de datos que permite almacenar
un conjunto de datos del mismo tipo, definiendo el intervalo con un límite
inferior y un límite superior, con un número denominado Índice hacemos
referencia a cada elemento individual del arreglo. Se utilizará indistintamente
el término Array o Arreglo.
Para comprender mejor
los arrays, resulta muy útil la siguiente descripción gráfica.
La sintaxis para definir un arreglo en
C++ es la siguiente:
TipoDato
nombreArray[TamañoArray];
en donde,
TipoDato: Tipo de dato que tendrán los elementos del arreglo. nombreArray: Nombre que asignamos para referirnos al arreglo. TamañoArray: Valor
entero que delimita el tamaño del arreglo.
Declarando un Arreglo
Si deseamos declarar
un arreglo llamado arrTemp con 5 elementos de punto flotante,
debemos hacerlo de esta forma:
float
arrCalif[5]; //Declara un arreglo de 5 elementos de punto flotante llamado
arrCalif
También podemos
declarar una arreglo sin límites predeterminados,
float
arrCalif[ ]; //Declara un arreglo de elementos de punto flotante llamado
arrCalif sin límite.
Accediendo a los elementos del Arreglo
Nota: Los índices de los arreglos se
numeran a partir de 0, de modo que si un arreglo tiene por ejemplo 4 elementos,
los índices serán 0, 1, 2, 3
Para acceder a un
elemento del arreglo debemos usar el nombre del arreglo utilizando el índice
del elemento que nos interesa. Por ejemplo, si queremos asignar los valores 2,
5, 8, -9, 100 al arreglo arrTemp lo haremos de la siguiente forma:
arrTemp[0] = 2;
arrTemp[1] = 5;
arrTemp[2] = 8;
arrTemp[3] = -9;
arrTemp[4] = 100;
Para que visualices lo
que hemos hecho hasta el momento, apóyate en la siguiente imagen.
Acceso a los
elementos de un Arreglo
Podemos recuperar de
varias formas un elemento de un arreglo, pero el concepto siempre es el mismo,
tomar el índice del elemento que nos interesa y asignarlo a una variable del
mismo tipo.
Por lo tanto en
resumen un arreglo en C++ es un
conjunto de datos que se almacenan en memoria de manera contigua con el mismo
nombre. Para diferenciar los elementos de un arreglo se utilizan índices detrás
del nombre del arreglo y encerrados por [ ].
EJEMPLO: int vector [10]; // array de 10 enteros: vector[0]..vector[9].
Arreglos unidimensionales en C++
En
el lenguaje C++ un arreglo puede tener varias dimensiones, en esta ocasión
hablaremos de los arreglos unidimensionales (una dimensión).
Arreglos
Unidimensionales
Un
arreglo unidimensional es una lista de valores guardados bajo el mismo nombre y
del mismo tipo. Cada valor dentro del arreglo se le conoce como elemento del
arreglo.
Para
declarar un arreglo unidimensional en C++ se utiliza la siguiente línea de
código:
tipo_dato
identificador[tamaño];
En
“tipo de dato” introduciremos que tipo de dato queremos (int, char, float..
etc) , luego en “identificador” introducimos como llamaremos a nuestro arreglo
para ubicarlo e identificarlo de manera rápida. Y entre los corchetes
colocaremos el tamaño que le demos a nuestro array. Quedando de esta manera:
intarreglo[3];
Ya que tenemos declarado nuestro arreglo
necesitamos declarar cada uno de los elementos que lo conforman, para esto
utilizamos lo siguiente:
Iniciamos con el nombre de nuestro
arreglo en el apartado que dice arreglo, entre los corchetes colocaremos que
parte del array utilizaremos, en C++ los valores se inician en 0, esto quiere
decir que si nuestro arreglo es de tamaño 3, al hacer nuestro índice hay que
tomar el 0 como valor inicial quedando [0] , [1] y [2] completando así nuestros
3 valores del tamaño antes mencionado.
En la parte “valor” asignaremos el valor
que le daremos a ese elemento quedando de la siguiente manera:
Ejemplo: el siguiente programa carga el
arreglo sqrs con los cuadrados de los números del 1 al 10 y luego los visualiza .
La forma como se almacenan los valores
en el arreglo es la siguiente:
sqrs[0] = 1*1
sqrs[1] = 2*2
sqrs[2] = 3*3
sqrs[3] = 4*4
sqrs[4] = 5*5
sqrs[5] = 6*6
sqrs[6] = 7*7
sqrs[7] = 8*8
sqrs[8] = 9*9
sqrs[9] = 10*10
ARREGLOS
BIDIMENSIONALES
¿Qué es un “Arreglo bidimensional”?: Es una matriz de datos de tamaño m x n
que contiene información almacenada del mismo tipo de datos (int, float, char,
etc). Este tipo de arreglos necesita dos subíndices para ser declarado o para
acceder a la información de un elemento en específico, a diferencia de una
matriz unidimensional que solo necesita un subíndice. Un arreglo
bidimensional es utilizado cuando queremos guardar mucha información sobre
un tipo de dato en específico en distintas filas. Por ejemplo, si quieres crear
una base de datos donde queremos guardar varios nombres, en un vector no se
podría hacer pues solo podrías guardar un nombre, en cambio, con un arreglo
bidimensional puedes guardar un nombre por fila, por lo tanto, si creamos
varias filas podemos guardar varios nombres.
¿Cómo declarar un arreglo bidimensional?: La sintaxis para declarar un arreglo
bidimensional es la siguiente:
·tipo_dato: Nos referimos al tipo de dato que estaremos
manejando en el arreglo, puede ser char, int, float, etc.
·nombre_arreglo: El nombre con el que identificaremos
nuestra matriz.
·tamaño_fila: La cantidad de filas que tendrá nuestra
matriz.
·tamaño_columna: La cantidad de columnas que tendrá
nuestra matriz.
Por ejemplo, si
queremos crear una matriz de 3 filas y 4 columnas de tipo de dato int y
con nombre “matriz”, lo haríamos de la siguiente manera:
Código:
int matriz[3][4];
Esta matriz tendría 12 espacios para
guardar información (3x4), y gráficamente lo veríamos de la siguiente manera:
Inicialización de un arreglo bidimensional: Podemos inicializar un arreglo
bidimensional al momento de declararlo, indicando los valores que queremos
entre corchetes y separados por coma, por ejemplo:
Código:
int matriz[4][5] = {1,2,3,4,5,6,7,8,9,10,11,12};
Y gráficamente los valores estarían
guardados de la siguiente manera:
Ejercicio
Realizar un programa en el que se puedan
guardar 5 nombres de máximo 20 caracteres. El usuario ingresará los nombres que
quiere guardar y posteriormente se mostrarán los nombres que ingresó.
Programa
Código:
/* Introducción a Arreglos - Por
Solución Ingenieril*/
#include <stdio.h> /* Declaración librerías*/
#include
<stdlib.h>
#include
<string.h>
int main(){
int contador;//Variable
contadora de ciclos
char nombre[5][20];
//Arreglo que guardara los nombres
//Ciclo en el que
solicitamos los nombres a ingresar
for(contador=0;contador<5;contador++){
printf("Ingresa
un nombre: ");
gets(nombre[contador]);
}
//Ciclo que imprime los
nombres guardados
printf("\nNombres
ingresados: \n");
for(contador=0;contador<5;contador++){
puts(nombre[contador]);
}
return 0;
}
Resultados
PASOS DE ARREGLOS FUNCIONES
Por default, los arrreglos en C se pasan a una función como
referencia y no como valor. Esto significa que todas las modificaciones que
hagamos dentro de la función en C al arreglo que recibimos como parámetro,
realmente se realizan en el arreglo original que se utilizó como argumento al
momento de llamar a la función.
Al escribir una
función en C, la forma de indicar que uno de los parámetros que se va a recibir
es un arreglo de una dimensión, es decir de que tipo va a ser el arreglo y el
nombre con el cual vamos a manipular dicho arreglo dentro de nuestra función
seguido de corchetes que abren y cierran; nuestra función también debe recibir
un segundo parámetro que nos indique el tamaño del arreglo, o dicho de otra
forma, el número de elementos de los que consta nuestro arreglo, recordemos que
como el arreglo se pasa a la función como referencia, lo que esta recibiendo la
función en realidad es un apuntador al primer elemento del arreglo, pero no
sabe en donde termina el arreglo, por eso es necesario que la función también
reciba como parámetro el número de elementos del arreglo.
Por ejemplo, el
prototipo de una función en C que va a regresar un entero y va a recibir un
arreglo de 10 elementos de tipo entero sería algo asi:
int MiFuncion(int
mi_arreglo[], int num_elemetos);
Para llamar a la
función anterior, se pone como primer argumento el nombre del arreglo (sin
corcehetes) y como segundo argumento el número de elementos del arreglo.
Supongamos que tenemos un arreglo de 10 elementos de tipo entero llamado numeros
y queremos guardar en una variable llamada «x» el valor que nos regresa la
función «MiFuncion» al llamarla pasandole como argumentos el arreglo «numeros»,
haríamos algo como esto
x =
MiFuncion(numeros, 10);
El siguiente
programa solicita al usuario que ingrese 10 números de tipo entero y los
almacena en un arreglo; después le pide que introduzca un número para que
busque su posición dentro del arreglo..
El programa utiliza
una función llamada BuscaNumero que recibe como parámetros el arreglo con los 10
números capturados, el número de elementos del arreglo (en este caso 10) y el
número del cual se desea saber su posición dentro del arreglo.. La función
regresa -1 si el número que se busca no se encuentra en el arreglo y en caso
contrario, regresa la primera posición del arreglo que contiene dicho número.
El programa también
utiliza una función llamada MuestraArreglo que no regresa valor alguno, sólo
recibe como parámetros el arreglo y el número de elementos. Esta función
imprime en pantalla los elementos del arreglo separados por un tabulador.
Para pasar a una función un arreglo de dos
dimensiones, si debemos indicar el tamaño de la segunda dimensión del arreglo
Ejemplo:int
MiFuncion(int mi_arreglo[][5], int num_elementos);
El siguiente
programa le pide al usuario que introduzca 9 números y los almacena en un
arreglo de dos dimensiones, en este caso una matriz de 3×3; posteriormente
utiliza una función llamada ImprimeMatriz para mostrar como quedaron
almacenados los números en la matriz. Dicha función recibe como parámetros, la
matriz de 3×3 y el tamaño de la primera dimensión (normalmente la primera
dimensión son filas y la segunda dimensión columnas).
Los
punteros (o apuntadores) son variables que se utilizan para almacenar
direcciones de memoria, puntualmente las direcciones de memoria que fueron
asignadas a variables convencionales en las que se almacenan datos de distinto
tipo. Vale la pena entonces recordar que a todas las variables en C++ se les
asigna un espacio de memoria en el cual se va almacenar el valor que se le
asigne en algún punto de la aplicación a esa variable, el tamaño de dicho
espacio va depender del tipo de dato que se pretende almacenar en la variable,
del compilador y de la arquitectura del procesador. Cada uno de los espacios de
memoria cuenta con una dirección para identificarlo, esta dirección es por lo
general un número en representación hexadecimal. Es precisamente ese número
correspondiente a la dirección lo que se almacena en un puntero.
Los punteros se usan ampliamente en C y
C++ para tres propósitos principales:
·para asignar nuevos objetos en el
montón,
·para pasar funciones a otras funciones
·para recorrer en iteración los elementos
de matrices u otras estructuras de datos.
Los punteros permiten
simular el paso por referencia, crear y manipular estructuras dinámicas de
datos, tales como listas enlazadas, pilas, colas y árboles. Generalmente las
variables contienen valores específicos. Para obtener o modificar el valor de
la variable a la que apuntan se utiliza el operador de indirección. Los
punteros, al ser variables deben ser declaradas como punteros antes de ser
utilizadas.
Sintaxis
int*ptrID,ID;
ID=8;
ptrID=&ID;// puntero a ID
ptrID es un puntero a int, mientras que
la variable ID es solo una variable del tipo int. Todo puntero debe ser
precedido por un asterisco (*) en la declaración.
Se puede declarar más de un puntero en
la misma sentencia. En el ejemplo que sigue se ve la declaración de dos
punteros a int.
int*ptrY,*ptrX;
C++ moderno proporciona punteros
inteligentes para asignar objetos, iteradores para recorrer estructuras de datos y expresiones
lambda para pasar funciones. Al usar estas instalaciones
de lenguaje y biblioteca en lugar de punteros sin formato, hará que el programa
sea más seguro, más fácil de depurar y más sencillo de entender y mantener.
Ejemplo de punteros
Muy
bien, ya hemos creado y usado nuestro primer puntero ¿Notaste el uso del asterisco y del ampersand? espero que sí y además de eso hay otros detalles que
debemos considerar, veamos:
Detalles al crear y usar punteros en C++
El tipo de dato del apuntador debe coincidir con
el de la variable cuya posición en memoria apuntan. En el ejemplo vemos
que tanto variable como apuntador son enteros.
Siempre que queremos usar el apuntador debemos
anteponer el asterisco (*) para indicar que usaremos el valor en la posición de
memoria apuntada.
De no usar el asterisco el comportamiento sería
impredecible. Estaremos haciendo uso de la dirección de memoria más no del
valor almacenado en ésta.
Después de usar un puntero, especialmente si
trabajamos con arreglos o matrices, es MUY recomendable liberar la memoria
utilizada con la función delete (tal como en el ejemplo)
Un puntero o apuntador puede ser de cualquier
tipo de dato, inclusive los podemos usar con tipos complejos.
OPERADORES
Ya
que sabemos algunos trucos y detalles sobre los apuntadores en C++, vamos a
definir formalmente la utilidad del operador & y del asterisco.
Los punteros y el ampersand
El ampersand es un operador de C++ y es comúnmente utilizado
para los punteros. Este operador nos permite obtener la dirección de memoria de
una variable cualquiera y es justo esto (la dirección en memoria) lo que
utilizan los punteros para referenciar valores.
Los apuntadores y el asterisco
El asterisco es, por decirlo de alguna forma, el
operador por excelencia de los punteros. SU utilidad radica en que si el valor
de dicho apuntador corresponde a una dirección de memoria, el asterisco nos
permite resolverla y acceder al valor almacenado allí. Viéndolo desde otro
enfoque, un apuntador es únicamente una dirección de memoria (un número) y
el asterisco es el que hace la magia de obtener el valor
referenciado por dicha dirección.
En otras palabras existen
dos operadores a tener en cuenta cuando trabajamos con punteros. El operador
de dirección (&) que devuelve la dirección de memoria de su
operando y el operador de indirección (*) que devuelve un
alias para el objeto al cual apunta el operando del puntero.
En el siguiente ejemplo vemos como se
inicializa una variable X con el valor 15. Luego se crea un puntero a int y por
último el puntero pasa a apuntar a la variable X. Esto es, ptrX es un puntero a
X.
intX=15;
int*ptrX;
ptrX=&X;
DECLARACIÓN DE PUNTEROS
Declaraciones
Se
puede declarar un puntero para almacenar la dirección de memoria
correspondiente a la variable var,
es decir, se puede "apuntar" un puntero a la variable var.
Para declarar un puntero se utiliza la sintaxis para declaración de variables:
calificadores opcionales, modificadores opcionales, tipo de dato obligatorio y
un identifador para el puntero que también es obligatorio. El tipo de dato del
puntero debe ser obligatoriamente el mismo tipo de dato de la variable a la que
se pretende apuntar, es decir, si se requiere almacenar la dirección en memoria
de una variable de tipo int,
entonces el tipo de dato del puntero también debe ser int.
Un puntero se distingue de otras variables porque en su declaración se utiliza
el operador * luego
del tipo de dato y antes del identificador del puntero. Observe a continuación
la declaración de varios punteros:
int *puntero_a_int;
float *puntero_a_float;
ClaseA
*puntero_a_objeto_claseA;
Para
apuntar un puntero a una variable se utilizan el operador de asignación =,
el operador & y
la variable a la que se quiere apuntar. Con el operador & se
obtiene la dirección de la variable y se le asigna al puntero mediante el
operador de asignación =.
Para
declarar un puntero se le debe informar a C que es lo que uno desea
almacenar en memoria, por lo tanto se le informa el tipo de lo almacenado,
por ejemplo:
§char
*p; (puntero a char)
§int *p;
(puntero a int)
§float *p; (puntero a float)
La forma general de la declaración de un puntero es tipo*variable;
OPERACIONES CON PUNTEROS
Un puntero es un tipo de dato similar a un entero, y
hay un conjunto de operaciones definidas para punteros:
La suma
o resta de un entero produce una nueva localización de memoria.
Se
pueden comparar punteros, utilizando expresiones lógicas, para ver si
están apuntando o no a la misma dirección de memoria.
La
resta de dos punteros da como resultado el número de variables entre las
dos direcciones.
if (princPunt ==
finPunt)
cout << " Esto no puede suceder "
<< '\n';
cout
<< "Numero de elementos \t" <<finPunt-princPunt <<
'\n';
}
El resultado de la ejecución de este programa es:
15 15
Numero de elementos 2
Veamos cómo trabaja este programa:
princPunt
es la dirección del primer elemento de vector, y finPunt la dirección del
último elemento. int princPunt = vector; es una combinación de declaración
y definición.
La
expresión *(princPunt+2) incrementa el valor del puntero princPunt en dos
y devuelve el número guardado en esa localización, es decir, apunta a la
tercera localización de memoria y su valor es 15.
Se
pueden utilizar también enteros en formato hexadecimal. Así,
cout
<< *(princPunt + 17 )
y
cout
<< *(princPunt + 0x11 )
producen la misma salida.
La
expresión princPunt == finPunt comprueba si los dos punteros son iguales.
Esto sólo puede ser verdad si los dos punteros apuntan a la misma
variable.
Siempre que se realiza una operación aritmética sobre
un puntero, sumando o restando un entero, el puntero se incrementa o decrementa
un número apropiado de sitios tal que el nuevo valor apunta a la variable que
está n elementos (no n bytes) antes o después que el dado. De la misma forma,
al restar dos punteros se obtiene el número de objetos entre las dos localizaciones.
Finalmente, dos punteros son iguales si y sólo si apuntan a la misma variable
(el valor de las direcciones es el mismo). No son necesariamente iguales si sus
valores indirectos son los mismos, ya que estas variables podrían estar en
diferentes localizaciones de memoria.