Está en la página 1de 16

Ejercicios C++: procedimientos, funciones y

recursividad (II)
¡Hola! Vuelvo con otra entrada de C++ para repasar tanto los procedimientos y funciones y el
paso de parámetros a estos como la recursividad. Si os interesa practicar estos temas os
recomiendo que además de ver esta entrada, os paséis por ésta sobre procedimientos
y ésta sobre recursividad publicadas hace unos días. Os dejo los ejercicios resueltos ;)

Escribe un programa que lea un número natural N por teclado y dibuje un triángulo de
asteriscos con base y altura N. Por ejemplo si N=5 debería dibujarse:

#include <iostream>
using namespace std;

void leerDatos(unsigned& x){


int n;
do{
cout << "Introduce altura de la pirámide: ";
cin >> n;
}while( n < 1);
x = n;
}

void dibujarFila(unsigned x, unsigned i){


for(unsigned j = 1; j <= (x-i); j++){
cout << " ";
}
for(unsigned j = 1; j<= i; j++){
cout << "* ";
}
for(unsigned j = 1; j <= (x-i); j++){
cout << " ";
}
}

void dibujarPiramide (unsigned x){


for(unsigned i = 1; i <= x; i++){
dibujarFila(x, i);
cout << endl;
}
}

int main() {
unsigned x;
leerDatos(x);
dibujarPiramide(x);
return 0;
}

Escriba un programa que tome como entrada desde teclado dos números naturales (mayores
que cero) "N" e "i", e imprima en pantalla el dígito que ocupa la posición i-ésima del número
N. Si i es mayor que el número de dígitos de N, se escribirá en pantalla -1. Por ejemplo, para N
= 25064 e i = 2, el resultado es el dígito 6, y para i = 7, el resultado es -1.

#include<iostream>
#include <math.h>
using namespace std;
void introducirDatos(unsigned& N, unsigned& i){
cout << "Introduce N (>0) : ";
cin >> N;
cout << "Introduce i (>0) : ";
cin >> i;
}
int calcularDigito(unsigned N, unsigned i){
int num;
num = N /(pow( 10,(i-1) ));
if(num == 0){
num = -1;
}else{
num = num%10;
}
return num;
}

int main() {
unsigned N, i;
cout << "Programa que imprime en pantalla el dígito que ocupa la
posición i-ésima del número N."<< endl;
introducirDatos(N, i);
cout << "Resultado: " << calcularDigito(N, i);

return 0;
}

Escribe un programa que acepte como entrada desde teclado un número natural mayor que
cero
y dé como salida el resultado de sumar dos a dos los dígitos que aparecen en posiciones
simétricas respecto al dígito central dentro del número dado como entrada. Por ejemplo :
para el número : 2354869
la salida es: 2+9 = 11, 3 + 6 = 9, 5 + 8 = 13, 4
para el número : 6582
la salida es : 6 + 2 = 8, 5 + 8 = 13
#include <iostream>
using namespace std;

void introducirDatos(unsigned& N){


cout << "Introduce N (>0) : ";
cin >> N;
}

unsigned longitudNumero(unsigned num){


unsigned contador = 0;
while(num!= 0){
num = num/10;
contador++;
}
return contador;
}
unsigned potencia(int a,int b) { //potencia(10, 3)
unsigned resultado = 1;
if(b == 1){
resultado = a;
} else if (b > 1){
for(int i = 1; i <= b; i++){
resultado = resultado * a;
}
}
return resultado;
}

int calcularDigito(unsigned N, unsigned i){


int num;
num = N /(potencia( 10,(i-1) ));
if(num == 0){
num = -1;
}else{
num = num%10;
}
return num;
}

void Sumar(unsigned N){


unsigned longitud = longitudNumero(N);
unsigned long2= longitud;

unsigned suma;
for(int i =1; i <= int(longitud/2); i++){
suma = calcularDigito(N, i) + calcularDigito (N, long2);
cout << calcularDigito (N, long2)<< "+" << calcularDigito(N, i)
<<"=" << suma << " ";
long2 --;
}
if(longitud%2 != 0){
cout << calcularDigito(N, unsigned(longitud/2)+1 );
}
}

int main() {
unsigned N;
introducirDatos(N);
Sumar(N);

return 0;
}

