domingo, 23 de agosto de 2015

vectores en c++


← Uso de funcionesVectoresCadenas de caracteres →

Los vectores son una forma de almacenar datos que permiten contener una serie de valores del mismo tipo, cada uno de los valores contenidos tiene una posición asociada que se usará para accederlos. Está posición o índice será siempre un número entero positivo.
En C la cantidad de elementos que podrá contener un vector es fijo, y en principio se define cuando se declara el vector. Los vectores se pueden declarar de la siguiente forma:
 tipo_elemento nombre[largo];
Esto declara la variable nombre como un vector de tipo_elementos que podrá contener largo cantidad de elementos, y cada uno de estos elemento podrá contener un valor de tipo tipo_elemento.
Por ejemplo:
 double valores[128];
En este ejemplo declaramos un vector de 128 elementos del tipo double, los índices de los elementos irían entre 0 (para el primer elemento y 127 para el último).
De la misma forma que con las otras declaraciones de variables que hemos visto se le puede asignar un valor iniciar a los elementos.
O también se pueden declarar:
 tipo_elemento nombre[largo]={valor_0, valor_1, valor_2};
En caso estamos asignadole valores a los primeros 3 elementos del vector nombre. Notar que largo debe ser mayor o igual a la cantidad de valores que le estamos asignando al vector, en el caso de ser la misma cantidad no aporta información, por lo que el lenguaje nos permite escribir:
 tipo_elemento nombre[]={valor_0, valor_1, valor_2};
Que declarará nombre como el vector de largo 3.
Para acceder a un elemento accederemos a través de su posición. Es decir:
 tipo_elemento elemento;
 ...
 elemento = nombre[2];
Asumiendo que tenemos el vector anterior definido estaríamos guardando valor_2 en elemento.
Veamos algunos ejemplos:
/*
 * Ejemplo : El producto escalar de dos vectores
 */
#include <stdio.h>

double producto_escalar(double v1[], double v2[], int d);

int main()
{ 
 const int largo = 3;
 double vector_1[] = {5,1,0};
 double vector_2[] = {-1,5,3};

 double resultado = producto_escalar(vector_1, vector_2, largo);

 // imprime el resultado
 printf("(%f, %f, %f) . (%f, %f, %f) = %f\n",
  vector_1[0], vector_1[1], vector_1[2],
  vector_2[0], vector_2[1], vector_2[2],
  resultado);
 return 0;
}

/* producto escalar entre dos vectores */
double producto_escalar(double v1[], double v2[], int d)
{
 double resultado = 0;
 int i;
 for (i=0; i < d; i++) {
  resultado += v1[i] * v2[i];
 }
 return resultado;
}
En el ejemplo anterior usamos los vectores de C para representar vectores matemáticos y calcular el producto escalar entre ellos. Una peculiaridad que se puede notar es que al recibir un arreglo en una función no se especifica el largo, volveremos a esto en un capítulo posterior.
Otra función clásica es la búsqueda de un máximo o mínimo, que podemos escribirla de la siguiente manera:
int buscar_maximo(double valores[], int num_valores)
{
 int maximo_pos = 0;
 for (int i = 1; i < num_valores; i++) {
  if (valores[i] > valores[maximo_pos]) {
   maximo_pos = i;
  }
 }
 return maximo_pos;
}
Otro ejemplo sencillo, calcular el promedio de los valores.
double promedio(double valores[], int largo)
{
 double suma=0;
 for (int i=0;i<largo;i++) {
  suma+=valores[i];
 }
 return suma/largo;
}
Cuando una función recibe un vector por parámetro y cambia su contenido y el cambio es permanente (se ve aún fuera de la función). Esto puede parecer extraño después del énfasis que pusimos en resaltar que todos los parámetros de una función se reciben por valor, pero se aclarará en el siguiente capitulo.
Mientras tanto usemos esto para definir una función que le aplique otra función que recibe por parámetro a cada elemento del vector, guardando el resultado en el mismo vector y una llamada de ejemplo a esta.
void cuadrados(double vector[], int largo)
{
 for (int i=0;i<largo;i++) {
  vector[i]=cuadrado(vector[i]);
 }
}
...
double cuadrado(double valor) {
 return valor*valor;
}
...
 cuadrados(elementos,num_elem);
