• No se han encontrado resultados

c++

N/A
N/A
Protected

Academic year: 2020

Share "c++"

Copied!
81
0
0

Texto completo

(1)

C++

Nivel Desarrolladores

Guía del Estudiante

(2)

FUNDAMENTACIÓN DEL CURSO

C++ es un lenguaje híbrido que se puede compilar y resulta más sencillo de aprender para los programadores que ya conocen C. En la actualidad, existe un estándar denominado ISO C++, al que se han adherido la mayoría de los fabricantes de compiladores más modernos. Las características principales del C++ son: el soporte para programación orientada a objetos y el soporte de plantillas o programación genérica (templates). Por lo tanto, se puede decir que C++ es un lenguaje que abarca tres paradigmas de la programación, como son la programación estructurada, la programación genérica y la programación orientada a objetos.

Además posee una serie de propiedades difíciles de encontrar en otros lenguajes de alto nivel:

• Posibilidad de redefinir los operadores (sobrecarga de operadores)

• Identificación de tipos en tiempo de ejecución (RTTI)

(3)

DESARROLLO DEL CURSO

El propósito fundamental del curso es introducir al participante en las características del entorno de desarrollo y del lenguaje de programación C++, desarrollando ejemplos que permitan dar a conocer las posibilidades del mismo.

El Curso esta dirigido a:

• Público en general con dominio teórico de la programación orientada a

objetos.

DATOS DE IDENTIFICACIÓN DEL CURSO

DEPENDENCIA ACADÉMICA: Academia de Software Libre Módulo: Desarrollador en Software Libre Curso: C++

UBICACIÓN DE LA ASIGNATURA EN EL

CURRÍCULO:

Área Curricular: Formación en Software Libre

Eje Curricular: Desarrollador en Software libre

DURACIÓN: 40 horas

RESPONSABLE DE DISEÑO

INSTRUCCIONAL: Ministerio del Poder Popular para Ciencia y Tecnología

RESPONSABLE DE CONTENIDO Ministerio del Poder Popular para Ciencia y Tecnología

FECHA DE ELABORACIÓN: Noviembre de 2007

PRE-REQUISITOS:

(4)

OBJETIVO GENERAL

Capacitar al participante para utilizar el lenguaje de programación C++ y su entorno de desarrollo.

OBJETIVOS ESPECÍFICOS

Al finalizar el curso sobre el uso del lenguaje C++, el participante será capaz de:

1. Identificar el compilador y el preprocesador 2. Describir los elementos básicos del lenguaje. 3. Manipular los datos en el lenguaje.

4. Demostrar como funcionan las herramientas de control de flujo. 5. Utilizar las funciones predefinidas del lenguaje.

6. Construir funciones propias y arreglos. 7. Emplear apuntadores.

8. Emplear estructuras de datos y apuntadores. 9. Manipular las clases y los objetos del lenguaje. 10. Utilizar correctamente archivos.

CONTENIDO PROGRAMÁTICO

Unidad 1: Conceptos básicos

Unidad 2: Elementos básicos del lenguaje • Unidad 3: Instrucciones de control de flujo • Unidad 4: Funciones

Unidad 5: Arreglos • Unidad 6: Apuntadores • Unidad 7: Tipos

(5)

UNIDAD 1: CONCEPTOS BÁSICOS

Estructura de un programa en C

Un programa se conforma por la cabecera y el cuerpo del programa.

Dentro de la cabecera se le incluyen a los programas algunas rutinas predefinidas que hacen a la programación más sencilla, ya que no se tiene que crear todo desde cero o "tratar de inventar la rueda", es muy bueno que se conozca la mayor cantidad de librerías disponibles para hacer el trabajo más fácil y estandarizado. Un programa puede no tener cabecera, a continuación se presenta un ejemplo de una cabecera para un programa sencillo.

#include <iostream.h>

El cuerpo del programa cuenta con la función principal, las funciones adicionales y las clases que se necesiten en el mismo.

La mejor forma de aprender un lenguaje es programando con él. El programa más sencillo que se puede escribir en C++ es el siguiente:

void main() {

}

Como es obvio, este programa no hace nada, pero contiene la parte más importante de cualquier programa C++ y además, es el más pequeño que se puede escribir y que se compile de forma correcta. En éste se define la función main, que es la que ejecuta el sistema operativo al llamar a un programa C++. La función principal (void main) o cualquier otra función siempre va seguida de paréntesis. La definición del cuerpo de la función se forma por un bloque de sentencias encerrado entre llaves {}.

(6)

Archivos de cabeceras

La función principal del preprocesador es incluir los headers o cabeceras. Lo que se desea es que cuando se quiera hacer un llamado a las funciones que se construyen a partir del lenguaje, como por ejemplo sacar algo por pantalla, se utiliza #include para incluir el archivo que define dichas funciones.

Las cabeceras más utilizadas son: iostream.h, para imprimir por pantalla y leer desde el teclado, y math.h, que contiene funciones matemáticas, como raíces cuadradas y logaritmos. Estos archivos se llaman cabeceras de sistema, porque no han sido creadas por el usuario, sino que son parte del compilador. Para incluirlas, se colocan entre corchetes:

#include <iostream.h> #include <math.h>

Esto le indica al preprocesador que se desea incluir las cabeceras de sistema, y no otras creadas por los usuarios.

Otras cabeceras importantes del sistema son:

limits.h que define los valores mayor y menor permitidos para cada uno de

los tipos de enteros.

float.h que define las características de los tipos reales, incluyendo su

precisión, valores máximos y mínimos, y rango del exponente.

ctype.h que define las funciones, como isupper(), isdigit(), ispunct()

para la clasificación de caracteres.

iomanip.h que define los manipuladores para dar formato de

entrada/salida.

Comentarios

/* Esto es un comentario */

Además, C++ define otra forma de comentar una línea. Un comentario comienza con //, y continúa hasta el final de esa línea, donde termina automáticamente:

(7)

Con el nuevo método se impide comentar de forma inadvertida varias líneas de código. Esto podía ocurrir en C cuando se olvidar incluir el final de la notación de comentario. Las dos formas de comentario pueden anidarse en C++. Además, existe otra forma de comentario, el cual utiliza el preprocesador, vía #ifdef, #endif. Este es el mejor método para comentar varias líneas de código, ya que /*

y */ no funcionarán si el código contiene comentarios del mismo tipo: #if 0

a = b + c; x = u * v; #endif

Directivas del Preprocesador

El preprocesador es una forma de indicarle al compilador de C++ cómo se debe manipular el programa antes de compilarlo. Por ejemplo, se puede querer dividir un programa muy grande en dos archivos. Entonces, se debería decirle al preprocesador que añada un archivo al otro antes de compilar. El compilador los verá como un único archivo, aunque en realidad son dos.

La forma de comunicarse con el preprocesador es utilizando # como primer carácter de una línea, seguido por el comando adecuado. Por ejemplo, con “include” se añade otro archivo en ese punto:

#include "horario"

Se agrega un archivo llamado "horario" al programa, en el lugar donde se encuentre esta línea. De esta forma, si el archivo que contiene el #include es:

Las clases de C++ serán # include "horario"

a partir de hoy

y el archivo "horario" contiene:

todos los lunes, miércoles y viernes de doce a dos

(8)

Las clases de C++ serán

todos los lunes, miércoles y viernes de doce a dos a partir de hoy

La Función Main()

Todos los programas en C++ cuentan con un punto de entrada al programa denominado main(). Esto quiere decir, que al compilar y ejecutar un programa, éste buscará el conjunto de órdenes que vienen dadas por las sentencias de la función main(), y por lo tanto, esta última será imprescindible en cualquier programa escrito en C++.

En este punto del caso de ejemplo, la función main no tomará parámetros ni devolverá nada, por lo que su cabecera será void main(void).

Identificadores

