Guía básica de programación en C++ estructurado Manejo básico de la parte estructurada de C++ 1 Introducción Durante el curso, se empleará el lenguaje de programación C++. Este lenguaje de programación cuenta con muchas posibilidades, ya que además de definir sus propias extensiones, incluye una variante de C más exigente con el cumplimiento del sistema de tipos que el C original. Específicamente, no se utilizan las capacidades de C++ para programación orientada a objetos, sino sólo la parte que da soporte al paradigma de programación estructurada. C++ permite ocultar una buena parte de las características más complejas de C, mientras otras se pueden utilizar tal cuál se hacía en C. A continuación se enumeran las características más básicas de este híbrido entre C y C++. 2 Programa de ejemplo: ¡Hola, Mundo! El típico programa de ejemplo, ¡Hola, mundo!, se muestra a continuación. #include <cstdio> int main() { printf( "¡Hola, Mundo!" ); } 3 Entrada y Salida La entrada y salida se realiza mediante las funciones scanf() y printf(), respectivamente. Sólo es necesario tener en cuenta que para las cadenas, se debe utilizar gets(). A continuación, se discuten los principales tipos de datos. 3.1 Cadena de formato Las cadenas de formato, además de contener formatos para tipos, como se verá a continuación, pueden contener también códigos que simbolizan ciertos caracteres. Los más utilizados son \n, que simboliza un salto de línea, y \t, que representa un tabulador. 3.2 Enteros Los números enteros se formatean con el indicador “%d”. Este indicador de tipo puede llevar en su interior un número, que indica el ancho que se desea para él. Se utiliza esta posibilidad en el printf(), ya que en ese caso se imprimen el número de espacios que sea necesario, en caso de que el número mismo no tenga suficientes dígitos, para completar el ancho. Así, las instrucciones printf( “%5d\n”, 100 ); printf( “%5d\n”, 32 );, hacen que los números 100 y 32 aparezcan en líneas distintas pero correctamente tabulados. El primero, con dos espacios precediéndolo, y el segundo con tres. #include <cstdio> int main() { int edad; printf( "Dame tu edad: " ); scanf( "%d", &edad ); } printf( "Tu edad es: %3d\n", edad ); 3.3 Reales En C++, se pueden emplear dos tipos de números flotantes: float, de precisión simple, y double, de doble precisión. Como mínimo, el primero ofrece exactitud hasta el sexto decimal, y el segundo hasta el décimo. Los números reales se formatean con el indicador “%f”, ó “%lf” (este último para números flotantes de tipo double). Este indicador de tipo puede llevar en su interior un número, o dos números separados por un punto, que indican el ancho que se desea para él, y el número de decimales que se debe mostrar, respectivamente. Se utiliza esta posibilidad en el printf(), ya que en ese caso se imprimen el número de espacios que sea necesario, en caso de que el número mismo no tenga suficientes dígitos, para completar el ancho. Así, las instrucciones printf( “%7.2f\n”, 100.456 ); printf( “%7.2f\n”, 32.789 );, hacen que los números 100.46 y 32.79 aparezcan en líneas distintas pero correctamente tabulados. El primero, con un espacio precediéndolo, y el segundo con dos. #include <cstdio> const float ValorEuroEnPesetas = 166.386; int main() { float cantidad; printf( "Dame una cantidad en euros: " ); scanf( "%f", &cantidad ); printf( "En pesetas es: %7.0f\n", cantidad * ValorEuroEnPesetas ); } 3.4 Cadenas Las cadenas se piden mediante la función gets(), y se muestran mediante la función printf(). Si se desea, se puede utilizar también la función scanf(), pero es necesario tener en cuenta que ante la aparición de un espacio, esta función dejar de leer la entrada retorna el control al programa. Además, en caso de utilizar scanf(), NO se debe poner un ampersand delante del nombre de la cadena de caracteres. #include <cstdio> int main() { char nombre[128]; printf( "Dame tu nombre: " ); gets( nombre ); } printf( "Hola, %s\n", nombre ); 4 Manejo de cadenas de caracteres Las cadenas de caracteres se manejan al igual que se manejaban en C, mediante vectores de caracteres. En estos vectores de caracteres, un carácter adicional de valor 0 (muchas veces indicado como '\0') marca el final de la cadena. Existe un módulo de C++, llamada cstring, que proporciona varias funciones para manejar cadenas. Así mismo, otra librería de C++, cstdlib, proporciona las conversiones de cadena a número y viceversa. 4.1 Asignar un valor a la cadena de caracteres Los valores se asignan mediante la función strcpy(), que admite dos parámetros: el primero el vector de caracteres de destino, y el segundo la cadena a insertar en el mismo, que puede ser otro vector de caracteres o un literal de cadena. En cualquier caso, el vector de caracteres receptor debe tener suficiente tamaño para recibir la cadena y el cero de fin de cadena. #include <cstdio> #include <cstring> int main() { char s[128]; strcpy( s, "esto es una prueba" ); printf( "%s\n", s); } 4.2 Concatenar dos cadenas Dos vectores de caracteres se pueden concatenar mediante la función strcat(), que admite dos parámetros. El primero es el primer vector de caracteres, que actuará también como destino. El segundo es un literal de cadena u otro vector de caracteres, que se copiará a continuación del último carácter ocupado en el primer vector de caracteres. Nótese que debe haber suficiente espacio en el primer vector de caracteres para su propia contenido concatenado con la segunda cadena y el cero de fin de cadena. #include <cstdio> #include <cstring> int main() { char s[128]; strcpy( s, "esto es una prueba" ); strcat( s, ", y esta es otra" ); printf( "%s\n", s); } 4.3 Obtener el tamaño de una cadena La cuenta de número de caracteres de una cadena se obtiene mediante la función strlen(), que admite un sólo parámetro, un vector de caracteres o cadena literal, y devuelve el tamaño sin contar el cero de fin de cadena. Así, printf( “%d\n”, strlen( “Hola” ) );, mostraría el valor 4 por pantalla. 4.4 Pasar el contenido de una cadena a mayúsculas o minúsculas No existe una función que permita convertir una cadena a mayúsculas o minúsculas, aunque las funciones toupper() y tolower(), definidas en el módulo cctype, permiten construir fácilmente sendas funciones. #include <cstdio> #include <cstring> #include <cctype> void strtoupper(char s[]); void strtolower(char s[]); int main() { char s1[128]; char s2[128]; strcpy( s1, "Hola" ); strcpy( s2, "Mundo" ); strtoupper( s1 ); strtolower( s2 ); printf( "%s, %s\n", s1, s2 ); } void strtoupper(char s[]) { int i; for(i = 0; i < strlen( s ); ++i) { s[ i ] = toupper( s[ i ] ); } } void strtolower(char s[]) { int i; for(i = 0; i < strlen( s ); ++i) { s[ i ] = tolower( s[ i ] ); } } 4.5 Vaciar de contenido una cadena El fin de cadena en C++ se marca con un carácter que tiene valor 0. Si se desea que una cadena pase a estar vacía, basta con poner la marca de fin de cadena en su primera posición. Por ejemplo, dado un vector de caracteres s, la instrucción s[ 0 ] = '\0'; ó s[ 0 ] = 0; hace que el contenido de s sea la cadena vacía. 4.6 Pasar una cadena a un número Para pasar una cadena a un número, debe usarse la función atoi(), en el módulo cstdlib. Admite un solo parámetro, el vector de caracteres a convertir. En caso de que el contenido de la cadena no pueda convertirse a un número, devuelve cero. Un ejemplo de uso se muestra a continuación. include <cstdio> #include <cstdlib> int main() { printf( "%d\n", atoi( "54" ) ); } 4.7 Pasar un número a cadena Para hacer la conversión inversa al punto anterior, se utiliza la función sprintf(), que funciona de manera análoga a printf(), pero formatea los datos a una cadena de caracteres, en lugar de la pantalla. Por ello, el primer argumento de sprintf() es el vector de caracteres de destino, mientras el resto de parámetros permanecen inalterados. #include <cstdio> #include <cstdlib> int main() { int numero; char s[128]; printf( "Dame un número: " ); scanf( "%d", &numero ); } sprintf( s, "%d", numero ); printf( "El número era %s\n", s ); 5 Paso de parámetros C++ permite dos tipos de paso de parámetros: por valor (también denominado por copia, en terminología de C++), y por referencia. 5.1 Paso de parámetros por referencia El paso de parámetros por referencia se utiliza cuando se le quiere pasar una variable a una función o procedimiento, de manera que los cambios que se le hagan a esa variable dentro de la función sean repercutidos después en el resto del programa. #include <cstdio> void sqr(float &x); int main() { float numero; printf( "Dame un número: " ); scanf( "%f", &numero ); sqr( numero ); } printf( "El cuadrado es %0.0f\n", numero ); void sqr(float &x) { x = x * x; } 5.2 Paso de parámetros por valor El paso de parámetros por valor se utiliza cuando se le quiere símplemente pasar un dato a una función o procedimiento, sin más repercursiones en el resto del programa. El paso de parámetros por valor admite dos variantes, que es necesario utilizar en función del tipo de dato del parámetro que se quiera pasar. Para los tipos de datos simples, esto es, Entero y Real, el paso de parámetros por valor consiste básicamente en poner el nombre del tipo y el nombre del parámetro formal a continuación. Para su invocación, se indica el nombre de la variable o un valor literal. #include <cstdio> double sqr(double); int main() { float numero; printf( "Dame un número: " ); scanf( "%f", &numero ); } printf( "El cuadrado es %0.0f\n", sqr( numero ) ); double sqr(double x) { return x * x; } 5.2.1 Paso de vectores y matrices Los vectores y matrices, siempre se pasan por referencia, aunque no se indique explícitamente con la sintaxis comentada anteriormente. Si se desea indicar que el vector no va a cambiar durante la función, se debe poner el modificador const delante del parámetro formal del vector. #include <cstdio> int strlen(const char s[]); int main() { char nombre[128]; printf( "Dame tu nombre: " ); gets( nombre ); printf( "Tu nombre tiene %d letras.\n", strlen( nombre ) ); } int strlen(const char s[]) { int i = 0; while( s[ i ] != 0 ) { ++i; } } return i; 5.2.2 Paso por referencia constante Sin embargo, este paso de parámetros no es suficiente cuando se tratan tipos de datos grandes. Por ejemplo, una estructura con varios campos se podría pasar por valor tal y como se ha mostrado hasta ahora, pero copiar la longitud entera de la misma con cada paso por valor supondría una penalización demasiado grande. Así, existe la posibilidad de utilizar el paso por referencia constante, de manera que se aúnan las ventajas del paso por referencia (es muy rápido), y del paso por valor (la variable no se puede modificar). A continuación se muestra un ejemplo. #include <cstdio> #include <cstring> const int Max = 128; typedef struct { char nombre[Max]; char apellidos[Max]; int edad; } Persona; void mostrarPersona(const Persona &x); void inicializarPersona(Persona &p, const char nombre[], const char apellidos[], int edad); int main() { Persona p1; } inicializarPersona( p1, "Perico", "Palotes", 18 ); mostrarPersona( p1 ); void inicializarPersona(Persona &p, const char nombre[], const char apellidos[]$ { strcpy( p.nombre, nombre ); strcpy( p.apellidos, apellidos ); p.edad = edad; } void mostrarPersona(const Persona &p) { printf( "%s, %s: %d\n", p.apellidos, p.nombre, p.edad ); }