...
De la misma forma que venimos usando vectores de tipos básicos, podemos tener vectores de vectores, estos se declaran de la siguiente forma:
int matriz[3][7];
int tabla[3][4]={ { 1, 2, 3, 4},
    { 5, 6, 7, 8}, /* los espacios y saltos de líneas no son tomados en cuenta */
    { 9,10,11,12} };
double v[2][2][2];
...
printf("tabla[0][1]: %i\n", tabla[0][3]); // Imprime 4
printf("tabla[2][0]: %i\n", tabla[2][0]); // Imprime 9 
...
En este ejemplo tabla es un vector de longitud 3, cuyos elementos son vectores de longitud 4 de elementos de tipo int.
En resumen, suponiendo que v[n] es un vector de cualquier tipo de dato con n cantidad de posiciones, al vector v se le aplican las siguientes reglas:
  1. La primera posición siempre será v[0]
  2. La última posición es v[n-1]
  3. En versiones previas a C99 n es una constante definida antes de la declaración de v[n]+

funcion en c++

^
Las funciones son un conjunto de instrucciones que realizan una tarea específica. En general toman ciertos valores de entrada, llamados parámetros y proporcionan un valor de salida o valor de retorno; aunque en C++, tanto unos como el otro son opcionales, y pueden no existir.
Tal vez parezca un poco precipitado introducir este concepto tan pronto en el curso. Sin embargo, las funciones son una herramienta muy valiosa, y como se usan en todos los programas C++, creo que debemos tener, al menos, una primera noción de su uso. A fin de cuentas, todos los programas C++ contienen, como mínimo, una función.

Prototipos de funciones

^
En C++ es obligatorio usar prototipos. Un prototipo es una declaración de una función. Consiste en una presentación de la función, exactamente con la misma estructura que la definición, pero sin cuerpo y terminada con un ";". La estructura de un prototipo es:
[extern|static] <tipo_valor_retorno> [<modificadores>] <identificador>(<lista_parámetros>); 
En general, el prototipo de una función se compone de las siguientes secciones:
  • Opcionalmente, una palabra que especifique el tipo de almacenamiento, puede ser extern o static. Si no se especifica ninguna, por defecto será extern. No te preocupes de esto todavía, de momento sólo usaremos funciones externas, lo menciono porque es parte de la declaración.
  • El tipo del valor de retorno, que puede ser void, si no necesitamos valor de retorno. En C, si no se establece, será int por defecto, aunque en general se considera una mala técnica de programación omitir el tipo de valor de retorno de una función. En C++ es obligatorio indicar el tipo del valor de retorno.
  • Modificadores opcionales. Tienen un uso muy específico, de momento no entraremos en este particular, lo veremos en capítulos posteriores.
  • El identificador de la función. Es costumbre, muy útil y muy recomendable, poner nombres que indiquen, lo más claramente posible, qué es lo que hace la función, y que permitan interpretar qué hace el programa con sólo leerlos. Cuando se precisen varias palabras para conseguir este efecto se puede usar alguna de las reglas más usuales. Una consiste en separar cada palabra con un "_". Otra, que yo prefiero, consiste en escribir la primera letra de cada palabra en mayúscula y el resto en minúsculas. Por ejemplo, si hacemos una función que busque el número de teléfono de una persona en una base de datos, podríamos llamarla "busca_telefono" o "BuscaTelefono".
  • Una lista de declaraciones de parámetros entre paréntesis. Los parámetros de una función son los valores de entrada (y en ocasiones también de salida). Para la función se comportan exactamente igual que variables, y de hecho cada parámetro se declara igual que una variable. Una lista de parámetros es un conjunto de declaraciones de parámetros separados con comas. Puede tratarse de una lista vacía. En C es preferible usar la forma "func(void)" para listas de parámetros vacías. En C++ este procedimiento se considera obsoleto, se usa simplemente "func()".