Son los nombres que se eligen para las variables, constantes, funciones, clases y similares. El primer caracter debe ser una letra o un subrayado. El resto del nombre puede contener dígitos. Los identificadores que comienzan con dos subrayados se reservan para uso interno del compilador C++.

Entrada/salida

Las operaciones de entrada y salida no forman parte del conjunto de sentencias de C++, sino que pertenecen al conjunto de funciones y clases de la biblioteca estándar de C++. Éstas se incluyen en los archivos de cabecera iostream.h

porque siempre que se quieran utilizar se debe introducir la línea de código

#include <iostream.h>

Los objetos de flujo que vienen predefinidos serán:

Ø cin, que toma caracteres de la entrada estándar (teclado);

Ø cout, que pone caracteres en la salida estándar (pantalla);

(9)

Estos objetos se utilizan por medio de los operadores << y >>. El operador << se denomina operador de inserción; y apunta al objeto donde tiene que enviar la información. Por lo tanto la sintaxis de cout será:

cout<<variable1<<variable2<<...<<variablen;

Se debe recordar que las cadenas de texto son variables y se ponen entre comillas dobles. El símbolo >> se denomina operador de extracción, el cual lee la información del flujo cin, que se encuentra a la izquierda del operador, y las almacena en las variables indicadas a la derecha.

A continuación se presenta su sintaxis:

cin>>variable1>>...>>variablen;

Ahora se presenta un ejemplo de código utilizando ambos objetos:

#include <iostream.h> ...

main ()

{

int i;

cout<<"Introduce un número"; cin>>i;

... } ...

Con esto se mostraría en la pantalla la frase "Introduce un número" y luego almacenaría el valor introducido por teclado en la variable i.

Cout

C++, como en el caso de C, no tiene operaciones de entrada/salida como parte del lenguaje en sí, sino que define la librería stream para añadir estas funciones. La salida por pantalla se hace a través de cout, pero es ligeramente diferente a la función printf(), ya que no se tiene que indicar el tipo de la variable que se desea imprimir. De esta forma se escribe en C++ el tradicional ejemplo "Hello, world":

#include <iostream.h>

main() {

(10)

}

El comando cout muestra en la pantalla cualquier tipo de dato estándar que existe en C++, ya sea un carácter, un número, o movimientos especiales del cursor,como \n en el ejemplo anterior. A continuación se presenta otro programa:

#include <iostream.h> main()

{ int a; float b;

a = 4; b = 52.2;

cout <<"Vamos a imprimir un número entero:"; cout << a;

cout <<'\n';

cout <<"Y ahora uno real:"; cout << b;

cout <<'\n'; }

La salida de este programa es:

Se va a imprimir un número entero: 4 Y ahora uno real: 52.2

Cin

El comando cin es complementario de cout. Lee lo que se introduce desde el teclado, y en este sentido es también una caja negra, pues no sabemos cómo lo hace. La sintaxis es similar a la de cout:

#include <iostream.h> main()

{

int numero;

cout << "Introduce un número:"; cin >> numero;

(11)

UNIDAD 2: ELEMENTOS BÁSICOS DEL LENGUAJE

Constantes

Las constantes se declaran, como en C, igual que una variable normal, pero añadiendo la palabra const delante. Por ejemplo, para declarar una constante con valor 14:

const int numero = 14;

Dichas constantes no pueden ser modificadas a lo largo del programa. Debido a esto deben ser definidas al mismo tiempo que declaradas. Sin embargo, el compilador no dará ningún mensaje de error si la constante no es inicializada.

Ejemplo

#include <iostream.h>

void print(const int datos) {

cout << "El valor del indice es "<<datos<< '\n'; }

void main(void) {

const int inicio = 3; const int final = 9;

for (int indice = inicio ; indice < final ; indice ++) print(indice);

}

El comando const también se utiliza en la función print para indicar que el parámetro datos es una constante dentro de la función. Cualquier intento de asignar un nuevo valor a esta variable dará un error de compilación. La salida del programa anterior es:

(12)

El valor del índice es 6 El valor del índice es 7 El valor del índice es 8

Estos debe ser utilizados en lugar de los #define del preprocesador. Pueden utilizarse para especificar las dimensiones de una matriz:

const int dimen = 20; double vector[dimen];

A continuación se presenta ejemplo de su uso:

# include <iostream.h>

main () {

const int MaxDim=3;

int vector[MaxDim][MaxDim] = {{1,2,3},{4,5,6},{7,8,9}};

for (int i=0; i < MaxDim; i++){ for (int j=0; j < MaxDim; j++){ cout << vector [i][j] << '\n'; }

}

}

El índice exterior de la matriz es el que se ejecuta más rápido, al contrario de lo que sucedía en FORTRAN. La salida del programa es:

1 2 3 4 5 6 7 8 9

Variables

(13)

int no_matricula = 99123; char si = 's';

double pi;

Por su parte, las variables pueden tener distinto tipo de almacenamiento, dependiendo de las partes del código en el que se vayan a utilizar. Existen cuatro tipos de almacenamiento.

Static

Es una variable estática que existe desde que el programa comienza su ejecución y dura hasta que el programa termina. Esta característica permite retener el valor de una variable incluso aunque la ejecución del programa salga fuera del ámbito en el que ha sido declarada. Se declara anteponiendo la palabra reservada static a la declaración de la variable. Considérese el siguiente fragmento de código:

ejemplo( ) {

static int x=0; x++;

... }

En este caso en particular, static modifica la declaración de la variable x, que por defecto es local a la función, ya que si bien el ámbito de x sigue siendo la función, y como no se puede utilizar fuera de ésta, no se destruye una vez ejecutada la función, sino que sigue en memoria conservando su valor, y si la función es llamada por segunda vez, el valor de la variable x ya no será 0 sino 1.

Las variables globales que se declaran en cada archivo son por defecto static.

Extern

(14)

Auto

Esta es la declaración de una variable local, que se utiliza por defecto en las funciones.

Register

Cuando a la declaración de una variable le antecede la palabra reservada

register se le indica al compilador que la variable se almacenará en uno de los registros del hardware del microprocesador. La palabra clave register, en una sugerencia, no un mandato, al compilador. Una variable register debe ser local a una función.

Una aplicación típica es el uso de una variable register como variable de control de un bucle; de este modo se reduce el tiempo en el la CPU requiere para buscar el valor de la variable en la memoria. Por ejemplo:

register int i;

for (i=1; i<10000; i++) {...}

Ámbitos

Esta es la zona donde la declaración tiene efecto. Dentro de este ámbito no puede existir otra declaración con el mismo identificador.

Nota: el ámbito corresponde con una zona del código fuente encerrada entre llaves { }, una lista de parámetros en una función o plantilla, o el espacio de una unidad de compilación no incluido en cualquier otro ámbito.

Clases de ámbito

Existen siete categorías de ámbitos en n C++: de sentencia; de bloque (o local); de función; de prototipo de función; de archivo; de clase y de espacio de nombres. El ámbito depende de como y donde es declarado el identificador.

(15)

declaración" y “enclosing scope”. Por ejemplo, las siguientes declaraciones son correctas en C++ pero no en C.

void main(void) { int i = 100;

cout << "Es el numero " << i << endl; char ch = 'A';

cout << "Es la letra " << ch << endl; }

Para ser compilado como C las variables tendrían que haberse declarado antes que cualquier ejecución de función. Por ejemplo:

void main(void) { int i = 100;

char ch = 'A';

 cout << "Es el numero " << i << endl; cout << "Es la letra " << ch << endl; }

Ámbito de Sentencia

C++ cuenta con soporte para las declaraciones en expresiones condicionales; se pueden declarar variables dentro de las expresiones de las sentencias for, if,

while y switch; por lo tanto, el ámbito de las variables es el de la sentencia. En el caso de if el ámbito incluye también el bloque else. Ejemplo:

...

for (j = 0; j>10; j++) { // comienza el ámbito de j int x = 0; // comienza el ámbito de x

...

}           // termina el ámbito de j x (ver nota)

 ...

Nota: el C++Builder incluye la opción de la opción -Vd de compilación que permite modificar el ámbito de las variables declaradas dentro de las sentencias for.

Ámbito de Bloque

El ámbito de un identificador con ámbito local, o de bloque, empieza en el punto de declaración y termina al final del bloque que contiene la declaración, el denominado bloque contenedor. Ejemplo:

(16)

char c = 'c'; // comienza el ámbito de c int x = 0; // comienza el ámbito de x ...

} // termina el ámbito de c x ...