Decimos que una sucesión a1,a2,...,an de enteros forma una montaña, si existe un h tal
que : 1 <= h <= n y además
a1 < ...ah-1 < ah > ah+1 > ...an
Definimos la longitud de una montaña como el número de enteros que la forman. Por ejemplo
la sucesión -7, -1, 6, 21, 15 es una montaña de longitud 5.
Definimos un valle de la misma forma que una montaña pero cambiando el signo de las
desigualdades de la definición anterior:
a1 > ...ah-1 > ah < ah+1 < ...an
Por ejemplo 24, 13, 6, 15, 50 sería un valle.
Dada una secuencia de enteros terminada en cero (0) que como mínimo contiene una montaña
y un valle (suponemos que la secuencia de enteros de entrada es una secuencia correcta de
montañas y valles), diseña un programa que calcule la longitud de la montaña y el valle más
largos.
#include<iostream>
using namespace std;

void leerNumero(int& mmontana, int& mvalle){


int num, ant=0;
int montana =0;
int valle = 0;
mmontana =0;
mvalle =0;
int antant=0;
cout << "Introduce: "<<endl;
cin >> num;
while(num!= 0){
if(num < ant && antant < ant){
valle = 2;
}else{
valle++;
if(mvalle < valle){
mvalle = valle;
}
}
if(num > ant && antant > ant){
montana =1;

}else{
montana++;
if(mmontana < montana){
mmontana = montana;
}
}
antant = ant;
ant= num;
cin >> num;
}
}

int main() {
int mmontana;
int mvalle;
leerNumero(mmontana, mvalle);
cout <<"Mayor montaña: "<< (mmontana+1) << endl;
cout << "Mayor valle: "<< mvalle;
return 0;
}

El máximo común divisor (mcd) de dos números naturales p y q es el mayor entero d que
divide a ambos. Un algoritmo muy conocido para calcularlo es el de Euclides. Éste utiliza
dos variables, que contienen inicialmente a cada uno de los números, y trata de hacer que su
contenido sea el mismo. Para ello, irá restando la menor a la mayor hasta que ambas
contengan el mismo valor. En dicho momento, el valor obtenido en cualquiera de ellas es el
máximo común divisor de los dos números iniciales. Por ejemplo, si P = 18 y Q = 12, el
algoritmo hará que P y Q vayan tomando los siguientes valores:
Inicialmente P == 18 y Q == 12 (P > Q => P = P - Q)
Después P == 6 y Q == 12 (Q > P => Q = Q - P)
Después P == 6 y Q == 6 (P == Q => El mcd es 6)
Diseña el algoritmo anterior siguiendo un enfoque recursivo:
unsigned mcd(unsigned P, unsigned Q)
#include <iostream>
using namespace std;

unsigned mcd(unsigned P, unsigned Q){


unsigned res;
if(P == Q){
res = P;
}else{
if(P > Q){
res = mcd(P - Q, Q);
}else{
res = mcd(P, Q - P );
}
}
return res;
}

int main() {
unsigned P, Q;
cout << "Máximo común divisor. " << endl; // prints !!!Hello World!!!
cout << "Introduce dos naturales: ";
cin >> P >> Q;
cout << "El mcd es "<< mcd(P,Q);
return 0;
}

Diseña un procedimiento recursivo que lea una secuencia de caracteres de longitud arbitraria
terminada en un punto, y la imprima en orden inverso. El procedimiento no tiene parámetros.
#include <iostream>
using namespace std;

void invertirTexto(){
char a;
cin.get(a);
if(a == '.'){

}else{
invertirTexto();
cout << a;
}
}

int main() {
cout << "Introduce texto a invertir terminado en punto." << endl; //
prints !!!Hello World!!
invertirTexto();
return 0;
}

Y hasta aquí la entrada de hoy ;)


Paso de Variables por Referencia y por Valor en C++
29 ENERO, 2015 / JULIO CÉSAR

En C++ se pueden pasar argumentos por valor o por referencia a la hora de


llamar una función o un método de una clase, cuando pasamos argumentos por
valor lo que sucede es que se realiza una copia de los valores que se envían a la
función y de esta forma los valores originales permanecen inalterados, por
ejemplo, si tenemos una función intercambiar( ) que recibe dos números
enteros y los ‘intercambia’ literalmente, es decir, el contenido de a pasa
hacia b y viceversa, si se tiene el siguiente programa:
1 #include <iostream>

2
using namespace std;
3

4
void intercambiar(int, int); //Protipo de la función para intercambiar los valor
5

6
int main(void)
7
{
8
int a = 2,b = 3;
9
cout<<"Valores originales a = "<<a<<" y b = "<<b<<endl<<endl;
10 intercambiar(a,b); //Llmado a la función intercambiar
11 cout<<"Luego de la funcion a = "<<a<<" y b = "<<b<<endl<<endl;

12
13 return 0;

14 }

15
void intercambiar(int i, int j)
16
{
17
int z;
18
z = i;
19
i = j;
20 j = z;
21 }

