Está en la página 1de 28

2.

- Constantes, variables, operadores


En esta unidad se trata el manejo de los objetos donde se almacenan los datos, así como las
operaciones que pueden realizarse con los valores almacenados.
En este contexto, llamaremos objeto a una región identificable de memoria que puede
almacenar un valor fijo o variable. No debe confundirse el significado que se dá aquí al
vocablo objeto con el que tiene en la Programación Orientada a Objetos.

2.1.- Constantes
Podemos definir a una constante como una localidad de memoria (objeto), la cual tiene un
nombre y un tipo de dato asociados, además de un valor que permanece fijo durante el
tiempo de ejecución.

En la unidad 1 se estudió la manera de definir constantes por medio de la directiva #define .


Aquí definiremos a las constantes utilizando el modificador const, usando del formato
mostrado a continuación:

const <tipo> <identificador> = <valor>

Ejemplo: const int iva = 10 ;

Los valores para las constantes, permitidos por Borland C++, se clasifican en cuatro clases:

I).- Enteros, que pueden escribirse en formato:

I.a).- Decimal, con valores de 0 a 4,294'967,295.

Ejemplo: const int smin = 15300 ;

I.b).- Octal, que deben empezar con un 0 (cero).

Ejemplo: const int lim = 030 ; (decimal 27)

I.b).- Hexadecimal, que empiezan con 0X ó 0x (cero x)

Ejemplo: const int mem = 0xFFFF ;

II).- Caracteres, compuestos de uno o más caracteres encerrados entre comillas


sencillas (apóstrofes).

Ejemplo: const char inicial = 'A' ;

Para manejar los caracteres de control se utilizan las secuencias de escape que
empiezan con el carácter de diagonal hacia atrás (\) seguida de un carácter. Si el
carácter que sigue a la diagonal es alguno de los caracteres mostrados en la tabla 2.1,
se obtendrá el efecto explicado en ella; pero si es cualquiera de los demás caracteres del
Código ASCII, se visualizará el carácter tal cual.

Secuencia Caracter Efecto


\a BEL Sonido de la bocina
\b BS Retroceso
\f FF Alimentación de forma
\n LF Nueva línea
\r CR Retorno de carro
\t HT Tabulación horizontal
\v VT Tabulación vertical
\\ \ Diagonal hacia atrás
\' ' Comilla sencilla
\" " Comilla doble
\? ? Interrogación
\O O Cualquiera O=Número octal de hasta tres dígitos
\x H Cualquiera H=Número hexadecimal

Tabla 2.1.- Secuencias de escape.

Borland C++ permite el uso de dos caracteres juntos como valores de caracteres.

Por ejemplo: 'AB' , '\n\t' , '\007\007'

III).- Punto flotante, cualquiera con punto decimal ó con formato exponencial.

Ejemplos: 3.135 , 0. , .0 , 34e3 , -3.4 , .075E12

IV).- Enumeración, son identificadores definidos en declaraciones de tipo enum.

Ejemplo: enum frutas { pera, uva, manzana, fresa } ;

donde: pera, uva, manzana y fresa son constantes de enumeración y tienen los
siguientes valores:

pera = 0
uva = 1
manzana = 2
fresa = 3

Pueden asignarse valores explícitos como en:

enum frutas {pera, uva , manzana = 10 , fresa = uva+5} ;


y entonces :

pera = 0
uva = 1
manzana = 10
fresa = 6

2.2.- Variables

Una variable es un espacio de memoria que tiene un nombre y un tipo de dato asociados,
además de un valor que puede cambiarse durante el tiempo de ejecución.

En el Lenguaje C++ puede declararse una variable en cualquier lugar del programa, con la
única restricción de que la declaración preceda a la primera instrucción donde se utiliza tal
variable.

El formato para la declaración de una variable o grupo de variables es el siguiente:

[, var2, ... ] ;

Ejemplos:
int algo, otro ;

int suma = 0 , inicial = 1 ;

float desc , acum = 0 ;

char prim = 'A' , seg ;

2.3.- Ambito

El ámbito es una zona de programa donde puede utilizarse un identificador para accesar su
objeto . El ámbito va a depender, básicamente, del lugar del programa en que aparece la
declaración.