El ámbito de los parámetros declarados en la definición de una función es el del bloque que define dicha función. Ejemplo:

...

int func (int x, int y) { // comienza el ámbito de x y ...

int y = 12; // Error!! declaración duplicada return (x + y);

} // termina el ámbito de x y ...

Ámbito de Función

Los únicos identificadores que cuentan con ámbito de función son las etiquetas de

goto, debido a esto sus nombres deben ser únicos en la función. Su ámbito es el de la función que las contiene, de forma que pueden ser utilizados por las sentencias goto en cualquier punto de la función en que se han declarado.

Los identificadores de función tienen enlazado externo, lo que significa que pertenecen al ámbito global, que sería el mismo para todas. Es decir, pueden ser referenciadas desde cualquier punto del archivo, incluso desde otras funciones, incluyendo main(), o desde ellas mismas, recursión; pero el bloque de código que engloba el cuerpo de cada función, incluyendo sus variables, es un espacio oculto, no puede ser accedido directamente desde su exterior. Por esta razón no es posible, por ejemplo, realizar un salto goto a una etiqueta en otra función. La única manera de acceder a una función es mediante una llamada a la misma siguiendo el formato específico definido en su prototipo. El único valor que se puede manejar directamente es el que devuelve y aún así, no es el valor original, sino una copia modelada de este.

Los nombres que se encuentran en la lista de parámetros formales de una función pertenecen al ámbito del bloque más externo de la función, que s el que define el cuerpo de la misma.

Una consecuencia del hecho que todas las funciones compartan el mismo ámbito global, es que no puedan declararse funciones dentro de funciones.

(17)

clases funcionan como auténticos subespacios de nombres, por lo que también pueden declararse funciones dentro de ellas, las llamadas funciones-miembro, que no pertenecen al espacio global.

Ámbito de Prototipo

Los nombres declarados en la lista de parámetros de un prototipo de función, que no sean parte de una declaración, tienen ámbito reducido al prototipo. En realidad estos nombres sólo se utilizan para el posible anuncio por el compilador de errores o advertencias sobre el prototipo que se declara.

Ámbito de Archivo

Los identificadores con ámbito de archivo, son llamados también globales o

externos. Son declarados fuera de cualquier bloque, clase o función. Su ámbito abarca desde el punto de declaración hasta el final del archivo, por esta razón se suelen declarar al principio del mismo, justo después de las directivas # de preproceso.

Ámbito de Clase

Se denomina clase a una colección de elementos, o miembros, junto con las operaciones que se realizan con éstos. El término ámbito de clase se aplica a los nombres de los miembros de una clase particular. Las clases y sus miembros tienen reglas de acceso y de ámbito muy especiales.

El nombre N de un miembro de una clase C tiene ámbito “local a C”, y puede ser utilizado solo en las siguientes situaciones:

• En funciones miembro (métodos) de C.

• En expresiones tales como c.N, donde c es un objeto de C, el selector directo

de miembro.

• En expresiones tales como cptr->N, donde cptr es un puntero a una

instancia de C, selector indirecto de miembro.

• En expresiones tales como C::N o D::N, donde D es una clase derivada de C. • En referencias anticipadas de miembros dentro de la clase.

Se debe recordar que los nombres de funciones declaradas amigas, o friend

de C, no son miembros de C; sus nombres simplemente tienen ámbito de la clase

C.

Ámbito de espacio de nombres

El espacio de nombre es el ámbito en el que un identificador debe ser único. Debido a esto, C utiliza cuatro clases distintas de identificadores:

• Nombres de etiquetas goto, las cuales deben ser únicas dentro de la función

(18)

• Nombres estructuras, uniones y enumeraciones., estos deben ser únicos

dentro del bloque en que se han definido. Las etiquetas definidas fuera de cualquier función deben ser únicas, ya que son globales al archivo.

• Nombres de miembros de estructuras y uniones; los cuales deben ser únicos

dentro de la estructura o unión en que se han definido. No existe restricción en el tipo de miembros del mismo nombre en diferentes estructuras.

• Variables, funciones, typedef y enumeradores; que deben ser únicos dentro

del ámbito en que han sido definidos. Los identificadores declarados externos deben ser únicos entre las variables declaradas externas.

En el caso de C++ se cuenta una palabra clave: namespace, que es en realidad un recurso para manejar los identificadores. Este permite dividir el espacio total de nombres en regiones distintas e independientes respecto a los identificadores.

Los objetos definidos en el subespacio raíz tienen ámbito de todo el programa, de la aplicación, siempre que se hayan definido como extern en el resto de los módulos. Al mismo tiempo, los compiladores utilizan una serie de variables y tipos globales a la aplicación cuyos nombres predefinidos son incluidos de forma automática en cualquier programa C++ para usos varios, como fechas, horas, etc.

Duración

Duración estática

Los objetos con duración estática se encuentran ubicados en la memoria durante todo el tiempo de ejecución del programa. Las funciones, las variables con ámbito de archivo, y las variables con especificadores de clase de almacenamiento static ó extern, tienen una duración estática. Los objetos con duración estática son inicializados a cero, en ausencia de un valor inicial explícito.

Duración local

Los objetos de duración local, conocidos también como objetos automáticos, son creados en la pila de la memoria RAM ó en los registros del microprocesador. La memoria asignada a estos objetos se libera cuando finaliza la ejecución del bloque donde fueron creados. Los objetos de duración local deben utilizarse siempre en un ámbito local ó de función. Al utilizar el especificador de clase de almacenamiento register, se implica el uso del especificador de clase de almacenamiento auto.

Duración dinámica

(19)

Enlace

Durante la creación de un programa ejecutable, primero se lleva a cabo la compilación de diversas unidades de traslación, las cuales se componen del código fuente junto con los archivos incluidos. Luego, el archivo objeto (.obj), se enlaza con las librerías preexistentes para obtener el archivo ejecutable (.exe).

El enlace es el proceso que permite a cada instancia de un identificador ser asociada correctamente con un objeto o función particular. Todos los identificadores tienen uno de los siguientes atributos de enlace, íntimamente relacionados con su ámbito:

• enlace externo,

• enlace interno,

• sin enlace.

Estos atributos son determinados por medio del emplazamiento y los formatos de las declaraciones, junto con el uso, ya sea implícito ó explícito, de los especificadores de clase de almacenamiento static ó extern. Cada instancia de un identificador con enlace externo representa al mismo objeto ó función a través de todo el conjunto de archivos y librerías que componen el programa.

Cada instancia de un identificador con enlace interno representa al mismo objeto ó función sólo dentro de un archivo. Los identificadores sin enlace representan entidades únicas. A continuación se presentan las reglas:

Reglas para los enlaces interno y externo:

1. Cualquier identificador de objeto ó archivo que tenga ámbito de archivo tendrá un enlace interno si su declaración contiene el especificador static. Para C++, si el mismo identificador aparece con ambos tipos de enlace en el mismo archivo, el identificador tendrá enlace externo.

2. Si la declaración de un identificador de un objeto ó función contiene el especificador extern, el identificador tendrá el enlace correspondiente a una declaración con ámbito de archivo.

3. Si una función se declara sin un especificador de clase de almacenamiento, su enlace se determina como si se hubiera utilizado el especificador extern. 4. Si un identificador de objeto con ámbito de archivo se declara sin