22

23

Y al compilar y ejecutar el código anterior lo que se obtiene en pantalla son los


valores que contienen las variables principales.

Cuando se imprime el valor de las variables a y b se observa que de hecho no


fueron alteradas después de la llamada a la función intercambiar( ), esto se
debe a que en realidad lo que le pasamos a la función fue una copia de los
valores de las variables ay b, por lo tanto era de esperar que las copias se
cambiaran pero que finalmente los elementos originales permanecieran
intactos.
Por otro lado, cuando se pasan valores por referencia, no se pasa una copia de
los valores originales, sino que se crea (cómo su nombre lo india) una referencia
que tiene la misma dirección en memoria del elemento original, esto quiere
decir dos cosas, en primer lugar la variable con la cual se trabajará al interior
de la función que recibe la referencia será por definición un sinónimo de la
variable original, esto quiere decir que será idéntica y cuanto a la dirección de
memoria, su contenido, etc; Por otro lado esto quiere decir que cualquier
modificación que hagamos en la función al contenido de la referencia afectará
de forma inmediata a las variables originales.
Para pasar valores por referencia a una función, solo se debe anteponer el
símbolo &(ampersand) en el nombre de la variable que será la referencia de
las variables originales, es decir, el prototipo de la nueva
función intercambiar( ) que ahora trabaja con referencias, será:
1 void intercambiar(int &i , int &j ); //Protipo de la función para intercambiar los

Luego si solo agregamos este operador (el & se conoce también como operador
de referencia), al programa anterior tal como se muestra a continuación, el uso
de las variables será común y corriente, sin embargo cualquier cambio que se
realice en ellas afectará directamente a las variables en el programa principal
(función main( )), si compilamos y ejecutamos el siguiente código.
1

2 #include <iostream>

3
using namespace std;
4

5
void intercambiar(int &i , int &j ); //Protipo de la función para intercambiar lo
6

7
int main(void)
8
{
9
int a = 2,b = 3;
10 cout<<"Valores originales a = "<<a<<" y b = "<<b<<endl<<endl;
11 intercambiar(a,b); //Llmado a la función intercambiar
12 cout<<"Luego de la funcion a = "<<a<<" y b = "<<b<<endl<<endl;

13

14 return 0;

15 }

16
void intercambiar(int &i, int &j)
17
{
18
int z;
19
z = i;
20
i = j;
21 j = z;
22 }
23

El resultado obtenido será el siguiente:


Entonces se puede notar como en esta ocasión si se vio afectado el contenido
de las variables “originales” (aunque finalmente no está del todo bien
decir originales ya que como habíamos comentado, en el fondo, las variables
son las mismas al compartir la misma dirección en memoria).
Si estabas pensando que sucede a la hora de trabajar con arrays, pues en ese
caso debemos trabajar con punteros, que aunque es bastante fácil y no es mucha
la diferencia, abordaremos ese tema más adelante, por ahora solo vemos como
pasar variables de cualquier tipo, incluso si son objetos o variables definidas
por el usuario.
Hasta aquí este artículo, espero que te haya sido útil, y si crees que esta
información puede resultar útil para alguien más, ayúdanos a compartirla.

Saludos.

Variable referencia

Una referencia o variable referencia en C++ es simplemente otro


nombre o alias de una variable. En esencia una referencia actúa igual que un
puntero (contiene la dirección de un objeto), pero funciona de diferente modo,
ya que no se puede modificar la variable a la que está asociada la referencia,
pero sí se puede modificar el valor de la variable asociada.

Usando variable referencia


int i;
int &x=i; // x es un alias de i
x=40; // i vale 40

Usando punteros
int i;
int *p=&i;
*p=40; //i vale 40

Parámetros por valor y por referencia

En 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.

Cuando una función debe modificar el valor de la variable pasada como


parámetro y que esta modificación retorne a la función llamadora, se debe
pasar el parámetro por referencia. En este método, el compilador no pasa una
copia del valor del argumento; en su lugar, pasa una referencia, que indica a la
función dónde existe la variable en memoria.

La referencia que una función recibe es la dirección de la variable. Es


decir, pasar un argumento por referencia es, simplemente, 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 limitación del 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.

Los modificadores const y volatile

El número y tipo de argumentos de una definición de función debe


coincidir con su correspondiente prototipo de función. Si no existe
correspondencia exacta, el compilador C++ intentará convertir el tipo de los
argumentos reales de modo que se puedan concordar con los argumentos
formales en las funciones llamadas. Los modificadores const y volatile pueden
preceder a los tipos de los argumentos formales para indicar instrucciones
específicas al compilador.

