Está en la página 1de 14

PARTE I

1. Introducción
No tiene ejercicios.

2. Conceptos básicos
2.1) Quedaría del siguiente modo:

hay otros tipos de comentarios como los de C++:


empezar un comentario tipo C y ahora lo acabo. */
/ * Que comentario más precioso * /
/ / Este es más precioso todavía.

2.2) Si en un programa no ponemos la función main(), el programa no se podría enlazar. Si


ponemos dos funciones main() ocurre lo mismo, el compilador da un error de redefinición de una
función.

2.3) El programa puede parecer a primera vista muy sencillo. En primer lugar vamos a leer y escribir una
cadena. La primera solución intuitiva:

#include <iostream> // USAMOS: cin, cout

void main() {

char s[20]; // Cadena de hasta 19 caracteres


cin >> s; // Leer la primera palabra
cout << endl << s // Escribir en nueva línea la cadena
<< endl; // Y pasar a la línea siguiente
}

El problema es que esto únicamente nos lee la primera palabra de una cadena (esto se explicará en el
capítulo de entrada/salida). Aunque no se comprenda de momento, la solución se encuentra en el
fichero EJ02_03.CPP

2.4) Aquí está el programa:

#include <iostream> // USAMOS: cin, cout

void main() {
double d1, d2;
out << "Introduce dos reales: ";
cin >> d1 >> d2;
cout << "La suma es: " << d1 + d2 << endl
}

2.5) El programa correcto es éste:


#include <iostream.h> // USAMOS: cout
void main() {
cout << "Hola mundo";
}

2.6) Sí es correcta.

2.7) Este comentario es erróneo. Hemos dicho que los comentarios no se detectan en las cadenas. Pues no
es completamente cierto. No se detecta su apertura pero sí su clausura. Por ello, las sentencias se
convertirían en:

";
*/

La solución sería definir de nuevo las sentencias como:

/*
cout << "/* Me pillaste *""/"; // Concatenación de cadenas
*/

3. Tipos de datos
3.1) La función es:

int Convierte(char c) {
return int(c - '0');
}
// Usando int() en vez de (int) se ahorra un par de paréntesis

3.2) Sí que es válido ya que en C++ todos los tipos integrales son compatibles. Aunque sería mucho
mejor explicitar las conversiones:

b= (byte)w;
w= (word)l;
d= (dword)w;

3.3) Su longitud es 9 tomando sizeof(int) == 2.


3.4) La primera dará error ya que 8 no es un dígito octal y por tanto 08 es un error. La segunda dará 24
porque 014 está en octal que es 12 en decimal.

4. Control de Flujo
4.1) El listado es funcionalmente correcto pero sintácticamente no. Faltan los puntos y comas de las
cuatro sentencias de asignación.

if (a < b)
if (a < c)
min= a;
else
min= c;
else
if (b > c)
min= c;
else
min= b;

4.2) Programa que cuenta el número de ocurrencias en una cadena de las 5 vocales en 5 variables
diferentes: a, e, i, o, u. Usaremos la función Leer_Cadena() del ejercicio 2.3. El programa está
en EJ04_02.CPP

4.3) Una función que calcule el M.C.D. de dos números:

int Mcd(int a, int b) {


if (a <= 0 || b <= 0)
return -1; // Código de error
while (a != b)
if (a < b)
b= b - a;
// b-= a; // Igual que la anterior. Ya se verá
else
a= a - b;
// a-= b; // Igual que la anterior
return a; // Al final el mcd está en a y en b (a == b)
}

Un ejemplo de uso de la función está en EJ04_03.CPP

4.4) Función que compara dos cadenas:

int StrCmp(char *s1, char *s2) {


int i= 0;
while (s1[i] || s2[i]) { // Hasta terminar las dos
if (s1[i] < s2[i])
return -1; // La cadena 1 es menor que la 2
else if (s1[i] > s2[i])
return 1; // La cadena 1 es mayor que la 2
i++;
}
return 0; // Las cadenas son iguales
}

Esta función es similar a strcmp() de la librería estándar <cstring> del C++.


Un ejemplo de uso de la función está en EJ04_04.CPP

4.5) Sólo la b) es correcta. Recordemos que la sentencia:

for(a; b; c);
se convierte en:

{
a;
while(b) {
d;
c;
}
}

Por tanto, las sentencias anteriores son:

a) int i= 0, int j= 0; // Incorrecto


// Dos declaraciones separadas por una coma
while ..

b) int i= 0, j= 0; // Correcto
// Dos declaraciones de tipo entero
while ..

c) int i= 0, long j= 0; // Incorrecto