(20)

Los identificadores presentados a continuación no tienen atributo de enlace:

a).- Cualquier identificador declarado para ser algo diferente de un objeto ó función (por ejemplo, un identificador typedef ).

b).- Los parámetros de las funciones.

c).- Los identificadores de ámbito de bloque para objetos declarados sin el especificador extern.

Operadores

Los operadores son símbolos que indican los cálculos que se deben realizar sobre, una, dos o más variables o constantes dentro de un expresión.

La tabla presentada abajo recoge los operadores comunes a C y C++

Operador Descripción del operador

( ) Llamada a una función [ ] Subíndice de un array

. Punto. Acceso a un miembro de una estructura o unión

-> Flecha. Apunta a un miembro de una estructura o unión

! Operación lógica NOT - Menos, unitario

-- Decrementar en uno ++ Incrementar en uno

& Obtener una dirección de memoria * Obtiene la indirección (contenido de) (tipo) Conversión forzada de tipos (moldes)

sizeof Obtiene el tamaño de una variable o constante * Multiplicación

/ División

(21)

Operador Descripción del operador

+ Suma - Resta < Menor que > Mayor que

<= Menor o igual que >= Mayor o igual que == Igual

!= No igual

&& Operación lógica AND | | Operación lógica OR = Valor de asignación

Tabla 2.1: Operadores comunes para C y C++

En la tabla presentada a continuación se muestran los operadores exclusivos de C++

Operador Descripción del operador

*= expresión 1 = expresión 1 * expresión 2 ( a *= 3 es a = a*3)

/= expresión 1 = expresión 1 / expresión 2 %= expresión 1 = expresión 1 % expresión 2 -= expresión 1 = expresión 1 - expresión 2 += expresión 1 = expresión 1 + expresión 2

Tabla 2.2: Operadores exclusivos para C++

(22)

UNIDAD 3: INSTRUCCIONES DE CONTROL DE FLUJO

Instrucciones de secuencia

Un programa puede contener sólo una secuencia de instrucciones, éstas instrucciones de una secuencia se ejecutan una después de la otra:

comienzo

secuencia de instrucciones fin;

Ejemplo: encontrar la solución de una ecuación de primer grado a x + b = 0. Los datos a y b se leen de teclado, se calcula el valor de x resultante, y se muestra el resultado en la pantalla.

/* declaraciones */ float a, b, x;

/*instrucciones*/ comienzo leer (a); leer (b);

x := - b / a;

mostrar ("El valor de x es: ", x); fin;

Instrucciones de selección

Dentro de este grupo se encuentran aquellas instrucciones que sirven para que la ejecución del programa tome una de varias opciones existentes en una ramificación.

En C++ se tienen las siguientes instrucciones de selección:

1. La instrucción if – else

Al utilizar esta instrucción se puede elegir entre dos opciones de ejecución, y su sintaxis es :

if( condición ) [else]

[bloque_2] donde:

(23)

else es opcional, y en caso de no existir, bloque_2 tampoco existirá.

Cuando se ejecuta esta estructura, primero se evalúa la condición. En caso de que esta evaluación de como resultado un valor de verdad verdadero, se ejecutarán las instrucciones que forman el bloque_1; en caso contrario, si el valor en realidad es falso, se ejecutarán las instrucciones del bloque_2.

Esta estructura puede anidarse para seleccionar entre un grupo que tenga más de dos opciones, tomando la siguiente forma:

if(condición_1) bloque_1;

else if(condición_2) bloque_2;

else if(condición_3) bloque_3;

... else

bloque_N;

A continuación se muestran algunos ejemplos de aplicación de la instrucción if-else

Ejemplo 3.1

#include <iostream.h>

void main() { long ncontrol;

cout << "NUMERO DE CONTROL: "; cin>> ncontrol;

if(ncontrol<=0) cout << "NUMERO INVALIDO..."; else cout << "CORRECTO !!" << "\n\n\n"; }

Ejemplo 3.2.- Uso de if-else

(24)

int calif; clrscr();

cout << "CALIFICACION: "; cin>> calif; if(calif > 100)

cout << "ERROR: CALIFICACION DEMASIADO ALTA ..."; else if(calif < 0) cout << "ERROR: CALIFICACION DEMASIADO BAJA ..."; else if( (calif>= 70) && (calif <=100)) cout << "CALIFICACION APROBATORIA."; else cout << "CALIFICACION REPROBATORIA."; }

2. Instrucción switch

Esta instrucción resulta útil cuando se tiene que elegir entre más de dos opciones, como es el caso de manejo de menús. Es preferible utilizar esta instrucción, al uso de anidamientos de varios if-else.

Su sintaxis es:

switch(expresión_entera) {

case Const_1 : Bloque_1 ; break ; case Const_2 : Bloque_2 ; break ; ...

... ...

case Const_N : Bloque_N ; break ;

default : Bloque_X }

En la entrada de una instrucción switch, se evalúa primero la expresión_entera. En caso de que el resultado coincida con Const_1, se ejecuta el Bloque_1 y break que interrumpe la ejecución del instrucción; en caso de que coincida con el valor de Const_2, se ejecuta el Bloque_2, y se interrumpe la ejecución de la instrucción. Lo mismo pasa en caso de que el resultado coincida con cualquiera de los otros valores constantes. En caso de existir default:, y si el resultado no coincide con ninguno de los valores constantes, se ejecutan las instrucciones contenidas en el Bloque_X.

(25)

Ejemplo 3.3

#include <iostream.h> #include <conio.h> void main() { char opcion; clrscr(); gotoxy(30,5); cout << "MENU DE OPCIONES"; gotoxy(30,8); cout << "1.- CREACION"; gotoxy(30,10); cout << "2.- MODIFICACION"; gotoxy(30,12); cout << "3.- ELIMINACION"; gotoxy(30,14); cout << "0.- SALIDA"; gotoxy(30,18);

cout << "SU OPCION ? "; opcion="getche();" cout << "\n\n"; switch(opcion) {

case '1': clrscr(); cout << "RUTINA DE CREACION\n"; break; case '2': clrscr(); cout << "RUTINA DE MODIFICACION\n"; break; case '3': clrscr(); cout << "RUTINA DE ELIMINACION\n"; break;

case '0': clrscr(); cout << "SALIDA AL SISTEMA OPERATIVO\n";break; default:clrscr(); cout << "OPCION INVALIDA...\n";

} }

Instrucciones de iteración

(26)

Además de estas dos instrucciones también se cuenta con el ciclo for que sirve principalmente para evaluar condiciones numéricas, donde una variable se va a estar incrementando continuamente, cabe destacar que un ciclo se utiliza en lugar de otro más que nada por comodidad, ya que todo ciclo for puede ser convertido en while, todo ciclo while puede ser adaptado a un do-while y todo do-while portado a for.

Instrucción WHILE

Mientras (expresión) hacer esto...

Ejemplo 3.4

while( i<10 ) {

i++; }

Instrucción DO-WHILE Hacer

esto...

Mientras(expresión) Ejemplo:

do { i++;

}while(i<10); Instrucción FOR

Para i = 0 mientras i<10 aumentar i++ hacer esto...

Ejemplo:

for( i=0 ; i<10 ; i++ ) {

(27)

A continuación se presenta un ejemplo de la iteración FOREACH, algunos programadores pueden no estar familiarizados con esta nueva forma, la cual sirve para recorrer arreglos; la instrucción se ejecutará en todos y cada uno de los elementos del arreglo.

Instrucción FOREACH

por cada perro en miArregloDePerros[] hacer esto...

Ejemplo 3.5

foreach( perro miPerro in miArregloDePerros ) {

Console.WriteLine(miPerro.nombre); }

Ejemplo 2:

foreach( int miEntero in miArregloDeEnteros ) {

(28)

UNIDAD 4: Funciones

Funciones

En C++ se debe distinguir entre lo que es un declaración y una definición, de una función. En la declaración de una función sólo se incluye la cabecera o prototipo de la misma, y siempre tiene que aparecer antes de ser utilizada. En la definición de la función aparecen las sentencias que ejecuta dicha función, y puede aparecer en cualquier parte del programa. Se puede realizar la declaración y la definición al mismo tiempo, pero como se verá más adelante, no es una declaración de una función si presenta la sintaxis siguiente:

tipo_devuelto nombre_función (parámetros);

La mejor manera de visualizar esto es por medio de un ejemplo:

float integral(float , float);

una práctica habitual.

Esta sería la declaración o prototipo de una función llamada integral que devolverá un tipo real, y que a su vez tomará como parámetros dos reales, en la declaración no es necesario indicar como se llaman.

Al no desear que una función devuelva un valor o que tenga parámetros, se utiliza el tipo void. Es decir, una función que no devolviera nada y no tomara parámetros se declararía de la siguiente forma:

void ejemplo (void);

Ahora bien, en la mayoría de los compiladores se podría prescindir de esto, al colocar en la declaración simplemente:

ejemplo ( );

(29)

El próximo paso será definir la función, para hacerlo se repite el prototipo de la función, que en este caso si se necesita introducir el nombre de los parámetros, y luego se coloca el cuerpo de la misma entre llaves; donde se puede utilizar todo lo visto hasta ahora, declaración de variables, bucles, sentencias de control de la lógica, etc. La forma en que se utilizan las variables o el llamado a otras funciones sigue la misma lógica que en turbo pascal. Se pueden utilizar las variables globales o variables locales definidas en la función, y se podrá llamar a todas las funciones que hayan sido declaradas con antelación. Nota: no es necesario que hayan sido definidas.

El valor que devuelve la función se indica en el cuerpo de la misma con la palabra reservada return valor;

Ahora, se presenta un programa ejemplo donde todo esto se aplica:

#include<iostream.h>

int factorial(int); //declaramos l función factorial.

main( ) //función principal del programa. {

cout<<"Introduce un número del que quieras hallar el factorial"; int i;

cin>>i;

cout << "El factorial de " << i << " es: "<< factorial(i); }

int factorial(int a) //Ahora definimos la función factorial. {

int aux;

for (int i=1; i<=a; i++) {

aux = aux*i; }

(30)

Sobrecarga de funciones

Ésta es una característica de C++ que hace que los programas sean más legibles. Consiste en que un conjunto de funciones que realizan la misma tarea o una tarea muy similar tengan el mismo nombre, de tal manera que en función de los parámetros que se le pasen al compilador sepa en todo momento cuál de éstas debe utilizar.

Por ejemplo, se pone de ejemplo una función llamada suma, esta deberá ser diferente si se quieren sumar complejos, de otra en la que se quieren sumar enteros, en C++ se pueden utilizar dos funciones llamadas suma de la forma presentada continuación:

typedef complejo int[2]

int suma (int a, int b) {

return (a+b) }

complejo suma (complejo a, complejo b) {

complejo aux;

aux[1]=a[1] + b[1]; aux[2]=a[2]+b[2]; return aux;

}

Se definió un tipo de dato que será complejo formado por un vector de dos elementos, por lo tanto, si a realizarse el llamado a la función, se le pasan como parámetros dos enteros ejecutará el cuerpo de la función que se definió primero, y en caso de que se le pasen dos variables de tipo complejo, ejecutará la segunda.

(31)

Paso de parámetros

Parámetros por valor y por referencia

En el caso de C++, el paso por valor significa que al compilar la función y el código que llama a la función, ésta recibe una copia de los valores de los parámetros que se le pasan como argumentos. Las variables reales no se pasan a la función, sólo copias de su valor.

En el caso de que una función deba modificar el valor de la variable pasada como parámetro, y que dicha modificación retorne a la función que hizo el llamado, se debe pasar el parámetro por referencia. En este método, el compilador no pasa una copia del valor del argumento; en cambio, pasa una referencia, que le indica a la función dónde existe la variable en la memoria.

La referencia que recibe una función es la dirección de la variable. Es decir, pasar un argumento por referencia consiste sólo en indicarle al compilador que pase la dirección del argumento.

Ejemplo:

void demo(int &valor) {

valor=5;

cout<<valor<<endl; }

void main() {int n=10;

cout<<n<<endl; demo(n);

cout<<n<<endl; }

La salida de este programa será: 10 5 5

Una limitante de este método de paso por referencia es que se pueden pasar sólo variables a la función. No se pueden utilizar constantes ni expresiones en la línea de llamada a la misma.

Funciones Recursivas

(32)

especialmente para que sean recursivas, de otro modo podrían conducir a bucles infinitos, o a que el programa termine de forma incorrecta.

El uso de C++ permite la recursividad. Al realizar un llamado a una función, se crea un nuevo juego de variables locales, de este modo, si la función hace una llamada a sí misma, se guardan sus variables y parámetros en la pila y la nueva instancia de la función trabajará con su propia copia de las variables locales; cuando esta segunda instancia de la función retorna, recupera las variables y los parámetros de la pila y continua la ejecución en el punto en que había sido llamada.

Por ejemplo:

Una función recursiva para calcular el factorial de un número entero. El factorial se simboliza como n!, se lee como "n factorial", y la definición es:

n! = n * (n-1) * (n-2) * ... * 1

No se puede calcular el factorial de números negativos, y el factorial de cero es 1, por lo tanto, una función bien hecha para cálculo de factoriales debería incluir un control para esos casos:

/* Función recursiva para cálculo de factoriales */ int factorial(int n) {

if(n < 0) return 0;

else if(n > 1) return n*factorial(n-1); /* Recursividad */ return 1; /* Condición de terminación, n == 1 */

}

Ahora se revisa paso a paso el proceso, lo que pasa cuando se ejecuta esta función, por ejemplo: factorial(4):

1a Instancia n=4

n > 1

salida ← 4 * factorial(3) (Guarda el valor de n = 4) 2a Instancia

n > 1

salida ← 3*factorial(2) (Guarda el valor de n = 3)

3a Instancia n > 1

salida ← 2*factorial(1) (Guarda el valor de n = 2)

4a Instancia

(33)

3a Instancia

(recupera n=2 de la pila) retorna 1*2=2

2a instancia

(recupera n=3 de la pila) retorna 2*3=6

1a instancia

(recupera n=4 de la pila) retorna 6*4=24 Valor de retorno → 24

La función factorial es un buen ejemplo para demostrar cómo se hace una función recursiva, sin embargo, la recursividad no es un buen modo de resolver esta función, que sería más sencilla y rápida con un bucle "for". La recursividad consume muchos recursos de memoria y tiempo de ejecución, y se debe aplicar a funciones que realmente le saquen provecho.

Por ejemplo: visualizar las permutaciones de n elementos.

Las permutaciones de un conjunto son las diferentes maneras de colocar sus elementos, utilizándolos todos, y sin repetir ninguno. Por ejemplo para A, B, C, tenemos:

ABC, ACB, BAC, BCA, CAB, CBA.

#include <iostream> using namespace std;

/* Prototipo de función */

void Permutaciones(char *, int l=0);

int main(int argc, char *argv[]) { char palabra[] = "ABCDE";

Permutaciones(palabra);

cin.get(); return 0; }

void Permutaciones(char * cad, int l) {

(34)

int i, j; /* variables para bucles */ int n = strlen(cad);

for(i = 0; i < n-l; i++) {

if(n-l > 2) Permutaciones(cad, l+1); else cout << cad << ", ";

/* Intercambio de posiciones */ c = cad[l];

cad[l] = cad[l+i+1]; cad[l+i+1] = c; if(l+i == n-1) {

for(j = l; j < n; j++) cad[j] = cad[j+1]; cad[n] = 0;

} } }

El algoritmo funciona de la forma presentada a continuación:

En un principio, todos los elementos de la lista pueden cambiar de posición, es decir, pueden permutar su posición con otros. No se fija ningún elemento de la lista, l = 0: Permutaciones(cad, 0)

0 1 2 3 4

A B C D /0

Se hace un llamado recursivamente a la función, pero dejando fijo el primer elemento, el 0: Permutacion(cad,1)

0 1 2 3 4

A B C D /0

Ahora, se llama recursivamente a la función, pero fijando el segundo elemento, el 1: Permutacion(cad,2)

0 1 2 3 4

(35)

En este punto sólo quedan dos elementos permutables, así que se imprime ésta permutación, y se intercambian los elementos: l y l+i+1, es decir el 2 y el 3.

0 1 2 3 4

A B D C /0

Se imprime ésta permutación, y se intercambian los elementos l y l+i+1, es decir el 2 y el 4.

0 1 2 3 4

A B /0 C D

En el caso especial de que l+i+1 sea justo el número de elementos, se deben mover los elementos hacia la izquierda desde la posición l+1 a la posición l:

0 1 2 3 4

A B C D /0

Ahora, se abandona el último nivel de recursión, y se retoma en el valor de l=1 e i = 0.

0 1 2 3 4

A B C D /0

Se permutan los elementos: l y l+i+1, es decir el 1 y el 2.

0 1 2 3 4

A C B D /0

En la iteración del bucle i = 1 presentada a continuación, se hace un llamado de forma recursiva con l = 2: Permutaciones(cad,2)

0 1 2 3 4

A C B D /0

(36)

0 1 2 3 4

A C D B /0

(37)

UNIDAD 5: ARREGLOS

Declaración de arreglos

En C++ un se llama un arreglo al conjunto de datos que se almacenan en la memoria de manera contigua con el mismo nombre. Si se desea diferenciar los elementos de un arreglo se utilizan los índices detrás del nombre del mismo, y encerrados entre corchetes []. El quinto (5º) elemento de un arreglo es representado por el índice [4], ya que los índices comienzan en 0. Esto quiere decir que un arreglo de 10 elementos tendría los índices del 0 al 9 [0...9]

Ejemplos: Ejemplo 5.1

int arregloEntero[10]; //Declaracion de un arreglo de 10 elementos, sin inicializar. arregloEntero[5] = 45; //Asignacion de 45 al elemento 6 del arreglo.

double arrPuntoFlotante[3] = {1.1,1.2,1.3};

//Declaracion y asignacion en un arreglo de 3 elementos double.

int a[4], b[5]; //Declaracion de dos arreglos enteros, de 4 y 5 elementos.

A continuación se presenta ejemplo donde se trata un arreglo de 10 elementos con ciclos for:

Ejemplo 5.2

//Uso de arreglos en C++ #include <iostream> using std::cout;

using std::cin; using std::endl; int main()

{

int arregloEntero[10] = {0}; //Arreglo entero de 10 elementos inicializados todos en 0.

cout << "Arreglo recien declarado: " << endl;

for (int i = 0 ; i < 10 ; i++) //Notar el menor estricto (<) para ir desde 0 hasta 9 cout << "arregloEntero["<<i<<"]="<<arregloEntero[i] << endl;

cout << "Introduzca 10 nuevos valores " << endl;

(38)

{

cout << " Introduzca nuevo valor para arregloEntero["<<i<<"]" << endl; cin >> arregloEntero[i];

}

cout << "Luego de los valores introducidos, el arreglo quedo asi: " << endl; for (int i = 0 ; i < 10 ; i++) //Notar el menor estricto (<) para ir desde 0 hasta 9 cout << "arregloEntero["<<i<<"]="<<arregloEntero[i] << endl;

return 0; }

Manejo de Arreglos

Cadena de Caracteres

Antes estudiar el tema de los "arrays", también conocidos como arreglos, tablas o matrices, se estudiará caso especial. Se trata de las cadenas de caracteres o "strings", en inglés.

En C, se considera una cadena al conjunto de caracteres, o valores de tipo "char", terminados con el caracter nulo, es decir el valor numérico 0. Internamente, se almacenan en posiciones consecutivas de memoria. Este tipo de estructuras recibe un tratamiento especial, y es de gran utilidad y de uso continuo. A continuación se presenta la manera en que se define una cadena:

char <identificador> [<longitud máxima>];

Nota: en este caso los corchetes no indican un valor opcional, sino que son realmente corchetes, por eso se encuentran en negritas.

Al declarar una cadena se debe tener en cuenta que se tiene que reservar una posición para almacenar el carácter nulo, de modo que si se quiere almacenar la cadena "HOLA", se debe declarar la cadena como:

char Saludo[5];

Con cuatro caracteres para "HOLA", y uno extra para el carácter '\000'.

(39)

Asignación de Valores Iniciales a Cadenas

En una cadena se pueden almacenar información, tal como nombres de personas, mensajes de error, números de teléfono, etc. La asignación directa sólo ese permite permitida cuando al hacerla junto con la declaración. Por ejemplo:

char Saludo[5]; Saludo = "HOLA"

Esto producirá un error en el compilador, ya que una cadena definida de este modo se considera una constante, como se verá en el capítulo de "arrays" o arreglos. La forma correcta de asignar una cadena es:

Ejemplo 5.3

char Saludo[5]; Saludo[0] = 'H'; Saludo[1] = 'O'; Saludo[2] = 'L'; Saludo[3] = 'A'; Saludo[4] = '\000';

O bien:

char Saludo[5] = "HOLA";

Funciones para el Manejo de Cadenas

El lenguaje C++ no cuenta con algún tipo de dato específico para el manejo de cadenas de caracteres, pero sí cuenta con un grupo de funciones que se han acumulado durante la evolución del Lenguaje C. Si se desea leer una cadena de caracteres desde el teclado existe la función gets(), y para desplegar una cadena en pantalla se utiliza la función puts(). Los prototipos de ambas funciones se encuentran declarados en el archivo STDIO.H.

Por ejemplo, el ejemplo 5.4 muestra un programa que sirve para leer y desplegar las cadenas de caracteres utilizando las funciones gets() y puts().

Ejemplo 5.4

(40)

void main() {

char nombre[31]; // Declara un arreglo de 31 caracteres char saludo1[] = "¡¡ HOLA,"; //Constante de caracteres

char saludo2[] = " !!";

clrscr(); gotoxy(20,10);

puts("¿ Cuál es tu nombre ? "); //Despliega cadena de car. gotoxy(45,10);

gets(nombre); // Lee cadena de caracteres strupr(nombre); // Convierte a mayúsculas gotoxy(20,12);

puts(saludo1); gotoxy(30,12); puts(nombre);

gotoxy(30+strlen(nombre),12); // Longitud de la cadena puts(saludo2);

}

Por otro lado, a parte de las funciones gets() y puts(), existe otro grupo de funciones para el manejo de cadenas de caracteres, como son strlen() y strupr() que se utilizan en el programa del ejemplo 5.4. Los prototipos de estas funciones se encuentran declarados en el archivo STRING.H En la tabla 5.1 se describen brevemente algunas de las funciones para el manejo de cadenas de caracteres en el C++ de Borland, cuyos prototipos se encuentran declarados en el archivo STRING.H .

Función Descripción

stpcpy Sirve para copiar una cadena de caracteres en otra. Se detiene cuando encuentra el terminador nulo.

strcat Se añade una cadena de caracteres a otra. strchr Busca un caracter dado en una cadena. strcmp Compara dos cadenas.

strcmpi Es un macro que compara dos cadenas sin distinguir entre mayúsculas y minúsculas.

(41)

Función Descripción

strcspn Busca los segmentos que no contienen un subconjunto de un conjunto especificado de caracteres.

strdup Copia una cadena a una nueva localidad

_strerror Genera un mensaje de error definido por el programador.

strerror Retorna el apuntador al mensaje asociado con el valor del error.

stricmp Compara dos cadenas sin diferenciar entre mayúsculas y minúsculas

strlen Determina la longitud de una cadena.

strlwr Convierte las mayúsculas de una cadena en minúsculas. strncat Añade el contenido de una cadena al final de otra.

strncmp Compara parte de una cadena con parte de otra.

strncmpi Compara parte de una cadena con parte de otra, sin distinguir entre mayúsculas.

strncpy Copia un número de bytes dados, desde una cadena hacia otra.

strnicmp Compara parte de una cadena con parte de otra, sin distinguir entre mayúsculas y minúsculas.

strnset Hace que un grupo de elementos de una cadena tengan un valor dado.

strpbrk Busca la primera aparición de cualquier caracter de un conjunto dado en una cadena,.

strrchr Busca la última aparición de un caracter en una cadena. strrev Invierte el orden de los caracteres de una cadena.

strset Hace que los elementos de una cadena tengan un valor dado. strspn Busca en una cadena el primer segmento que es un

subconjunto de un conjunto de caracteres dado.

strstr Busca en una cadena la aparición de una subcadena dada. _strtime Convierte la hora actual a una cadena.

(42)

Función Descripción

strtol Convierte una cadena a un valor long.

strtoul Convierte una cadena a un valor unsigned long.

strupr Convierte las minúsculas de una cadena a mayúsculas.

Tabla 5.1.- Funciones para el manejo de cadenas de caracteres.

Control de acceso a los elementos de un arreglo

En el caso particular de C++, el acceso a los elementos de un arreglo tiene que ser controlado por el programador, ya que el lenguaje no restringe la posibilidad de accesar a posiciones de memoria que se encuentran más abajo de la última posición reservada para el arreglo. Lo mismo sucede cuando se manejan cadenas, donde el programador tiene la responsabilidad de controlar el acceso a los caracteres de la cadena, tomando como límite el terminador nulo. En el ejemplo 5.5 se presenta un ejemplo de acceso a los 5 bytes colocados abajo del terminador nulo de una cadena dada por el usuario.

Ejemplo 5.5

Ubicación de los elementos de una cadena en la memoria RAM

#include // Para gets() y puts() #include // Para clrscr() y gotoxy() #include // Para strlen() void main() {

char nombre[31]; clrscr();

gotoxy(10,1);

puts("¿ Cuál es tu nombre ? "); gotoxy(35,1);

gets(nombre); clrscr(); gotoxy (10,1);

puts("ELEMENTO CARACTER DIRECCION");

(43)

UNIDAD 6: APUNTADORES

Declaración de apuntadores

Uno de los problemas más comunes cuando se programa en C, y en algunos casos en C++, es la comprensión de apuntadores y arreglos. En C (C++) ambos se encuentran altamente relacionados; con algunas pequeñas pero esenciales diferencias. Se declara un apuntador colocando un asterisco entre el tipo de datos y el nombre de la variable o función:

char *strp; /* strp es un "apuntador a char" */

Se accesa el contenido de un apuntador de referenciándolo por medio de un asterisco:

*strp = 'a'; /* Un caracter simple */

Igual que sucede con otros lenguajes, se debe proveer algún espacio para el valor al cuál el apuntador apunta. Un apuntador a caracteres puede ser utilizado para apuntar a una secuencia de caracteres: el string. Los strings en C++ se encuentran terminados por un caracter especial NUL (0 o como char '/0'). De esta forma, se pueden tener strings de cualquier longitud, estos strings se encierran en comillas dobles:

strp = "hello";

En este caso, el compilador agrega el caracter de terminación NUL de forma automática. De esta forma, strp apunta a una secuencia de 6 caracteres. El primer caracter es 'h', el segundo es 'e' y así sucesivamente. Se puede acceder estos caracteres por medio de un índice en strp:

strp[0] /* h */ strp[1] /* e */ strp[2] /* l */ strp[3] /* l */ strp[4] /* o */ strp[5] /* \0 */

(44)

constituye una de las poderosas características de C++. De esta forma, se tienen las siguientes ecuaciones:

*strp == *(strp + 0) == strp[0] *(strp + 1) == strp[1] *(strp + 2) == strp[2] ...

Nótese que estas ecuaciones son verdaderas para cualquier tipo de datos. La suma

no se encuentra orientada a bytes, sino orientada al tamaño del tipo correspondiente del apuntador.

Este apuntador strp puede ser ubicado en otras localidades. Su destino puede variar, en contraste con eso, los arreglos son apuntadores fijos, ya que apuntan a un área predefinida de memoria, la cuál se especifica por corchetes:

char str[6];

Str puede ser interpretado como un apuntador constante apuntando a una área de 6 caracteres. No se puede utilizar esto de la siguiente forma:

str = "hallo"; /* ERROR */

Ya que esto significaría cambiar el apuntador para que apuntara hacia 'h'. Se debe copiar el string al área de memoria provista. Por lo tanto, se utiliza una función llamada strcpy() la cuál es parte de la biblioteca estándar de C.

strcpy(str, "hallo"); /* Ok */

Nota: sin embargo, srt se puede utilizar en cualquier caso donde un apuntador a carácter se use, ya que es un apuntador.

Variables Automáticas y Apuntadores

Las variables automáticas se crean en tiempo de compilación y se destruyen al terminar la ejecución del módulo donde fueron declaradas.

Aunque no es estrictamente necesario, se pueden manejar las variables automáticas por medio de apuntadores, como se muestra en el siguiente ejemplo:

#include <iostream.h> #include <conio.h>

void main() {

(45)

int *apunt ; // Se declara el apuntador apunt, que // tará a objetos de tipo int.

automatica = 100 ; // Se asigna el valor 100 a la variable // automatica.

apunt = &automatica ; // Se asigna a apunt la dirección de // automatica ó apunt apunta a

// automatica. clrscr();

cout << "VALOR=" << automatica << " \n"; // 100 *apunt="200" ; // Se asigna el valor 200 al objeto apunta- // do por apunt. cout << "VALOR=" << automatica << " \n"; // 200 getch(); }

Las instrucciones de ejemplo se traducen en la siguiente secuencia, donde los apuntadores se representan con una flecha, para simular que "apuntan hacia" ó "señalan" un objeto, y los objetos apuntados se representan por un cuadro para simular un recipiente.

Apuntadores y Constantes

Se tienen cuatro casos donde se relacionan la palabra reservada const y los punteros:

1. apuntador constante ; valor constante 2. apuntador no constante ; valor constante 3. apuntador constante ; valor no constante 4. apuntador no constante ; valor no constante

1)apuntador constante; valor constante:

const int * const ptrA = &valor;

Se debe inicializar el puntero, de lo contrario el compilador emitirá un error. Operaciones no permitidas:

-Ninguna asignación es permitida: *ptrA=5; //error

(46)

2) apuntador no constante; valor constante