Bajo este enfoque, se tienen las siguientes clases de ámbito:

Ambito de BLOQUE.

En el Lenguaje C++, se generaliza el concepto de ámbito local, a través de la introducción


del concepto de bloque.

Un bloque está formado por las instrucciones delimitadas por un par de llaves { }.
Es posible anidar una serie de bloques, y pueden declararse variables dentro de cada
bloque. Si, en diferentes bloques, existen variables con el mismo identificador, todas serán
consideradas como variables diferentes. Así que puede tenerse:

..............
..............
int x=5; // x del bloque 1
{
int x=10; // x del bloque 2
{
int x=20; // x del bloque 3
{
int x=30; // x del bloque 4
printf("x=%d",x); // x=30
}
printf("%d",x); // x=20
}
printf("%d",x); // x=10
}
printf("%d",x); // x=5
.............
.............

Debe tenerse cuidado al utilizar identificadores iguales para diferentes variables. Lo más
recomendable es no utilizar los mismos identificadores y no intentar manejar variables en
un ámbito diferente al que pertenecen.

Ambito de FUNCION.

En esta clase sólo se encuentran los identificadores que se utilizan como etiquetas cuando se
tienen instrucciones goto.
El identificador de una etiqueta debe ser único dentro de una función, y no puede utilizarse
la instrucción goto para saltar de una función a otra.

Ambito de PROTOTIPO DE FUNCION.

El ámbito de los identificadores utilizados dentro de los paréntesis en la declaración de


prototipo de una función, finaliza con la declaración del prototipo.

Ambito de ARCHIVO.

Las variables declaradas fuera de cualquier bloque en un archivo son consideradas como
variables globales, y el ámbito de sus identificadores abarca todos los bloques contenidos en
el archivo en cuestión; por lo que: una variable con éste ámbito puede ser utilizada en las
funciones del archivo que estén definidas después del punto donde se declaró dicha
variable.
2.4.- Duración

La duración define el periodo de tiempo durante el cual los identificadores declarados


tienen objetos asignados en memoria. Las variables, por ejemplo, tienen memoria asignada
durante el tiempo de ejecución.

Existen tres clases de duración:

2.4.1.- Duración estática.


2.4.2.- Duración local.
2.4.3.- Duración dinámica.

2.4.1.- Duración estática

Los objetos con duración estática están localizados en 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 duración estática.

Los objetos con duración estática son inicializados a cero , en ausencia de un valor inicial
explícito.

2.4.2.- 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
éstos objetos es liberada 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.

Cuando se utiliza el especificador de clase de almacenamiento register, se implica el uso del


especificador de clase de almacenamiento auto.

2.4.3.- Duración dinámica

Los objetos de duración dinámica se crean y se destruyen, por invocaciones a funciones


específicas, durante el tiempo de ejecución de un programa. El almacenamiento de estos
objetos se dá en el área de reserva de la memoria RAM llamado montículo.
2.5.- Enlace

En 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. Posteriormente, el archivo objeto (.obj) , se enlaza con librerías
preexistentes para obtener el archivo ejecutable (.exe).

El enlace es el proceso que permite a cada instancia de un identificador asociarse


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 se determinan a través del emplazamiento y los formatos de las


declaraciones, junto con el uso ( 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
solamente dentro de un archivo.

Los identificadores sin enlace representan entidades únicas.

A continuación se presentan las:

REGLAS PARA LOS ENLACES INTERNO Y EXTERNO:

1. Cualquier identificador de objeto ó archivo que tenga ámbito de archivo tendrá 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


está determinado como si se hubiera utilizado el especificador extern.
4. Si un identificador de objeto con ámbito de archivo se declara sin especificador de clase
de almacenamiento, el identificador tendrá enlace externo.

Los siguientes identificadores no tienen atributo de enlace:

a).- Cualquier identificador declarado para ser algo diferente de un objeto ó función (
p.ej. un identificador typedef ).
b).- Los parámetros de las funciones.
c).- Los identificadores de ámbito de bloque para objetos declarados sin el el
especificador extern.

Para aclarar lo relacionado con el especificador extern, revisemos los listados 2.1 y 2.2.

// EXTERN1.CPP