Por ejemplo:
int Mayor(int a, int b);
Un prototipo sirve para indicar al compilador los tipos de retorno y los de los parámetros de una función, de modo que compruebe si son del tipo correcto cada vez que se use esta función dentro del programa, o para hacer las conversiones de tipo cuando sea necesario.
En el prototipo, los nombres de los parámetros son opcionales, y si se incluyen suele ser como documentación y ayuda en la interpretación y comprensión del programa. El ejemplo de prototipo anterior sería igualmente válido si se escribiera como:
int Mayor(int, int);
Esto sólo indica que en algún lugar del programa se definirá una función "Mayor" que admite dos parámetros de tipo int y que devolverá un valor de tipoint. No es necesario escribir nombres para los parámetros, ya que el prototipo no los usa. En otro lugar del programa habrá una definición completa de la función.
Normalmente, los prototipos de las funciones se declaran dentro del fichero del programa, o bien se incluyen desde un fichero externo, llamado fichero de cabecera, (para esto se usa la directiva #include, que veremos en el siguiente capítulo).
Ya lo hemos dicho más arriba, pero las funciones son extern por defecto. Esto quiere decir que son accesibles desde cualquier punto del programa, aunque se encuentren en otros ficheros fuente del mismo programa.
En contraposición las funciones declaradas static sólo son accesibles dentro del fichero fuente donde se definen.

Definición de funciones

^
Al igual que hemos visto con las variables, las funciones deben declararse, para lo que usaremos los prototipos, pero también deben definirse.
Una definición contiene además las instrucciones con las que la función realizará suTRABAJO, es decir, su código.
La sintaxis de una definición de función es:
[extern|static] <tipo_valor_retorno> [modificadores] <identificador>(<lista_parámetros>)
{
   [sentencias]
}
Como vemos, la sintaxis es idéntica a la del prototipo, salvo que se elimina el punto y coma final, y se añade el cuerpo de función que representa el código que será ejecutado cuando se llame a la función. El cuerpo de la función se encierra entre llaves "{}".
La definición de la función se hace más adelante o más abajo, según se mire, es decir, se hace después que el prototipo. Lo habitual es hacerlo después de la función main.
Una función muy especial es la función main, de la que ya hablamos en el capítulo primero. Se trata de la función de entrada, y debe existir siempre, ya será la que tome el control cuando se ejecute el programa. Los programas Windows usan la función WinMain como función de entrada, aunque en realidad esta función contiene en su interior la definición de una función main, pero todo esto se explica en otro lugar.
Existen reglas para el uso de los valores de retorno y de los parámetros de la función main, pero de momento la usaremos como int main() o intmain(void), con un entero como valor de retorno y sin parámetros de entrada. El valor de retorno indicará si el programa ha terminado sin novedad ni errores retornando cero, cualquier otro valor de retorno indicará un código de error.

Estructura de un programa C++

^
La estructura de un programa en C o C++ quedaría así:
[directivas del pre-procesador: includes y defines]
[declaración de variables globales]
[prototipos de funciones]
[declaraciones de clases]
función main 
[definiciones de funciones]
[definiciones de clases]
También se puede omitir el prototipo si se hace la definición antes de cualquier llamada a la función, es decir, en la zona de declaración de prototipos. Esto se puede hacer siempre, sin embargo no es muy recomendable como veremos a lo largo del curso.
Para no dejar las cosas "a medias", podemos ver una posible definición de la función "Mayor", que podría ser la siguiente:
int Mayor(int a, int b)
{
   if(a > b) return a; else return b;
}

Estructuras más complejas

Los programas complejos se escriben normalmente usando varios ficheros fuente. Estos ficheros se compilan separadamente y se enlazan todos juntos. Esto es una gran ventaja durante el desarrollo y depuración de grandes programas, ya que las modificaciones en un fichero fuente sólo nos obligarán a compilar ese fichero fuente, y no el resto, con el consiguiente ahorro de tiempo.
La definición de las funciones y clases puede hacerse dentro de los ficheros fuente o también pueden enlazarse desde bibliotecas compiladas previamente.
En C++ es obligatorio el uso funciones prototipo, y aunque en C no lo es, resulta altamente recomendable.