while ..

d) (int i = 0), (long j = 0) // Incorrecto


// Lástima porque era una buena idea

4.6) La solución se encuentra en el fichero EJ04_06.CPP

4.7) En este ejercicio se ha pretendido aumentar la atención del lector en este error común y sutil pero
difícil de detectar. La condición del bucle está formada por el operador de asignación (=) y no el operador
de comparación (==), con lo que el resultado del programa es que sólo muestra un 0, ya que el resultado
de la asignación i=10, además de asignar 10 a la variable i, es que devuelve el valor 10, que es un
valor cierto, al ser no nulo. Si después lo negamos con el operador ! obtenemos falso, con lo que el bucle
sale después de la primera iteración.

4.8) La solución se encuentra en el fichero EJ04_08.CPP

4.9) La solución se encuentra en el fichero EJ04_09.CPP

4.10) La solución se encuentra en el fichero EJ04_10.CPP

4.11) La solución se encuentra en el fichero EJ04_11.CPP

5. Operadores
5.1) Es simple:

x & (x - 1)

5.3) Se supone que tenemos dos valores enteros almacenados en dos variables reales. Yo lo haría así:
float Resto(float a, float b) {
return float((long)a % (long)b);
}

5.4) NO ES VÁLIDO PORQUE EL OPERADOR COMA NO SE PUEDE UTILIZAR EN ESE PARTE


DEL FOR. Si cogemos uno de los dos incementos y lo ponemos al final del bucle sí que funciona. En este
caso invierte el vector de caracteres s (no es una cadena porque no acaba en '\0'). El resultado en s
será ACABATOR.

5.5) Con algo parecido a esto sería suficiente para que pareciera aleatorio. Si además hacemos coincidir la
llamada a Rand() con un factor externo (tiempo, preferiblemente), esta función es casi
impredecible. El programa se encuentra en EJ05_05.CPP

6. Funciones
6.1) La solución se encuentra en el fichero EJ06_01.CPP

6.2) La solución se encuentra en el fichero EJ06_02.CPP

6.3) Es sintácticamente correcto. El compilador crea variables temporales para almacenar estas constantes
y así ya puede tomar la dirección. De todas formas no es un buen estilo de programación pasar constantes
por referencia porque aunque la función modifique su valor no nos podemos dar cuenta.

6.4) La llamada f(25) es ambigua. El compilador no sabe si llamar a la función con un argumento o
llamar a la segunda usando parámetros por defecto. La llamada f(17, 42) es completamente
correcta ya que no hay ambigüedad.

6.5) Sí que es correcto y sería equivalente a:

void f(int a= 1, int b= 2, int c= 3) {


// ..
}

6.6) La solución se encuentra en el fichero EJ06_06.CPP

7. Variables
7.1) Las variables estáticas se inicializan a 0. Las variables automáticas no. Por tanto a valdrá 0 y b tendrá
un valor indefinido dependiendo del compilador. No se recomienda usar la declaración de 'a' de ese
modo. Es mejor explicitar:

int a= 0;

7.2) Este sería un programa que volvería loco al propio Bjarne Stroustrup:

void Funcion(float f); // Decl1. Campo prototipo


float f;
// Decl 2. Campo global. Se almacena en el seg. de datos.
f vale 0
void Funcion(float f) {
// Decl. 2. Campo local automático. Se almacena en pila
float f;
// Error: parámetros y var. locales tienen el mismo campo

auto float a; // Este auto es opcional


// Decl.3.Campo local automático. Se almacena en pila. a
vale ?

static float f;
// Error: mismo campo.

static float s;
// Decl.4.Campo local estático. Se almac. en el s. de
datos. s vale 0
{
float f;
// Decl. 5. Campo de bloque. Se almacena en la pila
f= 2; // Accedo a la 'f' de la decl. 5
::f= 3; // Accedo a la 'f' de la decl. 1
s= 4; // Accedo a la 's' de la decl. 4
a= 5.5; // Accedo a la 'a' de la decl. 3
// No hay forma de acceder al parámetro 'f' de la función
(Decl. 2)
}
}

float f; // Error! Redefinimos la variable global.

7.3) Como hemos visto en el caso anterior, no es correcto ya que los dos tienen el mismo campo local
automático.

7.4) Dará un error en la definición const int a ya que las constantes se deben inicializar en el
momento de la definición. Las otras dos también darían error.

7.5) No sería equivalente a:

const char * Var;

sino a:

char * const Var;

porque typedef no es una macro.