int edad; char nombre[31]; // DEFINICION de variables globales

Listado 2.1.- EXTERN1.CPP, que define las variables globales.

// EXTERN2.CPP
#include <iostream.h>
// DECLARACION de variables globales que:
extern int edad; // se encuentran en otro archivo
extern char nombre[];
void main()
{
cout << "\nCUAL ES TU NOMBRE ? " ; cin>> nombre ;
cout << "\nCUANTOS AÑOS TIENES, " << nombre << " ?" ; cin>> edad ; cout <<
"\n\n" ; cout << " TE FALTAN " << 100-edad ; cout << " PARA LLEGAR A LOS CIEN,
" << nombre << "\n" ; }

Listado 2.1.- EXTERN2.CPP, que declara y utiliza variables globales definidas en otro
archivo.

En el archivo EXTERN1.CPP se definen las variables globales edad y nombre.


Posteriormente se compila este archivo para obtener el archivo EXTERN1.OBJ.

En el archivo EXTERN2.CPP se declaran las variable edad y nombre utilizando el


modificador extern.

Finalmente, se crea un proyecto al que puede llamarse EXTERN.PRJ, en el cual se incluyen


los archivos EXTERN1.OBJ y EXTERN2.CPP; se compila el proyecto y se obtiene el
archivo ejecutable EXTERN.EXE
2.6.- Operadores

Los objetos que se manejan en C++ van a tener un tipo de dato asociado, el cual determina
la cantidad de espacio de almacenamiento que se le asigna a cada uno de los objetos de tal
tipo, así como el conjunto de operaciones que podrán realizarse con los valores
almacenados. Las operaciones serán representadas a través de identificadores específicos
llamados operadores.

En esta sección estudiaremos los operadores, agrupándolos en:

2.6.1.- Operadores aritméticos.


2.6.2.- Operadores Relacionales.
2.6.3.- Operadores lógicos.
2.6.4.- Operadores entre bits.
2.6.5.- Operadores de asignación.

2.6.1.- Operadores ariméticos

Los operadores aritméticos se aplican sobre objetos con valores numéricos, como se
muestra en la tabla 2.2 .

Sean: X = 20 , Y = 30 , A = 100.0 , B = 200.0

Operador Operación Ejemplo Resultado


+ Adición Z=X+Y Z=50
- Sustracción Z=Y-X Z=10
* Multiplicación Z=X*Y Z=600
/ División Z=Y/X Z=1.5
% Módulo Z=Y%X Z=10
++ Incremento X++ X=21
- Decremento X- X=19

Tabla 2.2.- Operadores aritméticos.

2.6.2.- Operadores relacionales


Los operadores relacionales se usan para comparar los valores que resultan de reducir
expresiones. Los resultados coincidirán con los valores de verdad:

FALSO igual a CERO VERDADERO diferente de CERO Los operadores relacionales en


C++ son :

> Mayor que

= Mayor ó igual que

<= Menor ó igual que="=" Igual que !="Diferente" que ( No igual que )

2.6.3.- Operadores lógicos

Los operadores lógicos se aplican sobre los enunciados que resultan de las operaciones
relacionales, y el resultado siempre será un valor de verdad. Los operadores lógicos son:

&& Y ( Conjunción )

|| O ( Disyunción )

! NO ( Negación )

2.6.4.- Operadores entre bits

Con estos operadores se puede realizar la comprobación, colocación ó desplazamiento de


los bits actuales de una variable de los tipos int y char.

Los operadores entre bits son:

& Y ( Conjunción )

| O ( Disyunción )

^ O ( Disyunción Exclusiva ó XOR )

~ ( Complemento a uno ó NOT )

>> ( Desplazamiento a la DERECHA )

<< ( Desplazamiento a la IZQUIERDA )

A continuación se presentan ejemplos de algunas operaciones entre bits:


Ejemplo 2.6.1:
Supongamos que se quiere cambiar el bit de paridad ( el de la extrema izquierda ) de uno a
cero. Para esto puede utilizarse la operación de conjunción entre bits.

Si tenemos la declaración:

char indice = 81 ;

y, suponiendo que se quiere cambiar el bit de paridad de 1 a 0 , la instrucción en C++ se


escribiría :

indice & 127 ;

La operación realizada a mano es :

11010001 <------ indice operador> & 01111111 <---


--- 127 en binario 01010001 <------ resultado

En este caso se utilizó el número 127 porque es el único número entero que puede escribirse
en un octeto ( byte ) y que tiene los siete primeros bits con valor 1 .

Ejemplo 2.6.2
Se requiere que en el número 112 tengan valor 1 los bits que correspondan con los del
número 7 que tengan valor 1. En este caso, la operación a utilizar es la disyunción entre
bits, quedando las instrucciones en la siguiente forma:

char masc ;

masc = 112 | 7 ;

La operación manual tomaría la siguiente forma:

01110000 <------ 112 en binario operador> | 00000111


<------ 7 en binario 01110111 <------ resultado

Ejemplo 2.6.3
La disyunción exclusiva sirve para poner a uno los bits del primer operando cuyos
correspondientes bits en el segundo operando sean distintos, como se ve a continuación:

x = 125 ^ 120 ;
char x ;

Manualmente se tendría:

01111101 <------- 125 en binario operador> 01111000


<------- 120 en binario 00000101 <------- resultado

Los operadores de desplazamiento mueven todos los bits, de una variable entera ó de
carácter, hacia la izquierda ó hacia la derecha.
A medida que los bits son desplazados hacia un extremo, los lugares desocupados del
estremo contrario van siendo ocupados por ceros. Los unos que salen por los extremos no se
pueden recuperar(no hay rotación) .

2.6.5.- Operadores de asignación

El estudio de los operadores de asignación requiere del conocimiento de los conceptos de


valor izquierdo ( lvalue ) y de valor derecho ( rvalue ).

Un valor izquierdo es una expresión que designa un objeto. Las expresiones utilizadas como
valor izquierdo corresponden a objetos cuyos valores pueden cambiar durante la ejecución
de un programa. Generalmente, en la expresión correspondiente a un valor izquierdo
aparece el identificador de una variable. Cuando el valor izquierdo se refiere a la dirección
de una variable, la expresión puede constar de una combinación de varios identificadores.

Un valor derecho es una expresión formada por cualquier combinación de objetos y


operadores que pueda ser reducida a un valor.

Los operadores de asignación sirven para asignar un valor derecho a un valor izquierdo, y
están formados por la combinación del operador de asignación simple = con otro operador,
como se muestra en la tabla 2.3 .

Operador Significado Ejemplo: Equivale a:


= Asignación múltiple .
+= Suma asigna X+=Y X=X+Y
-= Resta asigna X-=Y X=X-Y
*= Multiplicación asigna X*=Y X=X*Y
/= Divide asigna X/=Y X=X/Y
%= Residuo asigna X%=Y X=X%Y
<<= Dezplaz. izq. asigna X<<Y X=X<<Y
>>= Dezplaz. der. asigna X>>Y X=X>>Y
&= Conj. e/bits asigna X&=Y X=X&Y
^= Disy. exclu. asigna X^=Y X=X^Y
|= Disyunción asigna X|=Y X=X|Y

Tabla 2.3.- Operadores de asignación.


Los operadores estudiados hasta aquí no son todos los que existen en el C++, sino los que a mi
juicio son indispensables para iniciar el estudio del lenguaje. Si, posteriormente, es necesario
utilizar algún operador no tratado en esta unidad, en ese momento se estudiarán las características
particulares de tal operador.

3.- Instrucciones de control

En esta unidad estudiaremos las instrucciones que sirven para controlar el flujo de
ejecución de un programa en C++ . De acuerdo a las características de cada una, las
clasificaremos en grupos de estructuras básicas de:

3.1.- Instrucciones de secuencia.


3.2.- Instrucciones de selección.
3.3.- Instrucciones de iteración.

Las estructuras básicas deben cumplir con la condición básica de la Programación


Estructurada de: sólo una entrada, sólo una salida.

3.1.- Secuencia

A este grupo pertenecen las instrucciones que están formadas por una o varias expresiones
simples colocadas una a continuación de la otra. La sintaxis para las instrucciones
estructurados en secuencia es la siguiente:

instruccion_1 ;
instruccion_2 ;
.........
instruccion_N ;

Su diagrama de flujo se presenta en la figura 3.1.


3.2.-Selección

A este grupo pertenecen 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 instruccións de selección:

3.2.1.- La instrucción if - else


3.2.2.- La instrucción switch

3.2.1.- La instrucción if - else

Esta instrucción permite elegir entre dos opciones de ejecución, y su sintaxis es :

if( condición )

[else]
[bloque_2]

donde:
bloque_1 y bloque_2 pueden estar formados por uno ó
más instrucciones.
else es opcional, y en caso de no existir, bloque_2
tampoco existirá.

Al ejecutarse esta estructura, primero se evalúa la condición. En caso de que, de esta


evaluación, resulte un valor de verdad verdadero, se ejecutarán las instrucciones que
forman el bloque_1; en caso contrario (si el valor de verdad es falso), se ejecutarán las
instrucciones del bloque_2.

El diagrama de flujo para la estructura if-else se presenta en la figura 3.2.

Esta estructura puede anidarse para elgir entre un grupo de 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

#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"; }

Listado 3.1.- Uso de if-else

#include <iostream.h>
#include <conio.h>
void main()
{
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."; }

Listado 3.2.- Uso de if-else-if

3.2.2.- La instrucción switch

Esta instrucción es útil cuando se tiene que elegir entre más de dos opciones, como es el caso
de manejo de menús. Esta instrucción es preferible que el 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 ;
}

A la entrada en una instrucción switch, primero se evalúa la expresión_entera. En caso de


que el resultado coincida con Const_1, se ejecuta el Bloque_1 y break interrumpe la
ejecución del instrucción; en caso de que coincida con el valor de Const_2, se ejecuta el
Bloque_2 , 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.

Esta estructura puede representarse con el diagrama mostrado en la figura 3.3.

Es recomendable el uso de la instrucción switch en el caso del manejo de un menú de


opciones como se observa en el listado 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";
}
}