const int *ptrA;//Se puede dejar sin inicializar

Operaciones no permitidas

-Asignación del apuntador desreferenciado:

*ptrA = 5;//error

Operaciones permitidas:

ptrA = &valor;//bien

3) apuntador constante; valor no constante

int * const ptrA = &valor;//debe inicializar

Operaciones no permitidas:

-Asignación del puntero:

ptrA = ptrB; //error

Operaciones permitidas:

*ptrA = 5;//bien

4) apuntador no constante; valor no constante

int *ptrA; //se puede dejar sin inicializar

Operaciones permitidas:

(47)

ptrA = &valor;//bien ptrA +=5;//bien Código de ejemplo:

//Apuntador y const

#include <iostream> using std::cout;

using std::endl;

int main()

{

int valorA = 1;

int valorB = 2;

const int * const ptrValorA = &valorA; //puntero constante a valor constante //*ptrValorA=5;//error:Apunta a un dato constante

//ptrValorA=&valorB;//error:El puntero es constante

const int * ptrValorB = &valorA; //puntero no constante a un valor constante //*ptrValorB=5; //error:No se puede ya que su desrefencia es constante

ptrValorB = &valorB; //se puede porque el puntero es no constante

//int * const ptrValorC;//error:Un puntero constante se debe inicializar

const int * ptrValorD;//Bien, ptrValorD puede tomar valores, pero no desreferenciado.

int * const ptrValorE = &valorB;//puntero constante apuntando a un valor no constante

(*ptrValorE)++; //La desreferencia se puede modificar //ptrValorE = ptrValorA;//error el puntero es constante

return 0;

}