7.6) El programa A funciona correctamente. El programa B da error porque no sabemos cómo es la
estructura, por tanto, no podemos definir una variable de ella. El programa C funcionaría si no se tratara
de una estructura. Ya se vio que extern sólo es aplicable a variables de tipos no compuestos.

7.7) Este sería un programa que volvería loco al propio Bjarne Stroustrup:

void Funcion(float f); // Decl1. Campo prototipo


float f;
// Decl 2. Campo global. Se almacena en el seg. de datos.
f vale 0

void Funcion(float f) {
// Decl. 2. Campo local automático. Se almacena en pila
float f;
// Error: parámetros y var. locales tienen el mismo
campo
auto float a; // Este auto es opcional
// Decl.3.Campo local automático. Se almacena en pila.
a vale ?
static float f;
// Error: mismo campo.
static float s;
// Decl.4.Campo local estático.Se almac. en el s. de
datos. s vale 0
{
float f;
// Decl. 5. Campo de bloque. Se almacena en la pila
f= 2; // Accedo a la 'f' de la decl. 5
::f= 3; // Accedo a la 'f' de la decl. 1
s= 4; // Accedo a la 's' de la decl. 4
a= 5.5; // Accedo a la 'a' de la decl. 3
// No hay forma de acceder al parámetro 'f' de la función
(Decl. 2)
}
}

float f; // Error! Redefinimos la variable global.

8. Sobrecarga y conversiones
8.1) En C++, las constantes tienen tipo por lo que el compilador asignará:

- la primera es un int . Coincidencia exacta con Print(int ).

- la segunda es un double. No hay coincidencia exacta ni trivial. No hay promoción. Hay


conversión estándar. Pero las conversiones estándar de un tipo aritmético puede ser a cualquier otro tipo
aritmético. Por tanto, fallará porque hay una ambigüedad. Podríamos haberlo solventado poniendo 2.2F.

- la tercera es un char. No hay coincidencia exacta ni trivial. Pero hay promoción con int; por
tanto, se llama a Print(int ).
En general, las posibles soluciones a los problemas que aparecen (como el de la segunda llamada) son:

a) Declarar variables auxiliares del tipo que se desee.

b) Forzar que las constantes sean del tipo requerido.


c) Utilizar conversiones explícitas (cast)

8.2) Sí, no hay coincidencia exacta o trivial, no hay promociones, pero hay conversión estándar
aritmética. Por tanto, se llama sin ningún problema.

8.3) No porque tomará f() como float y no como una función. Concretamente, dará un error
de llamada a no-función ("call of non-function") ya que estamos intentando llamar a un float como
si fuera una función.

8.4) La solución se encuentra en el fichero EJ08_04.CPP

8.5) La solución se encuentra en el fichero EJ08_05.CPP

8.6) Daría error al haber conversión trivial entre const T y T.


8.7) Como no hay conversión trivial, se podría definir perfectamente.

8.8) Las dos primeras llamadas invocan a sus funciones correspondientes sin ningún problema. La tercera
sigue estos pasos: Primero: no hay coincidencia exacta. Segundo: no hay promoción. Tercero: conversión
estándar, pero la hay a los dos, no le damos preferencia a la que no tiene signo. Por tanto daría error de
ambigüedad.

8.9) La solución se encuentra en el fichero EJ08_09.CPP

8.10) La solución se encuentra en el fichero EJ08_10.CPP

8.11) Son correctas. Se trata de conversiones de línea.

8.12) En C++, typedef no crea tipos nuevos distintos, sólo les da un nombre diferente.
8.13) Para las cinco llamadas, el proceso es bien diferente:

1.- El literal 0.0 es un double. Pasos: Primero: coincidencia exacta. Por tanto
se llama a f(double ).

0 es un int. Pasos: Primero: no hay coincidencia exacta. Segundo:


2.- El literal
no hay promoción posible. Tercero: hay conversión estándar de int a char y
de int a double. Además, dijimos que la constante 0 tiene conversión
estándar con cualquier puntero. Por tanto habrá error de ambigüedad al no poder elegir
ninguna de las tres funciones.

3.- El literal 0F da error de sintaxis, ya que F sólo se puede aplicar a constantes


reales.

0.0F es un float. Pasos: Primero: no hay coincidencia exacta.


4.- El literal
Segundo: hay promoción de float a double. Por tanto se llama
a f(double ).

char *. Pasos: Primero: no hay coincidencia exacta.


5.- El literal cadena es un
Segundo: no hay promoción. Tercero: hay conversión estándar entre char
* y void *. Por tanto se llama a f(void *).

8.14) Para la primera combinación, la segunda llamda es correcta (mismo tipo), pero la primera no,
porque no hay conversión estándar desde int a enum. Como si está permitido lo contrario, la
combinación dos es perfectamente correcta. La combinación tercera también lo es, llamando cada una a
su correspondiente función.