Se puede preceder a un tipo de argumento con el modificador const


para indicar que este argumento no puede ser modificado. Al objeto al cual se
aplica no se le puede asignar un valor o modificar de ningún modo.

El modoficador volatile se puede utilizar para indicar que los


argumentos formales pueden ser modificados en la ejecución del programa,
bien por el propio programador o por el propio sistema, es decir, cualquier
suceso externo al programa. Típicamente, las variables volátiles se necesitan
sólo para valores que pueden ser modificados por una rutina de interrupción
que opera independientemente del programa. La declaración de una variable
volátil es volatile int k;

Paso de parámetros en C++

El paso de parámetros en C++ se puede hacer de dos formas:


1. Paso de parámetros por valor.
2. Paso de parámetros por referencia.
1. Paso de parámetros por valor.
Pasar parámetros por valor significa que a la función se le pasa una copia del
valor que contiene el parámetro actual.
Los valores de los parámetros de la llamada se copian en los parámetros de la
cabecera de la función. La función trabaja con una copia de los valores por lo
que cualquier modificación en estos valores no afecta al valor de las variables
utilizadas en la llamada.
Aunque los parámetros actuales (los que aparecen en la llamada a la función) y los
parámetros formales (los que aparecen en la cabecera de la función) tengan el mismo
nombre son variables distintas que ocupan posiciones distintas de memoria.
Por defecto, todos los argumentos salvo los arrays se pasan por valor.
Ejemplo de paso de parámetros por valor
El siguiente programa lee un número entero y llama a una función que invierte las
cifras del número:
#include <iostream>
using namespace std;
int invertir (int);
int main()
{ int num;
int resultado;
cout << "Introduce un numero entero: ";
cin >> num;
resultado = invertir(num);
cout << "Numero introducido: " << num << endl;
cout << "Numero con las cifras invertidas: " << resultado << endl;
system("pause");
}
int invertir(int num)
{
int inverso = 0, cifra;
while (num != 0)
{
cifra = num % 10;
inverso = inverso * 10 + cifra;
num = num / 10;
}
return inverso;
}
La salida de este programa es:
En la llamada a la función el valor de la variable num se copia en la variable num de
la cabecera de la función. Aunque tengan el mismo nombre, se trata de dos variables
distintas.
Dentro de la función se modifica el valor de num, pero esto no afecta al valor de num
en main. Por eso, al mostrar en main el valor de num después de la llamada aparece
el valor original que se ha leído por teclado.

2. Paso de parámetros por referencia.


El paso de parámetros por referencia permite que la función pueda modificar el valor
del parámetro recibido.
Vamos a explicar dos formas de pasar los parámetros por referencia:
2.1 Paso de parámetros por referencia basado en punteros al estilo C.
2.2 Paso de parámetros por referencia usando referencias al estilo C++.
2.1 Paso de parámetros por referencia utilizando punteros.

Cuando se pasan parámetros por referencia, se le envía a la función la dirección


de memoria del parámetro actual y no su valor. La función realmente está
trabajando con el dato original y cualquier modificación del valor que se realice
dentro de la función se estará realizando con el parámetro actual.
Para recibir la dirección del parámetro actual, el parámetro formal debe ser un
puntero.
Ejemplos de paso de parámetros por referencia
Ejemplo 1:
Programa c++ que lee dos números por teclado y los envía a una función para que
intercambie sus valores.
#include <iostream>
using namespace std;
void intercambio(int *, int *);
int main( )
{
int a, b;
cout << "Introduce primer numero: ";
cin >> a;
cout << "Introduce segundo numero: ";
cin >> b;
cout << endl;
cout << "valor de a: " << a << " valor de b: " << b << endl;
intercambio(&a, &b);
cout << endl << "Despues del intercambio: " << endl << endl;
cout << "valor de a: " << a << " valor de b: " << b << endl;
system("pause");
}
void intercambio(int *x, int *y)
{
int z;
z = *x;
*x = *y;
*y = z;
}
La salida de este programa es:

En la llamada, a la función se le envía la dirección de los parámetros. El operador que