Variables dinámicas y apuntadores

En el montículo se manejan las variables dinámicas, y deben su nombre al hecho de que pueden ser creadas y destruidas durante el tiempo de ejecución de un módulo.

(48)

funciones tales como malloc() y free() para la asignación y liberación de memoria del montículo. Además de estas funciones, el C++ cuenta con dos operadores interconstruidos:

new el cual lleva acabo una labor parecida a la de la función malloc(), asignando un bloque de memoria.

delete que libera un bloque de memoria asignada en tiempo de ejecución, de manera semejante a como lo hace free().

En el ejemplo presentado abajo se muestra la aplicación de los operadores new y delete.

#include <iostream.h>

void main() {

int *apent; // Declara un apuntador a entero

apent = new int ; // Reserva un bloque de memoria dinámica // de 2 bytes para manejarlo por medio

// de apent.

*apent = 10 ; // Asigna el valor 10 al objeto apuntado // por apent.

cout << *apent ; // Despliega el contenido del objeto // apuntado por apent. delete apent ; // Libera el espacio de memoria manejado // por apent. }

En el programa antes presentado se supone que la reservación será exitosa porque existe espacio suficiente en el montículo. Pero, ¿quién asegura que el espacio requerido por new está disponible?. Para controlar esta situación y evitar un mensaje de error por parte del sistema en tiempo de ejecución, en el siguiente código se propone una nueva versión del programa.

#include <iostream.h>

#include <stdlib.h> // Para exit().

void main() {

int *apent; // Declara un apuntador a entero

(49)

cout << "NO hay espacio suficiente\n";

exit(1); // Finaliza la ejecución del programa. }

*apent="10" ; // Asigna el valor 10 al objeto apuntado // por apent.

cout << *apent ; // Despliega el contenido del objeto // apuntado por apent. delete apent ; // Libera el espacio de memoria manejado // por apent. }

Los operadores new y delete pertenecen al Lenguaje C++ , por lo que no se requiere incluir ningún archivo de cabecera para utilizarlos. Para crear un arreglo de 25 elementos de tipo double, se puede escribir el montículo de la siguiente forma:

double* dap ;

dap = new double[25];

ó su forma equivalente: double* dap = new double[25];

En el ejemplo se declara a dap como un apuntador a variables dinámicas de tipo doble; al tiempo que se le asigna el valor retornado por new. El valor retornado por new es la dirección del inicio de un bloque de memoria del tamaño requerido para almacenar 25 elementos de tipo double. En caso de que el montículo no disponga del espacio requerido, new retorna el valor NULL ( nulo ). Aunque, cuando se requiere memoria a través de new se debe indicar el tamaño exacto del bloque requerido, el tamaño del bloque se puede dar en tiempo de ejecución como se muestra en el código presentado a continuación:

#include <iostream.h> #include <conio.h> #include <stdlib.h>

void main() {

unsigned int n; clrscr();

gotoxy(20,1);

cout << "NUMERO DE ELEMENTOS DEL ARREGLO: "; cin>> n; float* apf;

(50)

cout << "NO HAY ESPACIO SUFICIENTE EN EL MONTICULO\N"; exit(1); } for(int x="0" ; x < n ; x++) { gotoxy(20,x+3); *(apf+x)="x+100.25;" cout << *(apf+x) << "\n"; } delete apf; getch(); }

Apuntadores y Arreglos

Además, existe una relación estrecha entre los punteros y los arreglos. En C++, un nombre de un arreglo es un índice a la dirección de comienzo del arreglo. En esencia, el nombre de un arreglo es un puntero al arreglo. Se puede considerar lo siguiente:

int a[10], x; int *ap;

ap = &a[0]; /* ap apunta a la direccion de a[0] */

x = *ap; /* A x se le asigna el contenido de ap (a[0] en este caso) */

*(ap + 1) = 100; /* Se asigna al segundo elemento de 'a' el valor 100 usando ap*/

Tal y como se puede observar en el ejemplo, la sentencia a[t] es idéntica a ap+t. Se debe tener cuidado ya que C++ no hace una revisión de los límites del arreglo, por lo que se puede ir fácilmente más allá del arreglo en memoria y sobrescribir otras cosas. Sin embargo, C++ es mucho más sutil en su relación entre arreglos y apuntadores. Por ejemplo sólo se puede teclear:

ap = a; en vez de ap = &a[0]; y también *(a + i) en vez de a[i], esto es, &a[i] es equivalente con a+i.

Como se ve en el ejemplo, el direccionamiento de apuntadores se puede expresar como:

a[i] que es equivalente a *(ap + i)

Sin embargo, los apuntadores y los arreglos son diferentes:

• Un apuntador es una variable. Se puede hacer ap = a y ap++. • Un arreglo NO ES una variable. Hacer a = ap y a++ ES ILEGAL.

(51)

Por lo tanto:

strlen(s) es equivalente a strlen(&s[0])

Esta es la razón por la cual se declara la función como:

int strlen(char s[]); y una declaración equivalente es int strlen(char *s);

Ya que char s[] es igual que char *s.

La función strlen() es una función de la biblioteca estándar que regresa la longitud de una cadena. A continuación se muestra la versión de esta función que podría escribirse:

int strlen(char *s) {

char *p = s;

while ( *p != '\0' ) p++;

return p - s; }

Ahora, se muestra una función para copiar una cadena en otra. Al igual que en el ejercicio anterior existe en la biblioteca estándar una función que hace lo mismo.

void strcpy(char *s, char *t) {

while ( (*s++ = *t++) != '\0' ); }

En los dos últimos ejemplos presentados, se emplean apuntadores y asignación por valor. Nota: se emplea el uso del caracter nulo con la sentencia while para encontrar el fin de la cadena.

Arreglos de apuntadores

Éstos son una representación de datos que manejan las líneas de texto de longitud variable de una forma eficiente y conveniente. ¿Cómo se puede llevar a cabo lo anterior?

Referencias

Documento similar

En referencia a la información disponible sobre el Sistema de Garantía Interno de la Calidad, la página web recoge un enlace a un PDF descargable con información breve sobre

Esquema del fundamento de la cromatografía de afinidad por iones metálicos inmovilizados (IMAC). Enlace peptídico entre dos aminoácidos. Estructura resonante del

Trazabilidad de una Orden: es un enlace que permite la generación de un archivo Excel donde vemos la gestión de cada una de las órdenes de compra generadas partiendo de una orden

Para cargar o adjuntar imágenes o fotografías, será necesario que el Enlace FAIS sitúe el cursor en recuadro con la leyenda Seleccionar archivo ( ), y después deberá dar

DIARIO DEL ALTO ARAGÓN Http://www.diariodelaltoaragon.es/ Sí. DIARIO DEL BIERZO

Cada época, a través de la poesía, avanza sus propias reivindicaciones, y el lector de este libro, ante todo, descubrirá cuán fecunda es hoy en día la lectura de José

 Cualquier colonia seleccionada que presente signos clínicos se considera sospechosa de padecer loque americana.  Criterios sintom Criterios sintomááááticos (sospecha):

En este proyecto se utiliza para la implementaci´ on de la base de datos tanto para los usuarios del sistema como para los dispositivos asociados a dichos usuarios.. Adem´ as,