Listado 3.3.- Uso de switch

3.3.- Iteración

Las estructuras de iteración se utilizan en la ejecución repetida de un bloque de


instruccións.

En el Lenguaje C++, se tienen:

3.3.1.- La instrucción while


3.3.2.- La instrucción do - while
3.3.3.- La instrucción for

Para el manejo de las estructuras de iteración.

3.3.1.- La instrucción while

Con esta instrucción se maneja una estructura en la que, de entrada, se evalúa una
condición. En caso de que el resultado de tal evaluación sea un valor diferente de cero , se
ejecuta un bloque de instrucciones, en el cual debe existir una instrucción que modifique la
condición, ya que de lo contrario ejecutará un ciclo infinito ( loop ). Si el resultado de la
evaluación es un valor igual a cero , el bloque de instrucciones no se ejecuta y finaliza la
ejecución de la instrucción.

La sintaxis de la instrucción while es la siguiente:

while(condición)
bloque;

En la figura 3.4 se presenta el diagrama de la instrucción while.


El listado 3.4 muestra el uso de la instrucción while.

#include <iostream.h>
#include <conio.h>
#define FALSO 0

void main()
{
int valor=1;
clrscr();
while(valor!=FALSO)
{
cout << "\nTeclee un valor entero ( 0="salir" ) : "; cin>> valor;
}
}

