Académique Documents
Professionnel Documents
Culture Documents
Programación
en C
Fundamentos para
ingeniería
i
Luego, en el capítulo 5 veremos las funciones y su importancia
en el lenguaje C. Mientras tanto, en el capítulo 6 estaremos
aprendiendo algunas de las estructuras de datos más
importantes. El capítulo 7 se enfoca a uno de los temas que
son casi exclusivos del lenguage C: apuntadores, con todo el
poder que implican. Y finalmente, en el capítulo 8 veremos la
manera de leer datos y escribirlos desde y hacia
almacenamiento secundario.
Junio 2012
Gildardo Sánchez
ii
Introducción
1
En este capítulo se
describe brevemente
algunos conceptos del
sistema operativo Linux y
se explica cómo instalarlo
en una máquina virtual.
Finalmente se instala un
ambiente de desarrollo
(IDE) para compilar y
ejecutar un primer
programa en C.
Sección 1
1. Sistema operativo. Veamos con un poco de más detalle qué significan cada uno de ellos.
4. ¡Hola mundo! La parte medular del sistema operativo es la que llamamos comunmente
núcleo o kernel del mismo. Sobre de éste se pueden construir los
sistemas que se encargan del manejo de archivos, los navegadores y
por supuesto cualquier otra aplicación que pueda ser ejecutada en
nuestro sistema de cómputo.
4
Hay una gran variedad de sistemas operativos, y cada uno de ellos tiene
sus características. Los hay mono usuario (sólo dan servicio a un
Compiladores
usuario a la vez), los hay multi usuario (varios usuarios compartiendo el El compilador es un programa que se encarga de traducir los programas
uso de los mismos recursos de hardware), de costo o gratuitos, escritos por el programador en lenguaje de alto nivel (entendible por el
compactos o muy extensos, etc. ser humano) a un lenguaje de bajo nivel que es el que la máquina
entiende y que, de esta manera, permite que pueda ser ejecutado por la
En este libro haremos énfasis en la utilización de un sistema operativo computadora.
gratuito, multiusuario y de libre acceso a su código fuente. Se trata de
LINUX, quien a su vez está basado en otro sistema operativo llamado
Ambientes De Desarrollo
UNIX.
Cuando se programa, es necesario disponer de una aplicación dónde
Hay varias razones para recomendar el uso de este sistema operativo, y escribir y probar nuestro código. Dicha aplicación puede ser una muy
la realidad es que la combinación de ellas nos pone en una simple llamada “Editor”, o puede ser algo mucho más elaborado que
circunstancia muy apropiada. Mencionaré aquí algunas de ellas: Es suele ser llamado de manera genérica “Ambiente Integrado de
gratuito, lo que significa que todos podemos usarlo legalmente sin tener Desarrollo”, o casi siempre por sus siglas en inglés: IDE (Integrated
que hacer grandes desembolsos de dinero. Es mantenido de manera Development Environment). Veremos que éstos últimos integran no solo
colaborativa por desarrolladores de todo el mundo, todo el tiempo. Esto al editor, sino componentes para ayudarnos posteriormente a probar
hace que sea un sistema muy actualizado, con tiempos de respuesta nuestro código y muchas veces contemplan funcionalidades que nos
que suelen ser muy rápidos cuando hay que modificar o añadir alguna ayudan a completar porciones del código, etc.
característica. Es extremadamente robusto y es una plataforma muy
En realidad cuál de estos usemos depende mucho de con cuál nos
utilizada en varios sectores de la industria, por lo que estaremos
sintamos más cómodos. Para propósitos de este libro, haré mención de
preparados para enfrentar problemas reales sin tantos tropiezos.
algunos de los que existen a la fecha, preferentemente gratuitos. Usted
Por supuesto, si existen otros sistemas operativos es por algo. Así que tendrá por tanto la libertad de elegir cuál desea emplear.
no pretendo decir que LINUX sea ni el único ni el mejor. No se puede
Enseguida te menciono algunos de los ambientes de desarrollo mas
hacer una aseveración como ésta última sin dar contextos. Por tanto,
comunes a la fecha.
diremos que para el tipo de aplicaciones a las que estamos orientando
este libro, LINUX resulta simplemente muy apropiado.
5
El Entorno está desarrollado en el lenguaje Delphi de Borland. Tiene
una página de paquetes opcionales para instalar, con diferentes
bibliotecas de código abierto.
6
Entre otros. En este sitio puedes encontrar una buena descripción de 10 Hay varias aplicaciones que permiten crear
diferentes IDEs. máquinas virtuales, quizá las dos más conocidas
son: VMWare y VirtualBox. La primera es de costo, y
Instalando Linux la puedes encontrar aquí. La segunda es gratuita, y
la puedes descargar de este sitio.
En esta sección veremos cómo podemos instalar una versión de Linux
en nuestras computadoras, para poder trabajar con C. En realidad La imagen mostrada más abajo fue tomada en una
podríamos usar cualquier sistema operativo que tenga tu computadora computadora Apple Macintosh corriendo Snow
ya instalado, aunque por las razones mencionadas con anterioridad Leopard como sistema operativo nativo y con una máquina virtual de
nosotros partiremos de la idea de que Linux será nuestra base. VirtualBox corriendo Ubuntu 12.04.
Lo primero que quisiera mencionar es que hay dos maneras (tres en Para propósitos de explicar la instalación de la máquina virtual usaré
realidad, pero no usaremos la tercera) de instalar Linux en tu VirtualBox y supondré que partimos de una computadora con Windows
computadora. En la primera o borramos por completo el sistema como sistema operativo nativo.
operativo que tu computadora tenga ahora, o creamos una partición
nueva en el disco duro e instalamos en ella nuestra versión de Linux. En
la segunda, creamos una máquina virtual y trabajamos sobre ella.
Desde el punto de vista de lo que hay que hacer no hay mucha
diferencia entre una y otra. Aquí me limitaré a explicar cómo hacer una
máquina virtual y luego a instalarle Ubuntu Linux.
7
Instalando La Máquina Virtual Si todo salió bien, tenemos instalada una máquina virtual con Ubuntu
Linux ya funcionando.
El siguiente video, producido por Gilberto Fuentes Díaz, y que puede Instalando “Guest Additions”
encontrarse en este enlace de YouTube, describe de manera muy
puntual cómo hacer la instalación.
Ahora bien, para que tu máquina virtual funcione mejor (en cuanto a su
rendimiento), es conveniente instalar los “Guest Additions” de
P ELÍCULA 1.1 Instalación de Virtual Box VirtualBox. Se trata de un conjunto de programas y drivers que harán
más rápida tu máquina virtual.
8
Preparando Un Editor/IDE Y El Compilador. escribimos gcc. Si el compilador está instalado obtendremos algo como
lo siguiente:
Damos clic en OK. Y veremos la interfaz del IDE ya lista para que
programemos.
Probemos ahora si nuestro IDE quedó correctamente instalado. Para
ello, escribimos en nuestra terminal: codeblocks y damos ENTER.
Veremos algo como lo que muestra la siguiente imagen.
¡Hola Mundo!
10
Grabamos el archivo, con el nombre de holamundo.c
#include<stdio.h>
int main(void)
printf("Hola Mundo!");
11
#include<stdio.h>
int main(void)
printf("Hola Mundo!");
return 0;
Luego ponemos:
./holamundo
Otra manera de hacer esto mismo es a través de un editor de texto y
compilando en línea de comandos. Si todo funciona bien, veremos una pantalla como la siguiente. Note
cómo en la terminal aparece en la última línea el texto “Hola Mundo!”.
En la terminal, escribimos gedit. Nos aparecerá una nueva ventana Es decir, hemos escrito y compilado nuestro primer código en C usando
con un editor de texto. el editor de texto y la línea de comandos en la terminal. ¡Felicidades!
Escribimos en él:
12
13
Sección 2
4. Instalación de aplicaciones en Unix. Hablemos un poco de porqué Unix/Linux son importantes en nuestro
contexto y cómo lo podremos usar.
5. Ejercicios
LInux está basado en UNIX, y como tal, se caracteriza por ser un
ambiente propio para el desarrollo de programas. En palabras del
mismo Thompson, UNIX es un sistema operativo diseñado por
programadores para programadores. Las principales características de
diseño de UNIX fueron:
14
• Combinar programas ya existentes para realizar trabajos complejos. Entrada/Salida Del Sistema (login/logout)
• Permitir a múltiples usuarios realizar múltiples tareas al mismo
tiempo. login permite el acceso al sistema. Normalmente, login es un
proceso que corre en cualquier terminal en cuanto se enciende. Por
Por tal motivo, la estructura básica de UNIX está conformada por varios tanto, hay que proporcionar el nombre de la cuenta a la que se desea
niveles, que van desde el control de acceso al hardware, dado por el ingresar (al nombre se le llama logon). El sistema entonces solicitará la
kernel del sistema operativo, hasta las aplicaciones de usuario, pasando clave de acceso (keyword o password). Una vez proporcionada ésta, se
por el shell del sistema operativo. Esta idea se ilustra en la siguiente estará dentro del sistema, posicionado en el directorio de trabajo del
gráfica. usuario.
15
Listado De Archivos (ls)
! u: Propietario
Esta otra imagen muestra un listado “largo”, donde puede verse más
! g: Grupo.
información de cada archivo.
! o: Otros.
16
Los permisos pueden ser:
! r: Lectura.
! w: Escritura.
! x: Ejecución.
17
Otra salida de ese comando, cuando hay más de un usuario conectado
a un sistema:
Por ejemplo un man ls produciría algo como esto: ! Para cambiarse de un directorio a otro, se usa el comando cd. Es
similar al homónimo de DOS, con la diferencia de que en UNIX se
emplea la diagonal normal (/). En Linux dos puntos seguidos son una
abreviatura del directorio padre de aquél en que nos encontremos en un
momento dado. Así,
18
$cd ..
Por ejemplo:
Nos mandará al directorio inmediato anterior al actual. Otra abreviatura
es la tilde, que representa el directorio base (también llamado home) de chmod go +w ejemplo1.txt
cada usuario.
habilita el archivo ejemplo1.txt para escritura para el grupo y todos
La imagen siguiente muestra una serie de aplicaciones de pwd, y cd. los demás usuarios (g y o).
19
Eliminación De Directorios (rmdir) Para Saber Más
21
a) Las derivadas de la original AT&T, desde las versiones1-7 a los
sistemas III-V. Ejercicios
b) M i c r o s o f t / S C O X E N I X .
1. Estudiar el formato largo de salida del comando ls. Interpretar las
Versión desarrollada entre 1978 y 1981 para la familia Intel 8088/6. siguientes líneas de salida:
Aunque hubo versiones para el Motorola 68000 y el Zilog Z8000, luego
del auge de las PC's fueron prácticamente olvidadas. Entre las
adiciones que hizo XENIX al UNIX están el cerrar archivos a nivel de
registros y la utilización de semáforos para la sincronización de
procesos. Ultimamente se ha movido el Sistema V de AT&T a
plataformas PC.
3. Sistema de archivos.
22
b) Trasladarse al directorio local, debajo del directorio usr, debajo del
directorio raíz. Mostrar el directorio actual; mostrar los archivos
contenidos en él.
4. Usuarios.
5. Manual.
23
Sección 3
Introducción a la Algoritmos
Al final de cuentas una computadora no es más que una máquina
24
Para ilustrar un poco esto, hablemos de una anécdota sobre el gran Suma= (50*100) + 50 = 5050
matemático Gauss, de quien estaremos hablando con frecuencia en
este texto. Hay al menos otras dos maneras de resolver este problema. La que
quizá la mayoría de nosotros pensaríamos es justamente el sumar todos
Se dice que siendo muy joven (quizá unos 10 años), su profesor de esos enteros, uno tras otro. Una más sería haciendo uso de la siguiente
aritmética pidió al grupo en que se encontraba Gauss que calcularan la fórmula que nos da el resultado:
suma de los 100 primeros enteros, quizá pensando que tendría un poco
de tiempo para comerse su manzana. Sin embargo, no había Donde n es el número entero hasta el cual deseamos hacer la suma, en
transcurrido mucho desde que el profesor solicitara el cálculo cuando este caso n = 100.
Gauss le dió la respuesta: 5050. La manera en que Gauss pudo hacerlo
También hablaremos un poco más de fórmulas como esta. Algunas de
fue la siguiente.
ellas son muy importantes por una sencilla razón: nos harán desarrollar
Si colocáramos los números en secuencia: códigos más eficientes. Para ejemplificarlo pensemos en el caso del
método dos, en el que iríamos sumando los enteros. Si lo analizamos,
0 resulta que nuestro programa tendría que realizar tantas sumas como
enteros haya que sumar, 100 en este caso. Cualquiera que sea el
1 tiempo que la computadora tarde en sumar un par de números,
deberemos multiplicarlo por n. Pensemos ahora en la fórmula. Ahí tengo
2
una suma, una multiplicación y una división, es decir 3 operaciones, no
... 100. ¡Eso seguro nos ahorrará tiempo de cómputo!
98
Algoritmos y pseudocódigo
99 Una vez que hemos planteado la solución del problema con un
algoritmo, resulta conveniente escribirlo usando una notación a la que
100 llamaremos pseudocódigo, porque no es código de algún lenguaje
particular, pero sí debe ser suficientemente claro como para que pueda
Podremos ver que si vamos tomando los extremos y los sumamos,
aplicarse la definición de algoritmo. El pseudocódigo puede incluso
obtenemos siempre como resultado 100. Es decir, (0 + 100) = 100, (1 +
hacer uso de un lenguaje natural, como el español o el inglés.
99) = 100 y así sucesivamente. Habrá 50 de estos pares de sumas y
nos sobrarán un número 50. Por tanto el total se puede calcular
multiplicando 100 por 50 y sumándole 50.
25
Por ejemplo, para el caso de la fórmula que acabamos de describir, un
pseudocódigo que la calcule pudiera ser como el siguiente:
Ejercicios
A fin de practicar lo que acabamos de mencionar sobre algoritmos y
1.Pide el valor de n. pseudocódigos, considere los siguientes ejercicios:
Estas tres líneas podrán traducirse con facilidad a cualquier lenguaje de b. Cambiar una llanta ponchada.
programación, así que el trabajo que hemos desarrollado hasta ahora es
igualmente valioso independientemente del lenguaje de aplicación. c. Comprar un café.
Por cierto, es importante mencionar que en este texto siempre 2. Determine y describa en pseudocódigo un procedimiento para
seguiremos los pasos que se enumeran enseguida para resolver calcular cuánto dinero se tiene en una caja registradora que contiene
problemas: n pesos, m monedas de 5 pesos, l monedas de 10 pesos y k
monedas de 50 centavos.
1. Identificar cuáles son los datos que se requieren para calcular.
3. Si tuviéramos una lista desordenada con apellidos de personas,
2. Diseñar los pasos que permitan realizar los cálculos necesarios para diseñe un algoritmo y descríbalo en pseudocódigo para encontrar el
llegar al resultado primer sitio de la lista en que aparezca el apellido “LOPEZ”.
3. Imprimir o almacenar los resultados para el usuario. 4. Determine y describa en pseudocódigo un procedimiento para que
dados cuatro números enteros, estos sean impresos de menor a
El paso dos es escencialmente el diseño del algoritmo y su
mayor.
pseudocódigo. En el caso que hemos usado ahora para ilustrar, el
pseudocódigo es muy sencillo, pero en problemas más complejos será
más elaborado y nos ayudará mucho a evitar cometer errores de
programación que luego son más difíciles de encontrar y corregir, asi
que trabajaremos para hacer el hábito de diseñar siempre el
pseudocódigo.
26
2. Solo se permiten otras letras, números o guiones bajos posteriores a
Estructura de un Programa en C la primera letra. No se permiten espacios en blanco.
Funciones 3. No se pueden usar las palabras clave (keywords) de C. Se muestran
El lenguaje C se basa en el concepto de construcción con módulos. en la siguiente tabla.
Cada uno de esos módulos está encargado de realizar una tarea
específica. En realidad, los módulos son subprogramas y como tal, debe
hacer lo que hacen todos los programas: recibir datos, procesarlos y
generar un resultado. Estos módulos se llaman funciones. Por tanto, un
programa en C puede ser visto como una colección de una o más
funciones. Cada función tiene un nombre y una lista de argumentos que
recibirá. En general, se puede usar cualquier nombre, excepto main que
está reservado para la función que inicia la ejecución. De hecho,
main() es la única función que siempre debe existir en un programa en
C. Esta función debe contener la esencia de lo que hace el programa y
puede hacer llamadas a otras funciones que realicen ciertas tareas
específicas. Dichas funciones pueden estar en el mismo archivo que
main() o bien, pueden estar en archivos diferentes. 4. El número máximo de caracteres que la computadora alcanza a
reconocer en el nombre de la función depende de la computadora. El
estándar ANSI de C exige que sean al menos 31 caracteres.
DATOS FUNCION RESULTADOS
5. Todos los nombres de funciones son seguidos por paréntesis.
27
Hay una convención que nos invita a usar minúsculas en los nombres En ese caso, tenemos que main() llama a tres funciones, primero a la
de las funciones, aunque el lenguaje no lo exige así. Luego veremos función A, cuando esta termina regresa su resultado a la función que la
que respetar ciertas convenciones hará que nuestros códigos sean más llamó (main() ) y luego sigue la función B que hace lo mismo y
fáciles de leer por otras personas. finalmente la función C. Por supuesto, puede darse el caso en que una
de esas funciones a su vez llame a otra:
Estructura De Un Programa En C main()
Decíamos que todo programa en C al menos tendrá un módulo, que es
el módulo principal (main). En el caso de un problema simple, quizá con
Función A
eso baste para resolver el problema, pero en la mayoría de los casos el
problema será resuelto usando la función principal main mas otras
Función C
funciones, sea que estas sean provistas por un tercero o sea que las
tengamos que desarrollar nosotros mismos.
Cualquiera que sea el caso, el main funciona como una especie de Función B
director de orquesta, que es el que le pide a cada uno de los
instrumentos que intervengan en un momento en particular.
En este otro caso, main llama primero a la función A. Esta a su vez
La idea se ilustra de la siguiente forma: require a la función C, cuando C termina, le regresa el resultado a A.
main() Cuando A termina, lo regresa a main, quien a su vez tiene entonces la
posibilidad de llamar a la función B. Veremos que en la práctica puede
Función A
haber muchas llamadas a funciones en un programa complejo.
Función C
28
declaración de variables globales En el diagrama aparecen varias cosas que aún no estudiamos, como las
declaraciones de variables. Otra cosa que quizá hayas notado es la
main() siguiente línea:
{! /* indica el inicio de la función */ /* indica el inicio de la funcion */
! declaración de variables locales Esa es en realidad la sintaxis para escribir comentarios en nuestros
códigos. Un comentario es algo que ayuda a los humanos a entender
! instrucciones ...
con mayor facilidad de qué se trata el código. Es una buena práctica
} “comentariar” nuestros códigos.
! instrucciones ... Note que /* */ nos permiten escribir comentarios de múltiples líneas.
También es posible usar // para comentarios de una sola línea:
}!
// Este es un comentario simple
función_n(lista de argumentos)
La comunicación entre funciones se realiza mediante el paso de
declaración de lista de argumentos parámetros, por lo que todas las funciones en C llevan cero o más
argumentos (un argumento es un valor que se pasa a la función)
{
separados por comas.
! variables locales
Cuando se define una función con uno o más parámetros, estos deben
! instrucciones ser declarados para especificar qué tipo de variables recibirán cada uno
de ellos. A estos argumentos de les denomina parámetros formales de
}! la función.
29
Las funciones en C se denominan así debido a que regresan un solo archivo stdio.h mediante la directiva de precompilación #include
valor a la función que las llamó, por medio de la instrucción return. <stdio.h>. La sintaxis de la función printf es la siguiente:
{
Tipo de dato Especificador Comentario
! Cuerpo de la función
Entero %d o %i Cualquier entero
} Flotante %f Número real
En este caso, la función se llama nombre_funcion, recibirá como datos Núm. Real de precisión
Doble %lf
lo que quede en “lista de argumentos” y regresará un resultado que será doble
de un cierto tipo. Cadena %s String
Caracter %c
Entradas Y Salidas En C
Notación
Hay dos elementos mas que es importante mencionar en este momento, %e o %E
Científica
antes de que comencemos a trabajar más en detalle en el lenguaje C.
Esos dos elementos son los que nos permitirán recibir datos y los que
nos permitirán imprimir los resultados.
30
Frecuentemente se combinan varias fases de compilación dentro de un ! b) Procesar las directivas de preprocesamiento. Estas directivas
paso de compilación. Regularmente, un paso involucra la lectura de un siempre empiezan con un carácter #. Entre las más comunes están:
archivo de entrada y la generación de un archivo de salida.
! #define: Crea macros.
Compilador:
! a) Análisis Léxico.
! b) Parser.
Toma como entrada el código fuente de un programa en C (Archivo Después de las dos primeras fases, se genera un código intermedio. El
texto ASCII). En este paso se realizan dos funciones: optimizador toma ese código e intenta que éste utilice menos espacio y
sea más rápido en la ejecución. Generalmente los compiladores no
! a) Eliminar los comentarios del código fuente. pueden tener ambas cosas, por lo que se debe escoger entre menor
31
espacio y mayor rapidez o viceversa. La fase de generación de código
traduce de código intermedio a lenguaje ensamblador.
Compilación de Programas en UNIX.
! La compilación depende del sistema al que se tenga acceso,
Ensamblador: aunque no hay diferencias muy grandes entre uno y otro. En este caso
se explica la instrucción para compilar en AIX.
Toma como entrada el programa en lenguaje ensamblador y lo traduce a
un lenguaje de máquina muy parecido al código ejecutable. Algunas ! Para compilar se usa:
veces se incorpora el ensamblador a la fase de generación de código,
de tal forma que se crea un archivo en lenguaje ensamblador. ! ! gcc archivo.c
La salida del ensamblador es un módulo objeto relocalizable. Los ! Este comando genera un archivo ejecutable llamado a.out. Si se
programas en C usualmente se escriben como varios archivos fuente deseara cambiar el nombre, habrá que usar:
que son compilados por separado y unidos posteriormente en una
gcc archivo.c -o ejecutable.exe
última etapa del proceso de compilación, mediante el ligador (Linker).
! En caso de requerir ligar con alguna biblioteca u otro programa, se
Una función puede llamar a otra que se encuentre localizada en un
emplea:
archivo diferente. Lo mismo sucede con las variables globales (variables
que deben ser conocidas por todas las funciones del programa). Este ! gcc archivo.c -o ejecutable.exe -l biblioteca
conjunto de objetos son llamados objetos externos. El ensamblador
traduce lo que puede a lenguaje de máquina, pero como aún no sabe Características del Lenguaje C.
dónde se ubicarán los objetos externos en la memoria, entonces
Muchas veces se ha clasificado a C como un lenguaje de nivel medio, lo
reemplaza todas sus referencias por alguna marca y crea una tabla con
cual se debe a que reúne características de un lenguaje de bajo nivel
esas direcciones y se las pasa al ligador.
(como ensamblador) con otras propias de lenguajes de alto nivel. C fué
Ligador (linker): desarrollado como un lenguaje para la programación de sistemas, por lo
cual posee características que lo han hecho muy útil en esas tareas. Por
El ligador toma la tabla de los objetos relocalizables y los ubica para ejemplo:
formar un sólo código ejecutable reemplazando todas las direcciones
marcadas con direcciones reales al conjuntar todos los objetos que
forman el sistema.
32
• En C realmente hay más operadores y combinaciones de operadores cercano al hardware hace que la velocidad de ejecución se aproxime
que palabras clave. C tiene menos reglas de sintaxis que muchos a la de sus equivalentes en lenguaje ensamblador.
otros lenguajes.
• C es un lenguaje con una alta portabilidad. Esto quiere decir que es
• C es un lenguaje extremadamente reducido. El lenguaje original fácil trasladar un programa en C desde una plataforma a otra. Esta
solamente contempla 27 palabras clave. El estándar ANSI de C tiene característica está muy relacionada con el manejo de bibliotecas. De
unas 32 palabras clave. A pesar de que C no incluye muchas de las hecho, la responsabilidad de que las funciones definidas en cada
funciones comúnmente definidas para otros lenguajes, se ofrecen biblioteca estén disponibles para una plataforma dada, dependen del
bibliotecas de uso tan común que prácticamente forman parte del fabricante.
lenguaje. Sin embargo, una de las ventajas de C es que es fácil
modificar esas funciones.
33
Tipos de Datos
2
Cualquier lenguaje de
programación tiene que
leer datos, calcular con
ellos, tomar decisiones y
ofrecer respuestas.
Esa información se guarda
en diversos tipos de datos,
algunos variables, otros
constantes. Este capítulo
trata de ello.
Sección 1
Variables y Identificadores
Los identificadores son los nombres con los cuales se hace referencia a
Constantes una variable, constante o función. Los identificadores en C tienen las
siguientes características:
• Están formados por una secuencia de letras y/o dígitos que siempre
deben comenzar con una letra. Los siguientes caracteres pueden ser
letras, números o el carácter de subrayado.
Contenido
• Las letras mayúsculas y minúsculas son distintas cuando se utilizan
en una variable. Por ejemplo, la siguiente declaración define tres
1. Identificadores. variables distintas: int var, Var, VAR;
3. Tipo Caracter. • Se deben usar identificadores distinto para las variables y las
funciones.
3. Variables.
35
depende del tipo de computadora. En la siguiente tabla se muestran los
valores típicos para el caso de una computadora PC de 8 bits.
Tipo entero
Este tipo de datos se usa para representar números enteros con o sin
signo, que estarán compuestos por los dígitos del 0 al 9, pudiendo ser
precedidos por los signos + o -.
Así que para declarar una variable llamada dato1 como un entero, se
escribe lo siguiente:
int dato1;
Debe aclararse que en diversas versiones de C y C++ se definen otros
tipos (por ejemplo enum y void). Cuando sea conveniente se Sin embargo, en ocasiones además de declarar la variable vamos a
mencionarán. querer darle un valor inicial. A esto se se llama inicialización. La
inicialización es opcional. Si tenemos varias variables de un mismo tipo,
Para poder usar variables en C hay que declararlas. Eso es, avisarle al es posible definirlas todas en una misma línea de código escribiendo un
compilador qué variables vamos a usar en nuestro programa y de qué único int, separando el nombre de las variables por comas (“,”). Una
tipo de dato son. vez que se haya acabado de definir variables, se cierra la línea de
código con punto y coma (“;”),
Aunque C en realidad nos exige que lo hagamos antes de usar la
variable, por limpieza de nuestro código, trataremos siempre de agrupar Por ejemplo:
todas las declaraciones de variables al inicio del programa.
int edad = 24; /*declara e inicializa edad */
La declaración de variables se hace anteponiendo una palabra especial
que define al tipo, antes del identificador de la variable. int edad; /* declarara edad como entero */
36
Tipo real
Usado para variables que representan números reales (con decimales).
Por ejemplo:
float numero3;
double numero3; Una constante tipo char se representa como un solo carácter
encerrado entre comillas simples.
Tipo carácter
Por ejemplo:
Este tipo de datos se emplea para representar un carácter perteneciente
a un determinado código utilizado por la computadora (normalmente el char letra, letra2; /* dos variables de tipo char */
código ASCII o el código UNICODE). Ambas tablas se muestran
enseguida. char letra=’a’; /* declara y asigna el caracter a */
37
char identificador[cantidad] = “ mensaje ”;
char cadena[20];
Por otro lado, la siguiente línea además de apartar el espacio para los
20 caracteres, almacena ya una cadena con los caracteres de la frase
“Hola mundo”.
38
Palabra
Tipo de Dato Ejemplo
reservada
39
! c) Las conversiones solicitadas por el usuario, también
llamadas cast, obligan a que una expresión sea de un tipo específico. El
Variables locales:
formato general es: (tipo de dato) expresión, donde tipo de dato Las variables locales se declaran dentro de una función. Sólo pueden
indica el tipo específico deseado. ser usadas por las expresiones que hay dentro de esa función. En C las
variables locales son creadas cuando la función es llamada, y
! Por ejemplo: destruidas cuando se sale de la función. Igualmente, la memoria
utilizada para almacenarlas es creada y destruida dinámicamente. Por lo
! (float)x / 2; el resultado de esta división será de tipo anteriormente expuesto, hay que considerar que las variables locales
float, independientemente del tipo de x. pierden su valor al salir de la función.
Debe tenerse cuidado en que los tipos declarados para los parámetros
formales coincidan con los argumentos, pues de lo contrario puede
pasar cualquier cosa, ya que C no verifica esta correspondencia.
40
una variable diferente (aunque tenga el mismo nombre) y actuará sobe
Variables globales: la variable local siempre.
Las variables globales mantienen su valor a lo largo de todo el
programa mientras dura su ejecución. Este tipo de variables se crean Por ejemplo:
declarándolas fuera de toda función. Por ejemplo, en el siguiente
int main(void)
programa la variable resultado1 podrá ser utilizada por cualquiera de
las funciones o incluso por la función main(). {
int resultado1; /* variable global */ ! int dato1 = 100; /* variable global */
main() ! ....
{ ! funcion2(...)
! funcion_1(...) ! {
! {! ... ! ! int dato1=80; /*variable local */
! } ! ! ...
! funcion_2(...) ! ! printf(“El valor es: %d”, dato1);
! { float x=10.0; /* variable local */ ! }
! resultado1 = x / 3.0; ! printf(“El valor es: %d”, dato1);
! } }
! printf(“El resultado fue: %f”, resultado1); Si ejecutáramos ese programa, obtendríamos dos resultados, uno con
80 y otro con 100. La variable que dejamos en azul es diferente de la
}
que está con color rojo, aunque se llamen igual. La que está en color
Si por alguna razón se declara dentro de una función una variable que rojo sólo “existe” dentro de la función en la que fue declarada, es decir
ya había sido declarada como global, C interpretará a ésta última como funcion2.
41
Archivo 1! Archivo 2
Es importante recordar que si una variable local y una global tienen el
mismo nombre, todas las referencias a ese nombre dentro de la función, int x,y;
extern int x,y;
serán para la variable local y no tendrán efecto alguno sobre la variable char ca;"" " extern char ca;
global. int main(void)
int main(void)
{
{
Modificadores de tipos de variables: funcion1()
funcion1()
Debido a que C permite compilar por separado los módulos de un {! ! ! ! {! ! !
programa, para enlazarlos sin que haya problemas por las variables, se ! ...!
! ...!
han definido tres modificadores que actúan sobre las variables. Dichos }
}
modificadores son: extern, static y register. } }
42
serie()
{
Constantes
Las constantes en C pueden ser de cualquier tipo, en ocasiones se
! static float result; separan en dos grandes grupos:
En este ejemplo, la variable result permanece entre llamadas sucesivas. ! b) Constantes de carácter y cadena (string). Las constantes de
Esto permite producir un número nuevo cada que se llama a serie(), que carácter son indicadas mediante comillas sencillas, por ejemplo: 'a'. En
se basa en el último número generado. C existen constantes para manejar los caracteres no imprimibles. Esas
constantes se indican con el slash (barra invertida). Estas se usan para
utilizar varios códigos especiales. En la siguiente tabla se muestran
Variables Registro (register):
algunos de los más importantes.
C tiene otro modificador de tipo de variable, que casi siempre se aplica a
los tipos int y char. El modificador register obliga al compilador de
C a mantener el valor de las variables declaradas con este modificador
en un registro del CPU en vez de hacerlo en memoria, que es donde
normalmente se almacenan las variables. Esto hace que las
operaciones con este tipo de variables sean muy rápidas, por lo que son
ideales para el control de ciclos. El modificador register sólo es
aplicable a las variables globales y a los parámetros formales. No se
puede usar con variables globales. Desafortunadamente, el número de
variables register dentro de una misma función depende del
Las constantes de cadena se indican mediante comillas dobles. Debe
procesador empleado. Por lo regular los de 8 bits permiten una variable
tenerse en cuenta que C añade a las constantes de cadena el carácter
de este tipo, mientras que los de 16 bits permiten dos o más.
nulo ('\0') al final de la misma. Por ejemplo, la representación de la
cadena "abc" es en realidad: a b c d \0.
43
2.- Determine los tipos de datos apropiados para los siguientes datos:
Ejercicios a. El promedio de tres datos.
1.- Sin correr este código escriba qué debería obtener. Luego, copie y
ejecute el siguiente código. Compare. b. El número de días en la semana.
! return 0;
}
44
Sección 2
Operadores y Operadores
Un operador es un símbolo que indica al compilador que se lleven a
Operadores Aritméticos:
Contenido
1. Operadores aritméticos.
2. Operadores relacionales.
3. Operadores lógicos.
4. Operadores de bits.
Los operadores de incremento y decremento permiten añadir y restar la
5. Operadores especiales. unidad a sus operandos, respectivamente. Por ejemplo, x= x+1 es lo
mismo que x++ ó que ++x. Sin embargo, existe una diferencia entre si
se usa el operador a la izquierda o a la derecha. Cuando el operador
precede al operando, el C utiliza el valor del operando antes de
incrementarlo o decrementarlo.
45
Operadores Relacionales: Operadores De Bits:
Los operadores relacionales se refieren a la relación que existe entre El lenguaje C soporta un completo juego de operadores de bits. Las
dos valores. La clave de este tipo de operadores son los valores de operaciones con bits se refieren a la comprobación, colocación o
verdadero (true) y falso (false). En C, cualquier valor distinto de 0 es desplazamiento de los bits actuales de una variable entera o carácter.
equivalente a verdadero. Las operaciones que los usan devolverán un Este tipo de operaciones son muy utilizados en aplicaciones con
1 cuando sea verdadero y un 0 cuando sea falso. Estos operadores controladores de dispositivos ya que permiten enmascarar ciertos bits,
tienen un nivel de precedencia menor al de los operadores aritméticos. como el de paridad.
La operación AND es una forma de poner a cero los bits. Con AND,
cualquier bit que esté a 0 en el operando hará que el correspondiente bit
de la variable se ponga a 0.
El OR bit a bit pude usarse para poner los bits a 1. Cualquier bit del
operando que esté a 1 hará que su correspondiente bit en la variable se
ponga a 1 también.
El XOR pondrá a uno sólo los bits que al compararlos sean distintos.
Operadores Lógicos:
Los operadores de desplazamiento mueven todos los bits de una
variable a la derecha o a la izquierda, según se especifique. A medida
Los operadores lógicos se refieren a las formas en que pueden darse que se desplazan los bits, se introducen ceros.
las relaciones entre dos objetos. La forma de evaluación es similar a la
de los operadores relacionales, sin embargo en los operadores lógicos e
usan los valores de verdad.
46
Los operadores & y *
! m= &contador;
! q = *m;
C tiene definidos algunos operadores especiales, que son el operador
condicional (?) y los operadores para el manejo de apuntadores. Si m contiene la dirección de memoria dela variable contador, entonces
el valor de contador se copiará hacia q.
El operador ?
! else expresion_3;
47
Tabla de precedencias.
! 2
! 3
! 4
! 6
! 1
4.- Se sabe que sobre un terreno rectangular se ha construido una casa
de ciertas dimensiones. Suponiendo que la casa es también rectangular
y que todo lo que no es casa es jardín, diseñe e implemente un
programa en C que diga cuánto tiempo se tardará un jardinero en podar
el pasto si lo hace a razón de 2 m2 por 15 minutos. Se conocen las
medidas del terreno y las medidas de la casa. Elabore primero el
algoritmo en pseudocódigo.
48
5.- La expansión lineal de una barra de acero como función de la
temperatura se calcula mediante la siguiente ecuación:
l = l0 [1 + ↵(Tf T0 )]
Donde l0 es la longitud a una temperatura T0, α es el coeficiente de
expansión lineal y Tf es la temperatura final de la barra. Diseñe e
implemente un programa en C que dados la longitud inicial, las
temperaturas inicial y final y el coeficiente de expansión, calcule la
longitud final.
3
Programar significa en
realidad hacer que la
computadora procese
datos de una cierta
manera. Para ello, debe
controlarse el flujo de los
datos. Este capítulo habla
de ciclos, de tomas de
decisión y de secuencias
de pasos.
Sección 1
Condicionales La estructura if
En C existen dos tipos de estructuras de control condicional. Se trata del
if-else y el switch.
La estructura if:
! if(expresión_1)
Contenido
! expresión_2
! else expresión_3
1. La estructura if.
La parte correspondiente al else es opcional. Cuando se desea ejecutar
2. La expresión switch.
un conjunto de expresiones, estas se encierran entre llaves y se
3. Ejercicios separan por puntos y comas:
! if(expresión_1)
! {
! ! conjunto1 de expresiones
! }
51
En ambos casos, si el resultado de evaluar expresión_1 es verdadero, else if(condición)
se ejecuta expresión_2 o el bloque correspondiente. En caso de
cualquier otro resultado se ejecuta expresión_3. ! expresión
Uno de los aspectos más confusos de las expresiones if es tenerlas ! else .....
anidadas. Por ejemplo:
! ! else
if(x)
! ! expresión.
{ ....}
Las condiciones se evalúan de arriba para abajo. Tan pronto como se
else encuentre una condición cierta, se ejecuta la expresión asociada, y se
salta el resto de la escalera. Si ninguna de las condiciones resulta cierta,
{ se ejecutará la expresión asociada al último else. Esta expresión se usa
muchas veces como condición por defecto (default), es decir, si todas
if(y) {....} las demás fallan, ésta se ejecutará.
else {.....}
La expresión switch:
} Aunque, tal y como se vió, la escalera permite ejecutar una serie de
pruebas, el código es difícil de seguir e incluso puede confundir. Por
Hay que recordar que en C, el else se asocia al if más inmediato que no
esto, C incluye una expresión condicional con alternativas múltiples,
tenga una expresión else.
llamada switch. Su formato general es:
También es posible construir escaleras if-else-if. Su esquema es como
switch(variable) {
el siguiente:
! case constante1: expresión;
if(condición)
! ! ! ! break;
expresión;
! case constante 2: expresión;
else if(condición)
! ! ! ! break;
expresión
52
! ....
! default : expresión;
switch(resp)
break;
! break;
! default: expresión;
La expresión break se usa junto con switch. Esta origina que el control
del programa salga del switch y continué en la expresión que sigue. Si
no se incluye ningún break, todas las expresiones antes y después de la
igualdad se ejecutarán.
53
Sección 2
! expresión;
! {
1. La estructura for.
! ! expresión_1;
2. La estructura while.
! ! ....
3. La estructura do-while.
! ! expresión_n;
4. Expresiones break, exit y continue.
! }
5. Ejercicios.
La inicialización normalmente es una expresión de asignación que se
usa para fijar la variable de control del ciclo. La condición es una
expresión relacional que determina cuándo se saldrá del ciclo. El
incremento define cómo cambiará la variable de control del ciclo cada
vez que se repita éste. El ciclo se continuará mientras que el resultado
de la condición sea verdadero. Por ejemplo, el siguiente código realiza
la suma de los n primeros enteros:
54
for(i=1; i<n;i++) Esto significa que un ciclo do-while siempre se ejecutará al menos una
vez. El formato general del ciclo do-while es:
suma=suma+i;
do{
La prueba de la condición siempre se lleva a cabo primero. Esto es, el
código dentro del ciclo podría no ejecutarse ninguna vez si la condición ! ! expresiones;
resulta falsa la primera vez que se evalúe.
} while(condición);
En la inicialización es posible incluir más de una expresión,
separándolas por comas. La condición puede estar formada por varios Quizás la utilización más común de do-while sea en una rutina de
elementos relacionados entre sí mediante operadores lógicos y selección por menú.
relacionales. El incremento también puede tener varias expresiones,
solamente hay que separarlas con comas. Las expresiones break, exit y continue
Salida mediante break y exit():
El ciclo while:
La expresión break y la función de biblioteca exit() permiten forzar una
Otra manera de formar un ciclo es mediante while. El formato general
salida de un ciclo, saltándose la condición.
es:
La expresión break:
! while(condición) expresión;
Cuando se encuentra una instrucción break dentro de un ciclo, éste se
en donde expresión puede ser un bloque de instrucciones. La condición
finaliza inmediatamente, y el control del programa pasa a la siguiente
puede ser cualquier expresión, siendo cierto cualquier valor distinto de
expresión del ciclo.
cero. La expresión se ejecuta mientras que la condición sea verdadera.
Al igual que en el for, while comprueba la condición en la parte superior La función exit():
del ciclo, lo que significa que es posible que el código no se ejecute.
Una segunda forma de terminar un ciclo desde dentro es utilizando la
función exit() que se encuentra en la biblioteca estándar. Debido a que
El ciclo do-while: la función originará una terminación inmediata del programa y que el
A diferencia de los ciclos for y while, que prueban la condición del ciclo
control pase al sistema operativo, su utilización es algo limitada.
al principio, el ciclo do-while comprueba su condición al final del ciclo.
Normalmente se llama a la función con un argumento 0 para indicar que
55
la terminación es normal. Se hace uso común de exit() cuando no se
satisface una condición obligatoria para la ejecución del programa.
La instrucción continue:
Ejercicios
56
Arreglos
4
Lorem ipsum dolor rutur
amet. Integer id dui sed
odio imperd feugiat et nec
ipsum. Ut rutrum massa
non ligula facilisis in
ullamcorper purus dapibus.
Sección 1
3. Uso de arreglos.
4. Strings o cadenas.
5. Ejercicios.
tipo_de_dato nombre_arreglo[tamaño];
Por ejemplo:
58
! char b[4]; Arreglo de 4 caracteres. El compilador de C no hace ninguna comprobación de los límites de los
arreglos. Esto quiere decir que si de declara un arreglo de 10 elementos
! float c[15]; Arreglo de 15 flotantes (reales). y se intenta usar el elemento 14, el compilador no marca ningún error,
simple y sencillamente leerá lo que se encuentre 14 posiciones después
del inicio. Es importante tomar esto en cuenta, pues se podría
Tal y como se mencionó antes, los arreglos empiezan con el elemento sobreescribir fuera de un arreglo y escribir algún dato de otra variable o
cero. Por ejemplo, cuando se escribe int x[10]; se está declarando un incluso en una parte del programa. En los programas escritos
arreglo de enteros que tiene 10 elementos, de x[0] a x[9]. correctamente, esto generalmente no es un problema, pero puede serlo
cuando se está aprendiendo a programar en C.
Al mismo tiempo que se declara un arreglo se puede inicializar, para lo
cual se incluyen los elementos del mismo entre llaves y separados por Cuando se declara un arreglo para almacenar una cadena de
comas, por ejemplo: caracteres, debe preverse un byte extra para el carácter nulo (\0), que
tienen todas las cadenas. Por ejemplo si se quisiera declarar un arreglo
int A[10] = {1,1,1,1,1,1,1,1,1,1}; mis_datos para contener una cadena de 10 caracteres, se tendría que
escribir:
Desafortunadamente no hay forma de abreviar esta situación. En caso
de tener un arreglo más grande, se podría requerir escribir un ciclo para ! char mis_datos[11];
inicializarlo.
La siguiente figura ilustra las dos formas en que puede ser visto un
Cuando la lista de inicialización es más pequeña que el número real de arreglo en C:
datos que contendrá el arreglo, C inicializa el resto en cero. Para
asegurarnos de ello se suele emplear:
int A[10]={0};
foo(string) }
char *string también valdría porque el C generará un código que indica a func() que
va a recibir un apuntador a carácter. Como no hay comprobación de
{ límites, al llamar a la rutina se puede pasar un arreglo de cualquier
tamaño, incluso aunque sólo se hubiese declarado un tamaño de 11.
! .....
Por ejemplo, para declarar un arreglo de enteros llamado listanum con
}
diez elementos se hace de la siguiente forma:
o como
foo(string)
int listanum[10];
60
En C, todos los arreglos usan cero como índice para el primer elemento. Observar que para declarar cada dimensión lleva sus propios paréntesis
Por tanto, el ejemplo anterior declara un arreglo de enteros con diez cuadrados.
elementos desde listanum[0] hasta listanum[9].
Para acceder los elementos se procede de forma similar al ejemplo del
La forma como pueden ser accesados los elementos de un arreglo, es arreglo unidimensional, esto es,
de la siguiente forma:
num = listanum[2]; /* Asigna el contenido del 3er elemento a la variable num = tabladenums[25][16];
num */
A continuación se muestra un ejemplo que asigna al primer elemento de
El lenguaje C no realiza comprobación de contornos en los arreglos. En un arreglo bidimensional cero, al siguiente 1, y así sucesivamente.
el caso de que sobrepase el final durante una operación de asignación,
entonces se asignarán valores a otra variable o a un trozo del código, main()
esto es, si se dimensiona un arreglo de tamaño N, se puede referenciar
{
el arreglo por encima de N sin provocar ningún mensaje de error en
tiempo de compilación o ejecución, incluso aunque probablemente se int t,i,num[3][4];
provoque el fallo del programa. Como programador se es responsable
de asegurar que todos los arreglos sean lo suficientemente grandes
para guardar lo que pondrá en ellos el programa.
for(t=0; t<3; ++t)
C permite arreglos con más de una dimensión , el formato general es:
for(i=0; i<4; ++i)
num[t][i]=(t*4)+i*1;
tipo nombre_arr [ tam1 ][ tam2 ] ... [ tamN];
En C se permite la inicialización de arreglos, debiendo seguir el Por ejemplo, el siguiente fragmento inicializa cadena con ``hola'':
siguiente formato:
char cadena[5]="hola";
62
printf("Introduce tu nombre: "); ! 1.! Escribir un programa que lea un arreglo de cualquier tipo
(entero, flotante, char), se podría pedir al usuario que indique el tipo de
scanf("%s",nombre); arreglo, y también escribir un programa que revise el arreglo para
encontrar un valor en particular.
printf("Introduce tus apellidos: ");
! 2.! Leer un texto, un caracter a la vez desde la entrada estándar
scanf("%s",apellidos);
(que es el teclado), e imprimir cada línea en forma invertida. Leer hasta
printf("Usted es %s %s\n",nombre,apellidos); que se encuentre un final-de-datos (teclar CONTROL-D para generarlo).
El programa podría probarse tecleando progrev | progrev para ver si una
} c o p i a e x a c t a d e l a e n t r a d a o r i g i n a l e s r e c r e a d a .
Para leer caracteres hasta el final de datos, se puede usar un ciclo
El lenguaje C no maneja cadenas de caracteres, como se hace con
c o m o e l s i g u i e n t e
enteros o flotantes, por lo que lo siguiente no es válido:
main() char ch;
! completo="Gral."+nombre+appellidos; /* Ilegal */ ! 7.! Escribir un programa para leer un texto hasta el fin-de-datos,
y mostrar una estadística de las longitudes de las palabras, esto es, el
}
número total de palabras de longitud 1 que hayan ocurrido, el total de
5.3 Ejercicios longitud 2 y así sucesivamente. Define una palabra como una secuencia
de caracteres alfabéticos. Se deberán permitir palabras hasta de una
l o n g i t u d d e 2 5 l e t r a s .
63
U n a s a l i d a t í p i c a p o d r í a s e r c o m o e s t a :
longitud 1 : 16 ocurrencias
! 12.! ........
! 13.!
64
Sección 2
int d[10][20];
Hay que recordar que el almacenamiento para todos los elementos del
arreglo está asignado permanentemente. En el caso de un arreglo
65
bidimensional, la siguiente fórmula determinará el número de bytes en la Los arreglos de tres o más dimensiones casi no se utilizan debido a que
memoria: ocupan mucha memoria. Tal y como se indicó anteriormente, el espacio
para todos los elementos del arreglo está asignado permanentemente
! bytes = fila * columna* número de bytes según el tipo de dato.! durante la ejecución del programa. Ese espacio aumenta
Por lo tanto, un arreglo entero con dimensiones 10,5 tendrá 10*5*2=100 exponencialmente con el número de dimensiones. La computadora
bytes asignados. tarda tiempo en generar cada índice y esto puede hacer que los arreglos
multidimensionales accedan mas lentamente que los arreglos
unidimensionales con el mismo número de elementos. Por esta y otras
Cuando se pasan arreglos bidimensionales a las funciones, sólo se razones, cuando son necesarios arreglos multidimensionales muy
pasa un apuntador al primer elemento. Esto se puede hacer utilizando el grandes, se deben asignar dinámicamente los bits y las partes del
nombre del arreglo sin índices. Sin embargo, una función que reciba un arreglo que sean necesarios, y utilizar apuntadores.
arreglo bidimensional como parámetro tiene que definir la longitud de la
Cuando se pasan arreglos multidimensionales como argumentos de
segunda dimensión. Por ejemplo, una función que recibiera un arreglo
funciones, hay que declarar todas las dimensiones excepto la primera.
entero bidimensional con las dimensiones 10,10 se declararía como:
Por ejemplo, si se declara el arreglo m como int m[4][3][6][5];
foo(x) entonces una función foo() que recibiera m, empezaría:
{ int d[][3][6][5];
.... {
} ....
66
Funciones
5
Lorem ipsum dolor rutur
amet. Integer id dui sed
odio imperd feugiat et nec
ipsum. Ut rutrum massa
non ligula facilisis in
ullamcorper purus dapibus.
Sección 1
68
! }
! Todas las funciones devuelven un valor (de ahí su ! Una función se puede utilizar en expresiones debido a
nombre). Este valor se especifica dentro de la función mediante que cada función tiene un valor que, o bien es un valor
la instrucción return: devuelto o por defecto es cero. Por lo tanto, cada una de las
siguientes expresiones es válida en C:
! return(expresión)
x=potencia(y);
donde expresión es cualquier expresión válida (incluyendo una
constante), también puede ser 0 si no se especifica ningún otro if(max(x,y)>100) printf("Mayor que");
valor. Por defecto, las funciones devolverán valores enteros. Si
for(ca=getchar();esdigito(ca);)...;
se especifica, pueden devolver otros tipos de valores.
! nombre_funcion(lista de parámetros)
! {
! cuerpo de la función;
69
! Aunque todas las funciones devuelven valores, es posible 5.2 Argumentos de las Funciones
clasificarlas en tres tipos, dependiendo de lo que devuelven:
! No es necesario asignar el valor que devuelven las ! Las funciones en C utilizan la llamada por valor. Esto
funciones a alguna variable, en el caso de que no se haga, C significa, por lo general, que no se pueden alterar las variables
simplemente descarta dicho valor. utilizadas para llamar a la función. Por ejemplo, en la siguiente
función:
70
que cualquier otro valor. Por ejemplo, la función intercambia(),
que intercambie los valores de sus dos argumentos enteros se
cuadrado();
escibirá como:
int x;
x=x*x;
return(x);
las variables */
! y=20;
main()
! intercambia(&x,&y); /* Se pasan como argumentos las
direcciones {
72
! int t[10],i; operando y alterando potencialmente el contenido real de los
elementos del arreglo utilizados para llamar a la función.
! for(i=0;i<10;++i)
! t[i]=getnum();
Funciones void:
! visualiza(t);
}
! Existen muchas funciones que no regresan ningún valor,
por ejemplo, una rutina de ordenamiento, una que invirtiera una
vizualiza(num) cadena alfanumérica o una que imprima errores.
int *num;
73
! En el lenguaje C es posible definir funciones recursivas, }
es decir, aquellas funciones que se llaman a sí mismas. Un
ejemplo clásico es la función para calcular el factorial de un
número, que se puede estructurar como sigue: fact(n)
int n
{
! Hay que notar que todas las funciones recursivas deben int resp;
tener una condición de terminación o criterio de paro, pues de
no ser así, se volverían infinitas. if (n==1)
return(1);
main()
74
75
Estructuras de
Datos
6
Lorem ipsum dolor rutur
amet. Integer id dui sed
odio imperd feugiat et nec
ipsum. Ut rutrum massa
non ligula facilisis in
ullamcorper purus dapibus.
Sección 1
Estructuras de Introducción
6. ESTRUCTURAS, UNIONES Y TIPOS DEFINIDOS POR EL
datos USUARIO
6.1 Estructuras
Contenido
! Una estructura es una colección de variables que están
referenciadas bajo un solo nombre. Cada estructura está formada por
1. Introducción. una ó mas variables que están relacionadas lógicamente. Estas
variables se denominan elementos de la estructura. Las estructuras,
2. Estructuras (struct). como grupos de variables conectados lógicamente, se pueden pasar
fácilmente a funciones.
3. Uniones (union).
! struct directorio {
77
! ! ! char calle [40]; ! ! ! char nombre[30];
! ! ! };
! ! ! ! ! tipo nombre_var;
struct directorio info;
! ! ! ! ! ! :
! ! ! ! ! ! :
! Esto declarará una variable de tipo directorio denominada info.
! ! ! ! ! } variables_estructura;
Cuando se define una estructura, en esencia se está definiendo una
variable de tipo complejo formado por elementos de la estructura.
78
putchar(info.nombre[t]);
! Por tanto para imprimir en pantalla el código postal, se escribirá : ! ! ! struct directorio info[100];
printf("%d",info.cp);
! for (t=0;info.nombre[t];++t)
79
! Cuando se pasa un elemento de una variable de tipo estructura,
realmente se está pasando el valor de ese elemento a la función, a
menos que ese elemento sea complejo, como un arreglo de caracteres.
Por ejemplo :
! ! struct xyz{
! Cuando una estructura se pasa a una función sólo se pasa la
! ! char x; dirección del primer byte de estructura. Esto es parecido a la forma en
que se pasan los arreglos a las funciones. Debido a que la función
! ! char y; referenciará a la estructura real y no a una copia, será posible modificar
los contenidos de los elementos reales de la estructura. El concepto
! ! int z; general es que se está pasando la dirección, lo cual significa que se
estará trabajando con un apuntador a una estructura.
! ! }wkl;
actualizar(t)
! Sin embargo, si se quisiera pasar la dirección de elementos
individuales de la estructura, se colocaría el operador & antes del struct tiempo *t;
nombre de la estructura, por ejemplo :
{
(*t).segundos++;
! ! funcion_1(&wkl.x)! /*Pasa la dirección del carácter x */
80
if((*t).segundos==60){ }
if((*t).minutos==60){ actualizar(t)
(*t).horas++; {
} t->segundos++;
if((*t).horas==24) if(t->segundos==60){
(*t).horas=0; t->segundos=0;
} t->minutos++;
! }
t->minutos=0;
! int horas; }
! Un elemento de una estructura que es un arreglo se trata como se en donde directorio es la estructura definida previamente. Aqui la
podría esperar, por ejemplo, en el siguiente programa estructura clientes se ha definido como formada por dos elementos: un
arreglo de estructuras del tipo directorio y un número de cuenta de tipo
entero. Este fragmento de código asignará el código 61853 al segundo
struct x { elemento de nuevadir:
! float b;
82
! C permite el acceso a bits individuales dentro de un tipo de dato
mayor, por ejemplo, un byte. Esta característica es muy útil cuando se
quieren alterar máscaras de datos usadas en sistemas de información y
gráficos. Esta posibilidad se basa en las estructuras.
struct dispositivo{
! unsigned activo : 1;
! unsigned listo : 1;
! La siguiente figura muestra el campo de bit de la variable
! unsigned error : 1; disp_codificado:
! } disp_codificado;
83
! Estado del teclado: 7 6 5 4 3 2 1 0 (bits del registro) ! ctrl! ! : 1;
! bit 6 = BLOQ MAY activado (1) ! Los bits se especifican en la estructura empezando por el menos
significativo (lsb) y continuando hasta el más significativo (msb). Se esta
! bit 7 = INS activado(1) usando un tipo de dato unsigned char por que éste emplea 8 bits para
su representación. Podemos especificar más de un bit para cada dato
del registro dando la cantidad (en lugar de 1). Como es lógico, podemos
Para acceder a esta información y manipularla habría que crear esta usar únicamente tipos de datos enteros para los campos de bits.
estructura:
! lshift! : 1;
6.5 Unión
84
justamente el requerido por el tipo de mayor tamaño en memoria. En el
ejemplo citado, un entero usa 16 bits, en tanto que un carácter requiere
! Una unión es un espacio de la memoria que puede ser usado por 8, por tanto el compilador reservará los 16.
más de una variable. Las variables que hagan uso de ese espacio
mediante el union, no necesariamente deben ser del mismo tipo. Por
ejemplo, enseguida se da la unión de un carácter y un entero, llamada
junta: 6.6 El sizeof
union junta { ! Tanto las estructuras como las uniones se pueden utilizar para
crear variables largas, pero el tamaño de estas puede cambiar de
! int i; máquina a máquina. El operador unario sizeof se utiliza para determinar
tamaño (en memoria) de cualquier tipo de variable, incluyendo uniones y
! char caract; estructuras.
};
! Por ejemplo
! Edad Ed_Juan;
! Edad Ed_Luis;
86
Apuntadores
7
Lorem ipsum dolor rutur
amet. Integer id dui sed
odio imperd feugiat et nec
ipsum. Ut rutrum massa
non ligula facilisis in
ullamcorper purus dapibus.
Sección 1
2. Operadores. Por ejemplo, sea p un apuntador y sea x una variable que reside en la
dirección 100 de memoria. Si hacemos que p apunte a x, entonces
tendremos algo como:
tipo_de_dato *nombre
88
donde tipo_de_dato indica el tipo de dato al cual está apuntando, y
nombre indica el nombre del apuntador, por ejemplo:
! float *pf;
! short *ps;
! float pi= 3.14;
! int *pi;
! pf = π
! char *
pc; ! printf("%f",*pf);
! double
*pd;
El siguiente ejemplo muestra tanto la declaración como el uso de los
ps apuntará a una apuntadores
variable de tipo
#include <stdio.h>
short, pi apuntará a
una variable de tipo main()
int, y pd apuntará a una variable de tipo double.
{
Operadores
int *p, *q;/* declaramos dos apuntadores a enteros */
Existen dos operadores unarios para el manejo de los apuntadores: int x,y;
89
por p y lo asigna a la variable ! pi= &x; ! /* pi apunta a la dirección 1000 */
apuntada por q */
! pi = pi+2;! /* pi apunta a la dirección 1004, es decir, el
printf("x= %d, y= %d", *p, y);
incremento se hace en base al tamaño del dato que
}
está apuntando. En este caso consideramos que el
90
variable tipo long se almacena en cuatro bytes. */
apun_char1= &codigo1;
apun_char2= &codigo2;
Fig. 3.- Luego de ejecutar apun_char1=apun_char2;
temp = apun_char1;
apun_char1 = apun_char2;
apun_char2= temp;
Fig. 4.- Luego de ejecutar apun_char2=temp;
printf("%c %c",*apun_char1, *apun_char2);
91
! Código de programa ! El argumento que espera la función malloc() es un entero sin signo
que representa el número de bytes de almacenamiento requeridos. Si el
! Información global, espacio de almacenamiento está disponible, malloc() devuelve un
apuntador de tipo void, que se puede transformar en el un apuntador de
! Pila (stack) y
cualquier tipo deseado. El concepto de apuntadores void se introdujo en
! Heap (montón). el C ANSI estándar, y representa un apuntador a un tipo desconocido, o
un apuntador genérico. Un apuntador void no pude ser utilizado para
referenciar cualquier cosa (ya que no apunta a ningún tipo específico de
información y recordemos que C necesita saber el tipo de dato para
! El heap es un área de memoria libre (a veces denominado
poder efectuar su aritmética de apuntadores), pero puede contener un
almacenamiento libre) manipulada con las funciones de asignación
apuntador de cualquier otro tipo. Por tanto, podemos transformar
dinámica malloc() y free().
cualquier apuntador en un apuntador void sin ninguna pérdida de
información.
92
! La función malloc() se ha utilizado para obtener almacenamiento ! Una aplicación particular podría tener que utilizar variables con un
suficiente para guardar los 200 datos de tipo float. Note la utilización del tamaño desconocido durante la compilación. Es esos casos no es
sizeof que se explicó en el capítulo anterior. Este código funcionará posible usar variables cuyos valores se almacenen en el stack o en el
perfectamente tanto en una PC como en una RISC, por ejemplo. Eso es espacio para las variables globales, ya que C requiere que se le diga al
portabilidad. Cada bloque de almacenamiento solicitado está totalmente compilador cúanto espacio se requerirá. En estas circunstancias, lo que
separado y es distinto de todos los demás. No debemos hacer habría que hacer es asignar la memoria por uno mismo, manipulando el
suposiciones acerca de dónde están localizados los bloques. heap. Podemos imaginar el heap ocupando la parte inferior del espacio
Generalmente los bloque están "marcados" con algún tipo de de memoria de programa y creciendo hacia arriba, mientras la pila
información que permite que el sistema operativo controle su ocupa la parte superior y crece hacia abajo.
localización y tamaño. Cuando el bloque ya no es necesario podemos
liberarlo al sistema operativo utilizando la siguiente instrucción.
93
! La mayoría de los compiladores de C utilizan las funciones de block_mem = (int *)malloc(MAX * sizeof(int));
biblioteca malloc() y free() para permitir la asignación de memoria
dinámica .Sin embargo, en C++ estas capacidades fueron consideradas if (block_mem==NULL)
tan importantes que se implementaron como parte del núcleo del
printf("Memoria insuficiente\n");
lenguaje. En realidad, C++ utiliza las funciones new() y delete() para
asignar y liberar la memoria del heap. El argumento de la función new() else
es una expresión que regresa el número de bytes asignados. El valor
devuelto es un apuntador al comienzo de este bloque de memoria. El ! {
argumento para delete() es la dirección inicial del bloque de memoria a
printf("Memoria asignada\n");
liberar. Los programas siguientes ilustran las similitudes y diferencias
entre aplicaciones de C++ y C que utilizan asignación de memoria free(block_mem);
dinámica.
! }
return (0);
Aquí tenemos el ejemplo de C:
}
#include <stdio.h>
! Las funciones malloc y free están definidas en el archivo stdlib.h.
#include <stdlib.h> Después de que el programa define la variable int *block _mem, se
llama a la función malloc para devolver la dirección a un bloque de
memoria de tamaño MAX * sizeof(int). Un algoritmo robusto debería
#define MAX 256 revisar siempre el éxito o fracaso de la asignación de memoria. Este
programa simple termina devolviendo de nuevo la memoria asignada al
heap con la función free y pasando la dirección inicial del bloque
asignado.
main( )
{ int *block_mem;
! El programa en C++ no parece muy distinto:
94
#include <iostream.h> ! La única diferencia significativa entre ambos programas está en la
sintaxis utilizada con las funciones new y delete. Mientras que la función
malloc requiere la función sizeof para garantizar la asignación de
memoria adecuada, la función new ha sido definida para realizar
#define MAX 256
automáticamente la función sizeof sobre el tipo de dato que se le pasa
main( ) como argumento. Ambos programas asignarán 256 bloques de 2 bytes
de memoria consecutiva (en sistema que asignan 2 bytes por entero).
{
8. Apuntadores
int *bloque_de_memoria;
Los apuntadores son una parte fundamental de C. Si usted no puede
usar los apuntadores apropiadamente entonces esta perdiendo la
potencia y la flexibilidad que C ofrece básicamente. El secreto para C
bloque_de_memoria=new(int(MAX));,
esta en el uso de apuntadores.
if (bloque_de_memoria == NULL )
C usa los apuntadores en forma extensiva. ¿Porqué?
{
! •! Es la única forma de expresar algunos cálculos.
cout << "Memoria asignada\n";
! •! Se genera código compacto y eficiente.
delete (bloque-de_memoria);
! •! Es una herramienta muy poderosa.
}
C usa apuntadores explícitamente con:
else cout << "Memoria insuficiente \n";
! •! Es la única forma de expresar algunos cálculos.
return(0);
! •! Se genera código compacto y eficiente.
}
! •! Es una herramienta muy poderosa.
95
C usa apuntadores explícitamente con: {
! •! Arreglos, int x = 1, y = 2;
! •! Funciones
ap = &x;
Se debe asociar a cada apuntador un tipo particular. Por ejemplo, no se Con el objetivo de entender el comportamiento del código supongamos
puede asignar la dirección de un short int a un long int. que la variable x esta en la localidad de la memoria 100, y en 200 y ap
en 1000. Nota: un apuntador es una variable, por lo tanto, sus valores
Para tener una mejor idea, considerar el siguiente código: necesitan ser guardados en algún lado.
main()
96
int x = 1, y = 2;
200
ap = &x; 1000
100 1
200 y
1000 1
x ap
1 100
ap
100 x = ap;
200
y = *ap; 1000
97
x 1
100 ap
y 100
int *ap;
x
main()
3
{
y
int *ap;
98
int x; flq = flp;
99
Para entender mejor lo anterior consideremos la función swap() que int temp;
intercambia el valor de dos argumentos enteros:
int x, y;
8.3 Apuntadores y arreglos
printf("x=%d\ty=%d\n",x,y);
void swap(int *px, int *py) x = *ap; /* A x se le asigna el contenido de ap (a[0] en este caso) */
100
*(ap + 1) = 100; /* Se asigna al segundo elemento de 'a' el valor 100 Por lo tanto:
usando ap*/
strlen(s) es equivalente a strlen(&s[0])
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 Esta es la razón por la cual se declara la función como:
límites del arreglo, por lo que se puede ir fácilmente más alla del arreglo
int strlen(char s[]); y una declaración equivalente es int strlen(char *s);
en memoria y sobreescribir otras cosas.
ya que char s[] es igual que char *s.
C sin embargo es mucho más sútil en su relación entre arreglos y
apuntadores. Por ejemplo se puede teclear solamente: La función strlen() es una función de la biblioteca estándar que regresa
la longitud de una cadena. Se muestra enseguida la versión de esta
ap = a; en vez de ap = &a[0]; y también *(a + i) en vez de a[i], esto es,
función que podría escribirse:
&a[i] es equivalente con a+i.
while ( (*s++ = *t++) != '\0' ); ! 3.! Comparar dos líneas usando la función de la biblioteca
estándar strcmp().
}
! 4.! Si dos líneas están desacomodadas -- intercambiar (swap)
En los dos últimos ejemplos se emplean apuntadores y asignación por los apuntadores (no el texto).
valor. Nota: Se emplea el uso del caracter nulo con la sentencia while
para encontrar el fin de la cadena.
En C se pueden tener arreglos de apuntadores ya que los apuntadores Con lo anterior se elimina:
son variables.
! •! el manejo complicado del almacenamiento.
A continuación se muestra un ejemplo de su uso: ordenar las líneas de
un texto de diferente longitud. ! •! alta sobrecarga por el movimiento de líneas.
102
Un arreglo multidimensional puede ser visto en varias formas en C, por En el último ejemplo se requieren los parénteis (*a) ya que [ ] tiene una
ejemplo: precedencia más alta que *.
Sin embargo:
f( int (*a)[35] ) { ..... }
103
-
anomb es un arreglo verdadero de 200 elementos de dos dimensiones Figura 8.2: Arreglo de 2 dimensiones VS. arreglo de apuntadores.
tipo char.
char anomb[][15] = { "No mes", "Ene", "Feb", "Mar", ... }; 8.7 Apuntadores y estructuras
Lo cual gráficamente se muestra en la figura 8.2. Se puede indicar que Los apuntadores a estructuras se definen fácilmente y en una forma
se hace un manejo más eficiente del espacio haciendo uso de un directa. Considerar lo siguiente:
arreglo de apuntadores y usando un arreglo bidimensional.
104
int valor;
{ } ELEMENTO;
n1.sig = &n2;
ap_punto = &punto; /* Se asigna punto al apuntador */ Figura 8.3: Esquema de una lista ligada con 2 elementos.
ap_punto->x++; /* Con el operador -> se accesan los miembros */ Nota: Solamente se puede declarar sig como un apuntador tipo
ELEMENTO. No se puede tener un elemento del tipo variable ya que
ap_punto->y+=2; /* de la estructura apuntados por ap_punto */ esto generaría una definición recursiva la cual no esta permitida. Se
permite poner una referencia a un apuntador ya que los los bytes se
ap_punto->z=3;
dejan de lado para cualquier apuntador.
}
105
C o n s i d e r a r :
! •! No asignar un apuntador a una dirección de memoria antes *p = (char *) malloc(100): /* pide 100 bytes de la memoria */
de usarloint *x
! •!
! •!
! •! *p = 'y';
! •! *x = 100;
! •!
! •!
E x i s t e u n e r r o r e n e l c ó d i g o a n t e r i o r . ¿ C u á l e s ?
lo adecuado será, tener primeramente una localidad física de memoria, El * en la primera línea ya que malloc regresa un apuntador y *p no
d i g a m o s i n t y ;
a p u n t a a n i n g u n a d i r e c c i ó n .
E l c ó d i g o c o r r e c t o d e b e r á s e r :
int *x, y;
p = (char *) malloc(100);
! •!
! •!
! •! x = &y;
Ahora si malloc no puede regresar un bloque de memoria, entonces p
! •! *x = 100; e s n u l o , y p o r l o t a n t o n o s e p o d r á h a c e r :
! •!
*p = 'y';
! •!
! •! Indirección no válida Supongamos que se tiene una función Un buen programa en C debe revisar lo anterior, por lo que el código
llamada malloc() la cual trata de asignar memoria dinámicamente (en a n t e r i o r p u e d e s e r r e e s c r i t o c o m o :
tiempo de ejecución), la cual regresa un apuntador al bloque de
memoria requerida si se pudo o un apuntador a nulo en otro caso.
p = (char *) malloc(100): /* pide 100 bytes de la memoria */
char *malloc() -- una función de la biblioteca estándar que se verá más ! •!
a d e l a n t e .
! •! if ( p == NULL )
S u p o n g a m o s q u e s e t i e n e u n a p u n t a d o r c h a r * p
106
! •! { (una cadena larga). Leer los datos de la entrada estándar. La primera
línea es una sola palabra, en la segunda línea se tiene un texto general.
! •! printf("Error: fuera de memoria\n"); Leer ambas hasta encontrar un caracter de nueva línea. Recordar que
s e d e b e i n s e r t a r u n c a r a c t e r n u l o a n t e s d e p r o c e s a r.
! •! exit(1);
L a s a l i d a t í p i c a p o d r í a s e r :
! •! }
La palabra es "el"
! •!
! 4.! La sentencia es "el perro, el gato y el canario"
! •! *p = 'y';
! 5.! La palabra ocurrio 3 veces.
! •!
! 6.!
8.9 Ejercicios
107
Entrada/Salida
8
Lorem ipsum dolor rutur
amet. Integer id dui sed
odio imperd feugiat et nec
ipsum. Ut rutrum massa
non ligula facilisis in
ullamcorper purus dapibus.
Sección 1
! ! FILE *fp;
109
! ! fp = fopen(nombre_archivo, modo); ! ! fp = fopen("prueba1", "w");
en donde modo es una cadena que contiene una r para leer (read), o Sin embargo, normalmente se escribirá como
una w para escribir (write), o una a (append) para añadir. Normalmente
el modo de lectura/escritura se especifica con la cadena rw. Esto varía
dependiendo del compilador, por lo que se debe de comprobar el
! ! If ((fp = fopen("test', "w"))= =NULL) {
manual de usuario. El nombre del archivo tiene que ser una cadena de
caracteres que forme un nombre válido para el sistema operativo bajo el ! ! puts ("No se puede abrir el fichero\n");
cual se este trabajando (a manera de recordatorio, en DOS los
identificadores para archivos se forman con máximo ocho caracteres, un ! ! exit();
punto y una extensión de tres caracteres. En UNIX los nombres pueden
! ! }
tener cualquier longitud y se pueden incluir puntos entre ellos -p.e. :
mi_archivo.uno.c- solo hay que mantener en mente que UNIX es
sensitivo a las mayusculas).
! Este método detecta cualquier error al abrir un archivo, tal como
una protección a escritura o un disco lleno, antes de intentar escribir en
él.
! Por otro lado, la variable fp es del tipo FILE y es un apuntador al
archivo en cuestión. FILE es un tipo de dato específico definido en
stdio.h . Todos los apuntadores a archivos deben declararse como de
tipo FILE. Algunos compiladores puede que denominen a este tipo de ! Si se utiliza fopen() para abrir un archivo para escritura, cualquier
datos por un nombre diferente, por lo que hay que comprobarlo en la archivo que hubiera existido con ese nombre se borrará y se abrirá uno
documentación. nuevo. Si se quiere añadir algo al final del archivo hay que utilizar el
modo a.
110
8.2 La función putc() ! en donde fp es un apuntador a archivo de tipo FILE devuelto por
fopen(). El apuntador indica a getc() de qué fichero debe de leer.
! ! ca = getc(fp);
! en donde fp es el apuntador a archivo devuelto por fopen() y c es
el carácter a enviar al archivo en disco. El apuntador a archivo le dice a ! ! while (ca != EOF) {
putc() en qué archivo en disco debe de escribir.
! ! ! .
! ! ! .
8.3 La función getc()
! ! ! .
! ! ! ca = getc(fp);
! La función getc() se utiliza para leer caracteres de un archivo
abierto en modo lectura por fopen(). El formato general de la función es : ! ! }
111
8.4 La función fclose()
112
de en binario. Si le preocupa la velocidad o el tamaño del archivo, biblioteca también proporciona extensiones definibles por el usuario
probablemente debería escribir rutinas de archivo adaptadas similares a para manipular el tipo class.
putw() y getw().
! En su nivel más bajo, C++ interpreta un archivo como un flujo o ! clog : Un objeto de la clase ostream con memoria intermedia
secuencia de bytes. A este nivel, el concepto de tipo de dato vinculado al error estándar.
desaparece. Hay un componente de la biblioteca de E/S que se encarga
de la transferencia de estos bytes.
113
pueden necesitar o no alteraciones, dependiendo de la estructura E/S, dándonos mayor flexibilidad para introducir la interfaz de usuario
particular utilizada. mas eficiente .
! La nueva biblioteca de E/S también puede ser utilizada para ! En C lamentablemente hay poca consistencia entre las funciones
realizar operaciones de entrada y salida en archivos. Un archivo puede de E/S en términos de los valores devueltos y la secuencia de
asociarse a su programa definiendo la instancia de uno de los siguientes parámetros. Debido a esto, los programadores tienden a confiar la E/S
tres tipos de clase : formateada a las funciones printf, scanf y otras similares, especialmente
cuando los objetos que se manipulan son números u otros valores que
no son caracteres. Estas funciones de E/S formateadas son
convenientes y compartidas en la mayoría de los casos con una interfaz
! fstream: Derivada de iostream, vincula un archivo a nuestra
consistente. Sin embargo, su uso resulta pesado porque tienen que
aplicación tanto para entrada como para salida.
manipular muchas clases diferentes de valores.
! ifstream: Derivada de istream, vincula un archivo a nuestra
aplicación sólo para entrada.
! El operador de extracción << y el operador de inserción >> se han
! ofstream: Derivada de ostream, vincula un archivo a nuestra
modificado para aceptar argumentos de cualquier tipo de dato
aplicación sólo para salida.
incorporado, incluyendo char*. También puede extenderse para aceptar
argumentos de tipo clase.
114
programador puede manipular los indicadores de estado utilizando las ! ios::hex! ! ! ! ! Base numérica hexadecimal.
funciones setf y unsetf.
! ios::oct! ! ! ! ! Base numérica octal.
! setf(long,long);
! En C++ los flujos homólogos a stdin, stdout y stderr son : cin, cout
y cerr. Estos tres flujos se abren automáticamente cuando comienza la
ejecución de nuestro programa, y se convierten en la interfaz entre el
programa y el usuario. El flujo cin está asociado con el teclado del
terminal.Los flujos cout y cerr se asocian con el monitor.
116
! Como resultado directo de la sobrecarga de operadores C++ ! Por último, pero no menos importante, los operadores de inserción
permitirá a un programa expandirse sobre los operadores de inserción y y extracción tienen una ventaja adicional: su tamaño de código final. Las
extracción. El siguiente segmento de código muestra cómo el operador funicones de E/S de propósito general printf y scanf llevan consigo
de inserción puede estar sobrecargado, imprimiendo el nuevo tipo segmentos de código en la versión ejecutable final de un programa, que
empleado. frecuentemente no son utilizados. En C, aunque sólo trate con datos
enteros, arrastrará todas las conversiones para los tipos de datos
estándar adicionales. En contrase, el compilador C++ sólo incorpora
aquellas rutinas necesarias.
ostream& operador << (ostream& out_file , empleado un_empleado)
{
Un programa de demosración
out _file << " " << un_empleado.nombre;
117
char respuesta;
int entero;
float real;
double real:doble ;
cin >>respuesta;
#define LONG 30 cout <<"\n"<< "Por favor introduzca un valor entero :";
#define CAR_NULO 1
cin >>entero
118
cin >>real; instrucción de entrada parece idéntica excepto por el nombre de
variable.
cout <<"\n\n";
cout<< \n\n;
cin >>nombre;
#include <iostream.h>
}
#define LONG 30
return (0);
#define CAR_NULO 1
}
main ( )
! En este ejemplo, el operador de salida << se utiliza en su forma
más simple para la salida inmediata de una cadena de literales. Observe
que aunque el programa utiliza cuatro tipos diferentes de datos, cada
119
{ tabulador o retorno de carro. Por tanto, cuando se imprime nombre sólo
se muestra a la salida el primer nombre leído.
char nombre [LONG+CAR_NULO];
cin>>nombre ;
#define LONG 30
main ( )
Por Favor introduzca su nombre y apellidos : Juan Diaz char nombre [LONG+CAR_NULO];
cin.get(nombre, LONG);
! El operador de entrada,>>, define la lectura de información tan cout <<"\n\n Gracias," << nombre;
pronto como se encuentra un blanco. El blanco puede ser un espacio,
120
return (0); leería LONG caracteres en nombre, o todos los caracteres hasta un
símbolo * (pero sin incluirlo) o un retorno de carro.
}
Gracias, Juan
cin.get(nombre,LONG,´*´);
121