obtiene la dirección de una variable es &.
intercambio(&a, &b);
En la cabecera de la función, los parámetros formales que reciben las direcciones
deben ser punteros. Esto se indica mediante el operador *
void intercambio(int *x, int *y)
Los punteros x e y reciben las direcciones de memoria de las variables a y b. Al
modificar el contenido de las direcciones x e y, indirectamente estamos modificando
los valores a y b. Por tanto, pasar parámetros por referencia a una función es
hacer que la función acceda indirectamente a las variables pasadas.
Ejemplo 2:
En el siguiente ejemplo, se lee una hora (hora, minutos y segundos) y se calcula la
hora un segundo después. El programa utiliza una función segundo_despues que
recibe la hora, minutos y segundos leídos y los modifica de forma que al finalizar
contienen la hora un segundo después.
// Hora un segundo después
#include <iostream>
#include <iomanip>
using namespace std;
void segundo_despues(int *, int *, int *);
int main()
{ int horas, minutos, segundos;
do
{
cout << "Introduce hora: ";
cin >> horas;
}while(horas<0 || horas > 23);
do
{
cout << "Introduce minutos: ";
cin >> minutos;
}while(minutos<0 || minutos > 59);
do
{
cout << "Introduce segundos: ";
cin >> segundos;
}while(segundos<0 || segundos > 59);

//llamada a la función
segundo_despues(&horas, &minutos, &segundos);

cout << setfill('0');


cout << endl << "Hora un segundo despues: ";
cout << setw(2) << horas << ":";
cout << setw(2) << minutos << ":";
cout << setw(2) << segundos << endl;
system("pause");
}

//function c++ que recibe una hora expresada en


//horas, minutos y segundos y calcula la hora
//un segundo después
void segundo_despues(int *h, int *m, int *s)
{
(*s)++;
if(*s == 60)
{
*s = 0;
(*m)++;
if(*m == 60)
{
*m = 0;
(*h)++;
if(*h == 24)
{
*h = 0;
}
}
}
}
Esta función no devuelve nada (aparece void como tipo devuelto), por lo que no es
necesario poner la instrucción return.
Mediante return una función solo puede devolver un valor. En casos como la función
anterior en los que es necesario devolver más de un valor, habrá que hacerlo pasando
los parámetros por referencia.
Este paso de parámetros por referencia basado en punteros es el utilizado por C y
por tanto también puede usarse en C++. Pero C++ incorpora una nueva forma de
paso de parámetros que no hace uso de punteros.
Es el paso de parámetros utilizando referencias.
2.2 Paso de parámetros utilizando referencias.

Una referencia es un nombre alternativo (un alias, un sinónimo) para un objeto. Una
referencia no es una copia de la variable referenciada, sino quees la misma
variable con un nombre diferente.
Utilizando referencias, las funciones trabajan con la misma variable utilizada en la
llamada. Si se modifican los valores en la función, realmente se están modificando
los valores de la variable original.
Ejemplo:
El primer ejemplo del punto anterior utilizando referencias lo podemos escribir así:
#include <iostream>
using namespace std;
void intercambio(int &, int &);
int main( )
{ int a, b;
cout << "Introduce primer numero: ";
cin >> a;
cout << "Introduce segundo numero: ";
cin >> b;
cout << endl;
cout << "valor de a: " << a << " valor de b: " << b << endl;
intercambio(a, b);
cout << endl << "Despues del intercambio: " << endl << endl;
cout << "valor de a: " << a << " valor de b: " << b << endl;
system("pause");
}
void intercambio(int &x, int &y)
{
int z;
z = x;
x = y;
y = z;
}
En la declaración de la función y en la definición se coloca el operador referencia a &
en aquellos parámetros formales que son referencias de los parámetros actuales:
void intercambio(int &, int &); //declaración de la función
void intercambio(int &x, int &y) //definición de la función
Cuando se llama a la función:
intercambio(a, b);
se crean dos referencias (x e y) a las variables a y b de la llamada. Lo que se haga
dentro de la función con x e y se está haciendo realmente con a y b.
La llamada a una función usando referencias es idéntica a la llamada por valor.
Ejemplo:
El segundo ejemplo anterior, utilizando referencias:
// Hora un segundo después
#include <iostream>
#include <iomanip>
using namespace std;
void segundo_despues(int &, int &, int &);
int main()
{ int horas, minutos, segundos;
do
{
cout << "Introduce hora: ";
cin >> horas;
}while(horas<0 || horas > 23);
do
{
cout << "Introduce minutos: ";
cin >> minutos;
}while(minutos<0 || minutos > 59);
do
{
cout << "Introduce segundos: ";
cin >> segundos;
}while(segundos<0 || segundos > 59);
segundo_despues(horas, minutos, segundos);
cout << setfill('0');
cout << endl << "Hora un segundo despues: ";
cout << setw(2) << horas << ":";
cout << setw(2) << minutos << ":";
cout << setw(2) << segundos << endl;
system("pause");
}

void segundo_despues(int &h, int &m, int &s)


{
s++;
if(s == 60)
{
s = 0;
m++;
if(m == 60)
{
m = 0;
h++;
if(h == 24)
{
h = 0;
}
}
}
}

También podría gustarte