Listado 3.4.- Uso de la instrucción while

3.3.2.- La instrucción do - while


La instrucción do-while tiene un comportamiento similar a while, sólo que en este caso
primero se ejecuta el bloque de instrucciones y después se evalúa la condición. Con esto se
asegura que el bloque se ejecutará al menos una vez.

Esta es la sintaxis de la instrucción do-while :

do
bloque;
while(condición);

La instrucción do-while puede representarse con el diagrama mostrado en la figura 3.5.

Como una aplicación de la instrucción do-while , se presenta en el listado 3.5 una variante
al problema resuelto en el listado 3.3 .

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

void main()
{
char opcion;
do{ // inicia ciclo 1
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 ? "; window(42,18,42,18); do{ // inicia ciclo 2 clrscr();
opcion="getche();" }while((opcion < '0')||(opcion>'3'));// fin ciclo 2
window(1,1,80,25);
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; } cout << "\n\nPULSE CUALQUIER TECLA PARA
CONTINUAR.."; getch(); }while(opcion!="0" ); // fin ciclo 1 }

Listado 3.5.- Aplicación de la instrucción do-while

3.3.3.- La instrucción for

Entre las instrucciones de iteración, for es la más versátil, ya que, entre otras
características, permite la declaración de variables dentro de su estructura.