8.15) El compilador da un error de ambigüedad, ya que no sabe si llamar a ff(fc) sin signo
o ff(fc) con signo. ¡Qué complicados son los complicadores!

9. Punteros
9.1) El primero carga en p la dirección de la variable a (p= &a), pero al cerrarse el bloque la
variable a se destruye con lo que el acceso posterior de (*p= 10) puede ser catastrófico.
El segundo programa, en cambio, funciona correctamente ya que el carácter a tratar se almacena en
el 'heap' y no en la pila, así al cerrar el bloque no destruimos ninguna variable ya que no hemos definido
ninguna tampoco. El acceso (*p= 10) será válido hasta que pongamos (delete p;).

Una mejor solución sería:

void main() {
char *p;
int a;
{
p= &a;
}
*p= 10;
}

9.2) Invierte una cadena. La solución se encuentra en el fichero EJ09_02.CPP

9.3) La solución se encuentra en el fichero EJ09_03.CPP

9.4) La solución se encuentra en el fichero EJ09_04.CPP

9.5) La solución se encuentra en el fichero EJ09_05.CPP

9.6) Ese programa es muy peligroso. Leemos una cadena en s, pero s apunta a una dirección
indefinida; por ello, podemos estar estropeando código, datos de nuestro o de otro programa. Además no
se puede asegurar que la salida sea igual que la entrada. En fin, que este es uno de los errores más graves
y típicos del C++. Además, puede que en un primer momento funcione. Más tarde el error aparecerá
inesperadamente de forma catastrófica. La solución es reservar la memoria que vamos a usar:

#include <iostream.h>

void main() {
char s[100];
// Suponemos que con 100 caracteres es suficiente
cin >> s;
cout << s;
}

También podríamos haber usado:

#include <iostream.h>

void main() {
char *s;
s= new int[100];
cin >> s;
cout << s;
delete []s;
}

9.7) No ocurre nada, al final del programa el compilador se encarga de hacer todos los delete que falten.
De todas formas, es muy recomendable no olvidarse de ponerlo porque si es en una función que se llama
1000 veces acabaremos con el 'heap' lleno!. Tampoco es muy recomendable hacer lo que se ha hecho en
el ejercicio 1, pero a veces como en ese ejercicio, es necesario.

9.8) Para hacer lo que se nos pide en el ejercicio habría que hacer uso de punteros:

float f;
int *pi= (int *)&f;
char *pc= (char *)&f;

Y con f, *pi, *pc accederíamos a lo mismo que con la unión: f, i, c. Claramente, usar
una unión anónima es más limpio aunque con punteros se ve físicamente que comparten la misma
memoria. En este caso, trabajar con punteros puede ser peligroso, ya que si tenemos:

char c;
int *pi= (int *)&c;
float *pf= (float *)&c;

un acceso a (*pi) a (*pf) excedería del tamaño del carácter, estropeando lo que hay después en
memoria, que en este caso es el puntero que accede. Aquí, se puede decir, que está casi asegurado que el
sistema se quede bloqueado o lo que en el argot se conoce como "colgado".

9.9) El programa compara los punteros, no donde apuntan. Si lo sustituyéramos por(*s == *t)
tampoco ya que sólo compararía el primer elemento. Queda como ejercicio hacer una función que
compare cadenas. En el siguiente capítulo también se verán algunas funciones de comparación.

p como un puntero a enteros constantes sobre los cuales


9.10) No es correcto porque hemos definido
nos podemos hacer un delete. Además, delete p sólo borraría el primer elemento, en el
caso de que no fuera const.
9.11) Los dos son, obviamente, equivalentes y ninguno de ellos da error. El puntero retornado en p es
indefinido y la dirección a la que apunte no está reservada. No retorna NULL como podríamos
imaginar en un principio, del mismo modo quedelete no modifica el puntero, sino simplemente
libera la memoria.
9.12) Intentar borrar sólo una parte del vector reservado es una barbaridad, no porque sea ilógico
pensarlo, sino porque el C++ no lo detecta como error y dependiendo de la implementación, puede ser
que no ocurra nada o se convierta en un desastre. Lo único que sabemos con seguridad es que si hacemos
lo correcto, no tendremos ningún problema.

Ejemplo 1 de impresión de texto por pantalla en C++

#include "iostream"

using namespace std;

int main()
{
//Se muestra un mensaje por pantalla.
cout << "Hola Mundo" << " Desde AAP." << endl;

return 0;
}