La sintaxis de la instrucción for es la siguiente:

for(inicialización ; condición ; control)


bloque;

donde:
inicialización es un bloque de instrucciones que puede
incluir la declaración de las variables
involucradas y la asignación de valores
iniciales.

condición es una instrucción que puede evaluarse de


tal forma que se obtenga como resultado un
valor de verdad ( falso ó verdadero ).
Mientras, se cumpla la condición, se ejecu-
tará el bloque de instrucciones.

control es un bloque de instrucciones separadas por


comas y que controlan la variación de los
valores de las variables utilizadas.

Los bloques de inicialización, condición y control no son obligatorios, pero sí lo son los tres
punto y coma que los separan; de tal suerte que la forma mínima de una instrucción for
quedaría así:
for(;;) // ciclo infinito
; // no realiza tarea alguna

El diagrama para la instrucción for se muestra en la figura 3.6.

El listado 3.6 muestra el uso de la instrucción for.

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

int main()
{
clrscr();

for(int x=1 ; x <= 25 ; x++) { for(int y="1" ; y <="80" ; y++) {


gotoxy(y,x); if((x="=25)" && (y="=80))" getch(); cout << '.'; } } return 0; }

Listado 3.6.- Utilización de la instrucción for

4.- Diseño de funciones


Las funciones son el módulo básico para la construcción de programas en C++.

Además de la función main(), con frecuencia es necesario utilizar funciones adicionales que
pueden ser accesadas a través del enlace de librerías precompiladas ó a través de su
definición en el archivo de código fuente ó en archivos de cabecera.

En esta unidad estudiaremos los procedimientos necesarios para el manejo de las funciones
definidas en el código fuente.

En principio, debemos distinguir entre: declarar, definir e invocar una función, ya que la
confusión de éstos términos es causa de frecuentes problemas.

Desde los orígenes del Lenguaje C ha existido la distinción entre definir y declarar una
función. Cuando se define una función se le está reservando espacio de almacenamiento en
memoria; en cambio cuando se declara solo se está avisando que más adelante se encuentra
una función con ciertas características, pero no se le reserva espacio en memoria.

4.1.- Definición de funciones

La definición de una función implica reservarle espacio de almacenamiento en memoria, de


acuerdo al tipo de dato a retornar.

Es en la definición donde se incluye el cuerpo de la función.

El formato general de la definición de una función es el siguiente:

tipo identificador( argumentos )


{
bloque;
}

donde:

tipo es el tipo de dato que va a retornar la función, e identificador es el nombre de la


función.

La existencia de argumentos dependerá de la tarea que va a realizar la función, pero el par


de paréntesis es requisito indispensable.

En el ejemplo 4.1. se presenta una aplicación de la definición, la declaración y la invocacion


de funciones.
4.2.- Declaración de funciones

Cuando se declara una función, se está avisando al compilador que más adelante
encontrará la definicion de tal función, y que por el momento, tome nota de las
características de ella, como son: el tipo de dato que retorna, su nombre y los tipos de
argumentos que va a recibir. Con esto, no habrá ningún problema en invocar a la función
en un bloque de programa ubicado antes del lugar donde se encuentra escrita su definición.
En el ejemplo 4.1, las líneas:

void saludo();
float calcula(float);

representan la declaración de las funciones ó la declaración de los prototipos de las


funciones saludo() y calcula().

En la declaración de la función saludo() se especifica que no va a retornar valor alguno, y


que no recibirá argumentos.

En la declaración de la función calcula() se especifica que va a retornar un valor de tipo


float, y que va recibir un argumento de tipo float.

Es importante observar que la declaración de una función es parecida a la línea de


encabezado de su definición , sólo que en el caso de la declaración se escribe un punto y
coma al final.

También cabe hacer notar que en la declaración no se requiere escribir identificadores para
los argumentos, como se observa en el ejemplo 4.1, sino que basta con incluir los tipos de
datos de los argumentos. Se pueden incluir identificadores de argumentos, sólo que el
ámbito de tales identificadores estará restringido a la declaración de la función
correspondiente.

Por lo tanto, la declaración:

float calcula(float); podría haberse escrito así :

float calcula(float arg);

En este caso, el identificador arg no tiene uso alguno; por lo que es innecesaria su inclusión.

Si el número de argumentos, o los tipos correspondientes no coinciden entre la declaración


y la línea de encabezado de la definición de la función el compilador marcará un error.

4.3.- Invocación a funciones


Una invocación ó llamada a una función implica pasarle el control de la ejecución del
programa, así como los argumentos ó parámetros que requiere para realizar su tarea. En el
listado 4.1 se tienen las líneas:

saludo(); //INVOCACION A LA FUNCION saludo()


precio = calcula(costo); //INVOCACION A LA FUNCION calcula()

En la primera, se invoca a la función saludo() y no se le pasa ningún argumento. En la


segunda, se invoca a la función calcula(), pasándosele como argumento una copia del valor
que tiene la variable costo. El valor retornado por calcula() se asigna a la variable precio.

// ENCABEZADOS
#include <iostream.h>
#include <conio.h>
// DECLARACION DE FUNCIONES
void saludo();
float calcula(float);
// DEFINICION DE LA FUNCION PRINCIPAL
void main()
{
float costo, precio;
clrscr();
cout << "COSTO : $ "; cin>> costo;
saludo(); //INVOCACION A LA FUNCION saludo()
precio = calcula(costo); //INVOCACION A LA FUNCION calcula()
cout << "PRECIO : $ " << precio; } // DEFINICION DE LA FUNCION saludo()
void saludo() { clrscr(); cout << "!! BIENVENIDO A LA VENTA ESPECIAL !!"; } //
DEFINICION DE LA FUNCION calcula() float calcula(float x) { return( x * 1.6);
}

Listado 4.1.- Diseño de funciones utilizando prototipos.

4.4.- El prototipo de una función


¿es obligatorio?

Cuando encuentra la primera invocación a una función, el compilador verifica si ya se


realizó la declaración ó la definición de la función invocada. En caso de no existir ni
declaración ni definición previa, enviará un mensaje de error diciendo que la función
invocada debe tener un prototipo. En ese momento se detiene la compilación y no se genera
el archivo .OBJ correspondiente.

Lo anterior puede sugerir que el prototipo de la función invocada es indispensable. La


realidad es que se puede omitir el prototipo si escribimos la definición de la función antes de
su primera invocación, como se muestra en el ejemplo 4.2.
// ENCABEZADOS
#include <iostream.h>
#include <conio.h>
// DEFINICION DE LA FUNCION saludo()
void saludo()
{
clrscr();
cout << "!! BIENVENIDO A LA VENTA ESPECIAL !!"; } // DEFINICION DE LA
FUNCION calcula() float calcula(float x) { return( x * 1.6); } // DEFINICION
DE LA FUNCION PRINCIPAL int main() { float costo, precio; clrscr(); cout <<
"COSTO : $ "; cin>> costo;
saludo(); //INVOCACION A LA FUNCION saludo()
precio = calcula(costo); //INVOCACION A LA FUNCION calcula()
cout << "PRECIO : $ " << precio; return 0; }

Listado 4.2.- Diseño de funciones sin uso de prototipos.

Aunque el prototipo no es obligatorio, es recomendable utilizarlo, ya que de esta manera se


permite al compilador verificar que el número y tipos de argumentos utilizados en la
invocación (parámetros actuales) coinciden con los de la definición (parámetros formales).

La declaración de prototipos también sirve para que el usuario de un programa conozca la


forma de utilización de la función, sin tener que proporcionarle el código fuente.
Por ejemplo, si tenemos un programa que maneja operaciones matemáticas podemos
distribuir a los usuarios el código objeto que contenga las definiciones de las funciones y un
archivo de cabecera que contenga los prototipos.

Los archivos pudieran tomar la forma mostrada a continuación:

ARITME.OBJ Archivo que contiene el código objeto, donde se


definen las funciones aritméticas.

//ARITME.HPP: CONTIENE LOS PROTOTIPOS DE LAS FUNCIONES


double suma(double,double);
double resta(double,double);
...........................
...........................

//APLICA.CPP: PROGRAMA FUENTE ELABORADO POR


// EL USUARIO
#include <iostream.h>
#include "ARITME.HPP" //Incluye el archivo que contiene
//los prototipos de las funciones
//aritméticas

void main(void)
{
double result, a=13500.45, b=16763.87;
result = suma(a,b); // Invocación a la función suma()
// definida en ARITME.OBJ
cout << result; }
Este es el criterio que se sigue al utilizar los archivos de cabecera que contiene el paquete
compilador.

Por ejemplo, cuando se escribe la línea:

#include <stdio.h>

se está incluyendo el archivo que contiene los prototipos de las funciones que se utilizan
para el manejo de la entrada/salida estándar.

4.5.- Sobrecarga de funciones

En la mayoría de los casos, los identificadores ó nombres de los objetos deben ser únicos,
esto es, que dos objetos dentro del mismo ámbito no deben tener el mismo nombre. Una de
las excepciones la constituyen las funciones, que pueden compartir el mismo nombre entre
varias de ellas, dando la impresión de que a una sola función se le ha "sobrecargado de
tareas", razón por la cual se les llama funciones sobrecargadas.

Por ejemplo, supongamos que deseamos una función que sirva para sumar números. Lo
ideal sería contar con una sola función que realizara la suma de números de diferentes tipos
y que retornara un valor de acuerdo al tipo de resultado. El Lenguaje C++ permite resolver
este problema por medio de la sobrecarga de funciones.

A continuación se muestran los prototipos para cada una de las funciones que comparten el
nombre suma :

int suma(int,int); // Recibe enteros, devuelve entero

float suma(float,float);

double suma(double,double);

Aquí surge la pregunta: ¿ cómo distingue el compilador entre cada una de las funciones
suma() ?.

Debido a que varias funciones sobrecargadas pueden usar el mismo identificador, C++
utiliza el concepto de nombres ampliados para controlar cada función individualmente. El
nombre ampliado de una función se construye tomando como base el nombre de la función
y los tipos de los argumentos. El tipo de retorno de la función no se utiliza en la formación
del nombre ampliado. La composición del nombre ampliado ocurre a nivel del compilador,
no al nivel del enlazador. El enlazador resuelve fácilmente las referencias externas para
sobrecargar las funciones debido a que éstas tienen nombres ampliados únicos.

Las reglas básicas usadas en Borland C++ para los nombres ampliados son relativamente
simples como se observa a continuación :
1. Se utiliza primero el nombre de la clase, precedido por el carácter @.
2. Enseguida viene el nombre de la función, precedido por el carácter @.
3. Si la función no es parte de una clase, el nombre ampliado empieza con el símbolo @
seguido por el nombre de la función. Se respetan las reglas establecidas para la
creación de identificadores.
4. El nombre de la función está seguido por una secuencia $q ; las letras minúsculas que
le siguen designan cada tipo de argumento declarado.

La tabla 4.1 muestra una lista de las letras que se utilizan para manejar los tipos
predefinidos de C++ en la formación de los nombres ampliados.

Tipo de argumento Letras


void v
void* pv
unsigned char uc
unsigned char* puc
unsigned char& ruc
signed char zc
signed char* pzc
signed char& rzc
int i
int* pi
int& ri
unsigned int ui
unsigned int* pui
unsigned int& rui
long l
long* pl
long& rl
unsigned long ul
unsigned long* pul
unsigned long& rul
float f
float* pf
float& rf
double d
double* pd
double& rd

Tabla 4.1.- Letras para los tipos de argumentos en la creación de nombres ampliados.

Utilizando la tabla 4.1, se pueden predecir fácilmente los nombres ampliados de las
funciones, como se muestra a continuación:

Declaración de funciones Nombre ampliado


void imprim(void); @imprim$qv
void imprim(int); @imprim$qi
void imprim(int,int,int); @imprim$qiii
int redim(long,unsigned int*); @redim$qlpui
void despliega(char*); @despliega$qpzc
int suma(int,int); @suma$qii
float suma(float,float); @suma$qff
double suma(double,double); @suma$qdd

También podría gustarte