El ejemplo que acabas de ver es bastante sencillo,


excepto por un detalle. Si ejecutar este código en tu
pantalla aparecerá un mensaje diciendo "Hola
Mundo Desde AAP.". Tal como habrás notado el
operador <<se usa para concatenar (unir) dos
cadenas de texto por eso el resultado es el obtenido.
Luego se usa endlindicando el final de la línea lo
cual crea un salto de línea. No es obligatorio pero es
recomendable.
Ejemplo 2 de impresión de texto por pantalla en C++

#include "iostream"
#include "string"

using namespace std;

int main()
{
string salida1 = "Ejemplo de salida"; //El valor de esta variable se mostrará en
pantalla
int numero = 2; //Este valor también se mostrará en pantalla.
string salida2 = "Desde AAP."; //Estos valores se concatenarán en una única salida

//Se concatenan y muestran los valores por pantalla con cout<<


cout << salida1 << " " << numero << ". " << salida2 << endl;

return 0;
}
En este ejemplo de salida por pantalla hemos visto
que también es posible usar la instrucción cout para
mostrar en pantalla el valor de las variables así sean
numéricas o cadenas de texto. También vimos que
podemos concatenar los valores de esas variables
entre sí y también concatenarlas con otros valores
directamente (espacios, puntos, símbolos, etc.).
Ya tenemos claro cómo mostrar texto por pantalla en
C++ ahora haremos uso de este concepto y veremos
cómo leer texto por teclado en C++. Veamos:
Entrada o lectura de datos en C++
Tal como mencioné hace un momento, la lectura de
datos en C++ es bastante simple. Leer datos por
teclado en C++ se hace usando el comando cin
>> es importante notar el uso de los dos
signos >> que son usados para controlar el flujo de
datos. No te preocupes mucho por ellos, solo ten en
cuenta que cada vez que vaya a usar la
instrucción cin debes agregarle >> para quedar con
un cin>>. Una manera muy sencilla de recordar esta
instrucción es que in significa entrar y como estamos
programando en C++ le añadimos la letra C al
comienzo quedando así cin>> (sin olvidar los >>).
Veamos unos ejemplos simples para leer datos en
C++. Recuerda como dije más arriba que lo ideal
para leer datos es indicarle al usuario qué es lo que
esperamos que ingrese por lo que en estos ejemplos
usaremos también lo recién aprendido (mostrar texto
por pantalla).
Ejemplo 1 de lectura de datos en C++
#include "iostream"
#include "string"

using namespace std;

int main()
{
cout << "Hola! Este es un ejemplo en C++" << "\n" << "Por favor ingrese su
nombre:" << endl; //La instrucción \n es un salto de línea Mostrando los textos
separados

string nombre;//En esta variable estará almacenado el nombre ingresado.


cin >> nombre; //Se lee el nombre

cout << "Bienvenido al sistema " << nombre << ". Gracias por usar nuestra
aplicación" << endl;

return 0;
}

En este ejemplo hemos hecho un pequeño sistema


de bienvenida personalizado para el usuario leyendo
el valor por teclado de su nombre. En el
comando cin>> se debe poner, después de >>, el
nombre de la variable en la cual se almacenará el
valor que el usuario ingrese. Por tal motivo primero
se declaró primero una variable llamada nombre y
luego se uso cin >> nombre indicando que lo que l
usuario ingrese se almacenará en dicha variable.
Ejemplo 2 de lectura de datos en C++

#include "iostream"
#include "string"

using namespace std;

int main()
{
cout << "Hola! Aqui podras realizar sumas" << endl;//Mensaje de bienvenida
float numero1, numero2; //Se declaran los números que se sumarán (pueden ser
decimales)

cout << "Por favor ingrese el primer valor: " << endl; //Se pide el primer numero
cin >> numero1; //Se asigna el primer valor a numero1
cout << "Por favor ingrese el segundo valor: " << endl; //Se pide el segundo numero
cin >> numero2; //Se asigna el segundo valor a numero2

cout << "El resultado de la suma de " << numero1 << " + " << numero2 << " es: " <<
numero1+numero2 << endl; //Se muestra el resultado.

return 0;
}

Muy bien eso ha sido todo en este contenido. Espero


que te haya sido de muchísima utilidad. Si has
comprendido todo a la perfección y no tienes
problemas ni dudas, puedes continuar con la sección
de Condicionales en C++ o si ya sabes
condicionales puedes seguir con Ciclos en
C++. Por otro lado si tienes algún problema, duda,
comentario y/o sugerencia la atenderé con gusto si
la publicas en la sección de comentarios. Nos
leemos.

También podría gustarte