Manual c
-
Upload
jose-alejos -
Category
Documents
-
view
213 -
download
0
description
Transcript of Manual c
TABLA DE CONTENIDOS
TABLA DE CONTENIDOS ............................................................................................................................ 1
1. INTRODUCCIÓN ................................................................................................................................... 3
1.1. HISTORIA DEL C ............................................................................................................................... 3 1.2. GENERALIDADES Y CARACTERÍSTICAS DEL LENGUAJE “C” .............................................................. 4 1.3. VISTA EXTERNA E INTERNA DE UN PROGRAMA EN C ....................................................................... 6
1.3.1. Vista externa ............................................................................................................................... 6 1.3.2. Vista interna ................................................................................................................................ 8
2. OPERADORES Y EXPRESIONES .................................................................................................... 10
2.1. TIPOS FUNDAMENTALES DE DATOS ................................................................................................. 10 2.1.1. Caracteres (tipo char) ............................................................................................................... 10 2.1.2. Números enteros (tipo int) ........................................................................................................ 11 2.1.3. Números reales (tipo float) ....................................................................................................... 12 2.1.4. Números reales (tipo double) .................................................................................................... 12
2.2. IDENTIFICADORES ........................................................................................................................... 13 2.3. PALABRAS CLAVES ........................................................................................................................ 13 2.4. COMENTARIOS ................................................................................................................................ 14 2.5. VARIABLES ..................................................................................................................................... 14 2.6. CONSTANTES .................................................................................................................................. 17
2.6.1. Constantes enteras: ................................................................................................................... 18 2.6.2. Constantes reales: ..................................................................................................................... 18 2.6.3. Constantes alfanuméricas. ........................................................................................................ 18
2.7. INCLUSIÓN DE ARCHIVOS EN C ....................................................................................................... 19 2.8. OPERADORES .................................................................................................................................. 19
2.8.1. Operadores Aritméticos ............................................................................................................ 19 2.8.2. Operadores Relacionales .......................................................................................................... 19 2.8.3. Operadores Lógicos .................................................................................................................. 19 2.8.4. Operadores Lógicos a Nivel de Bit ........................................................................................... 20 2.8.5. Operadores de Asignación ........................................................................................................ 20
2.9. GRUPO DE PROCEDENCIA DE OPERADORES .................................................................................... 20 2.10. CONVERSIONES DE TIPO.................................................................................................................. 21
3. ELEMENTOS DE PROGRAMACIÓN ESTRUCTURADA ............................................................ 23
3.1. SENTENCIAS DE ENTRADA Y SALIDA DE DATOS. ............................................................................ 23 3.1.1. Introducción:............................................................................................................................. 23 3.1.2. Funciones de entrada y salida .................................................................................................. 23
3.2. SENTENCIAS DE CONTROL .............................................................................................................. 28 3.2.1. Introducción .............................................................................................................................. 28 3.2.2. Sentencias de Control Simples .................................................................................................. 28 3.2.3. Sentencias de Control Cíclicas ................................................................................................. 31
3.3. VARIABLES - ÁMBITO DE VALIDEZ................................................................................................. 35
4. TIPOS ESTRUCTURADOS DE DATOS ........................................................................................... 38
4.1. DEFINICIÓN .................................................................................................................................... 38 4.2. INICIALIZACION DE ARREGLOS ...................................................................................................... 39 4.3. TIPOS ARREGLOS ............................................................................................................................ 41
4.3.1. Unidimensionales: .................................................................................................................... 41 4.3.2. Multidimensionales ................................................................................................................... 42
4.4. GENERALIDADES ............................................................................................................................ 43 4.5. ARREGLOS Y CADENAS .................................................................................................................. 45
4.5.1. ¿Qué es una cadena? ................................................................................................................ 45 4.6. PROCESAMIENTO DE ARREGLOS. .................................................................................................... 54 4.7. PASO DE ARREGLOS A FUNCIONES. ................................................................................................. 54
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 2
5. FUNCIONES ......................................................................................................................................... 57
5.1. QUE SON FUNCIONES ...................................................................................................................... 57 5.2. DECLARACIÓN DE FUNCIONES ........................................................................................................ 57 5.3. LLAMADAS A FUNCIÓN ................................................................................................................... 60 5.4. PARÁMETROS POR VALOR. .............................................................................................................. 60 5.5. PARÁMETROS POR REFERENCIA ...................................................................................................... 62
6. PUNTEROS Y MANEJO DE ARCHIVOS ........................................................................................ 64
6.1. PUNTEROS ...................................................................................................................................... 64 6.1.1. Concepto de Puntero. ................................................................................................................ 64 6.1.2. Declaración de punteros ........................................................................................................... 64 6.1.3. Operadores de Punteros. .......................................................................................................... 64 6.1.4. Aritmética de Punteros.............................................................................................................. 68 6.1.5. Asignación dinámica de Memoria. ........................................................................................... 70 6.1.6. Punteros y arrays. ..................................................................................................................... 74 6.1.7. Inicializaciones de punteros ...................................................................................................... 80 6.1.8. Punteros a funciones. ................................................................................................................ 81 6.1.9. Modificador de acceso const y punteros. .................................................................................. 82 6.1.10. Declaraciones curiosas. ....................................................................................................... 84
6.2. ESTRUCTURAS ................................................................................................................................ 86 6.2.1. Concepto de estructura ............................................................................................................. 86 6.2.2. Declaración .............................................................................................................................. 86 6.2.3. Estructuras y funciones ............................................................................................................. 88 6.2.4. Arrays de estructuras ................................................................................................................ 91 6.2.5. Typedef ...................................................................................................................................... 92
6.3. FICHEROS ....................................................................................................................................... 93 6.3.1. Apertura .................................................................................................................................... 93 6.3.2. Cierre ........................................................................................................................................ 95 6.3.3. Funciones de Escritura y lectura .............................................................................................. 95
7. GRÁFICOS EN EL LEGUAJE C ..................................................................................................... 102
7.1. PROGRAMA TEXTO VS. PROGRAMA GRAFICO ................................................................................ 102 7.2. INICIALIZACIÓN DEL MODO GRAFICO ............................................................................................ 103 7.3. DIBUJAR EN EL MODO GRÁFICO (PRIMITIVOS GRÁFICOS) ............................................................. 104
7.3.1. Pintar un punto: ...................................................................................................................... 104 7.3.2. Dibujar un segmento de recta: ................................................................................................ 105 7.3.3. Para dibujar una circunferencia: ........................................................................................... 105 7.3.4. Para trazar un rectángulo: ..................................................................................................... 105 7.3.5. Dibujar un arco circular: ....................................................................................................... 105 7.3.6. Dibujar un arco elíptico: ........................................................................................................ 105
7.4. CAMBIAR EL ESTILO DE LÍNEA: ..................................................................................................... 106 7.5. FUNCIONES DE RELLENO ............................................................................................................... 106 7.6. FUNCIONES DE ESCRITURA DE TEXTO ........................................................................................... 108
7.6.1. Mostrar texto en un lugar específico ...................................................................................... 108 7.6.2. Seleccionar el estilo del texto .................................................................................................. 108 7.6.3. Cambiar los límites del dibujo. ............................................................................................... 108 7.6.4. Limpiar el viewport actual: ..................................................................................................... 109 7.6.5. Otras ....................................................................................................................................... 109
REFERENCIAS ............................................................................... ¡ERROR! MARCADOR NO DEFINIDO.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 3
1. Introducción
1.1. Historia del C
C fue desarrollado originalmente en los años setenta por Dennis Ritchie en Bell
Telephone Laboratories, Inc. (ahora una sucursal de AT&T). Es el resultado de
dos lenguajes anteriores, el BCPL y el B, que se desarrollaron también en los
laboratorios Bell. C estuvo confinado al uso en los laboratorios Bell hasta 1978,
cuando Brian Kernighan y Ritchie publicaron una descripción definitiva del
lenguaje. La definición de Kernighan y Ritchie se denomina frecuentemente «K.&R
C».
Tras la publicación de la definición de K&R, los profesionales de las
computadoras, impresionados por las muchas características deseables del C,
comenzaron a promover el uso del lenguaje. A mediados de los ochenta la
popularidad del C se había extendido por todas partes. Se habían escrito
numerosos compiladores e intérpretes de C para computadoras de todos los
tamaños y se habían desarrollado numerosas aplicaciones comerciales. Es más,
muchas aplicaciones que se habían escrito originalmente en otros lenguajes se
reescribieron en C para tomar partido de su eficiencia y portabilidad.
Las primeras implementaciones comerciales de C diferían en parte de la definición
original de Kernighan y Ritchie, creando pequeñas incompatibilidades entre las
diferentes implementaciones del lenguaje. Estas diferencias reducían la
portabilidad que el lenguaje intentaba proporcionar. Consecuentemente, el
Instituto Nacional Americano de Estándares** (comité ANSÍ X3J11) desarrolló una
definición estandarizada del lenguaje C. La mayoría de los compiladores e
intérpretes comerciales de C actuales adoptan el estándar ANSCII. Algunos
compiladores también pueden proporcionar características adicionales propias.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 4
En la década de los ochenta, Bjarne Stroustrup desarrolló en los laboratorios Bell
otro lenguaje de programación de alto nivel denominado C++. Éste se basa en C,
y por tanto todas las características estándar de C están disponibles en C++, Sin
embargo, C++ no es una mera extensión de C. Incorpora nuevos fundamentos
que constituyen una base para la programación orientada a objetos —un nuevo
paradigma de la programación de interés para los programadores profesionales—.
1.2. Generalidades y características del Lenguaje “C”
C es un lenguaje de programación estructurado de propósito general. Sus
instrucciones constan de términos que se parecen a expresiones algebraicas,
además de ciertas palabras clave inglesas como if, else, for, do y while. En este
sentido, C recuerda a otros lenguajes de programación estructurados como Pascal
y Fortran. C tiene también algunas características adicionales que permiten su uso
a un nivel más bajo, cubriendo así el vacío entre el lenguaje máquina y los
lenguajes de alto nivel más convencionales. Esta flexibilidad permite el uso de C
en la programación de sistemas (por ejemplo, para el diseño sistemas operativos)
así como en la programación de aplicaciones (por ejemplo, para redactar un
programa que resuelva un complicado sistema de ecuaciones matemáticas o un
programa que escriba las facturas para los clientes).
C se caracteriza por hacer posible la redacción de programas fuente muy
concisos, debido en parte al gran número de operadores que incluye el lenguaje.
Tiene un repertorio de instrucciones relativamente pequeño, aunque las
implementaciones actuales incluyen numerosas funciones de biblioteca que
mejoran las instrucciones básicas. Es más, el lenguaje permite a los usuarios
escribir funciones de biblioteca adicionales para su propio uso. De esta forma, las
características y capacidades del lenguaje pueden ser ampliadas fácilmente por el
usuario.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 5
Hay compiladores de C disponibles para computadoras de todos los tamaños, y
los intérpretes de C se están haciendo cada vez más comunes. Los compiladores
son frecuentemente compactos y generan programas objeto que son pequeños y
muy eficientes en comparación con los programas generados a partir de otros
lenguajes de alto nivel.
Los intérpretes son menos eficientes, aunque resultan de uso más cómodo en el
desarrollo de nuevos programas. Muchos programadores comienzan utilizando un
intérprete, y una vez que se ha depurado el programa (eliminado los errores del
mismo) utilizan un compilador.
Otra característica importante de C es que los programas son muy portables, más
que los escritos en otros lenguajes de alto nivel. La razón de esto es que C deja
en manos de las funciones de biblioteca la mayoría de las características
dependientes de la computadora.
Toda versión de C se acompaña de su propio conjunto de funciones de biblioteca,
que están escritas para las características particulares de la computadora en la
que se instale. Estas funciones de biblioteca están relativamente normalizadas y el
acceso a cada función de biblioteca es idéntico en todas las versiones de C. De
esta forma, la mayoría de los programas en C se pueden compilar y ejecutar en
muchas computadoras diferentes prácticamente sin modificaciones.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 6
1.3. Vista Externa e Interna de un Programa en C
1.3.1. Vista externa
No referimos con vista externa a la forma en que se encuentra estructurado un
programa en C.
A continuación se explica de forma sencilla la estructura de un programa en C.
Todo programa en C consta de uno o más módulos llamados funciones. Una de
las funciones se llama main. El programa siempre comenzará por la ejecución de
la función main, la cual puede acceder a las demás funciones. Las definiciones de
las funciones adicionales se deben realizar aparte, bien precediendo o siguiendo a
main.
Cada función debe contener:
Una cabecera de la función, que consta del nombre de la función, seguido
de una lista opcional de argumentos encerrados entre paréntesis.
Una lista de declaración de argumentos, si se incluyen éstos en la
cabecera.
Una instrucción compuesta, que contiene el resto de la función.
Los argumentos son símbolos que representan información que se le pasa a la
función desde otra parte del programa. (También se llama parámetros a los
argumentos.).
Cada instrucción compuesta se encierra con un par de llaves, { }. Las llaves
pueden contener combinaciones de instrucciones elementales (denominadas
instrucciones de expresión) y otras instrucciones compuestas. Así las
instrucciones compuestas pueden estar anidadas, una dentro de otra. Cada
instrucción de expresión debe acabar en punto y coma (;).
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 7
Los comentarios pueden aparecer en cualquier parte del programa, mientras estén
situados entre los delimitadores /* y */.
Por ejemplo: /* esto es un comentario */
Los comentarios son útiles para identificar los elementos principales de un
programa o para explicar la lógica subyacente de éstos.
He aquí un programa en C elemental que calcula el área de un círculo y escribe el
resultado calculado. Luego se nombran todas las partes que contiene el programa
en forma de comentarios.
/*Programa para calcular el área de un círculo*/
#include<stdio.h>
#define PI 3.1416
main ()
{
float area, radio;
radio = 10;
area = PI * (radio * radio);
printf ("Circulo.\n");
printf("%s%f\n\n", "Area de circulo radio 10: ", area);
}
Estructura de un Programa en C
/ * titulo (comentario) */
/ * acceso a archivo de biblioteca */
/ * definicion de una constante*/
/ * cabecera de función */
/ * declaración de variables */
/ * inicializacion de la variable radio*/
/ * expresion para calcular el area */
/ * instrucción de entrada*/
/ * instrucción de salida*/
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 8
1.3.2. Vista interna
Nos referimos con vista interna, al proceso que se lleva acabo internamente, para
crear un programa ejecutable de un programa fuente escrito en c.
Primeramente hablaremos del compilador de C, que se llama GCC.
GCC es un compilador integrado del proyecto GNU para C, C++, Objective C y
Fortran; puede recibir un programa fuente en cualquiera de estos lenguajes y
generar un programa ejecutable binario en el lenguaje de la máquina donde ha de
correr. El compilador es capaz de detectar ciertos errores durante el proceso de
compilación, enviando al usuario el correspondiente mensaje de error.
La sigla GCC significa "GNU Compiler Collection". Originalmente significaba "GNU
C Compiler".
1.3.2.1. Etapas de compilación.
El proceso de compilación involucra cuatro etapas sucesivas:
Preprocesamiento, compilación, ensamblado y enlazado.
Para pasar de un programa fuente escrito por un humano a un programa
ejecutable se realizan estas cuatro etapas en forma sucesiva.
Preprocesado.
En esta etapa se interpretan las directivas al preprocesador. Entre otras cosas, las
variables inicializadas con #define son sustituidas en el código por su valor en
todos los lugares donde aparece su nombre.
Usaremos el ejemplo utilizado para demostrar la vista externa de un programa en
c, para explicar la etapa del preprocesado.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 9
/*programa para calcular el area de un circulo */
#include<stdio.h>
#define PI 3.1416
main ()
{
float area, radio;
radio = 10;
area = PI * (radio * radio);
printf ("Circulo.\n");
printf("%s%f\n\n", "Area de circulo radio 10: ", area);
}
En este ejemplo, en la expresión ”area=PI *(radio * radio);”, la variable PI se
reemplaza por el valor antes definido (3.1416).
Compilación.
La compilación transforma el código C en el lenguaje ensamblador propio del
procesador de nuestra máquina.
Ensamblado.
El ensamblado transforma el programa escrito en lenguaje ensamblador a código
objeto, un archivo binario en lenguaje de máquina ejecutable por el procesador.
Enlazado
Las funciones de C incluidas en nuestro código, tal como printf () en el ejemplo, se
encuentran ya compiladas y ensambladas en bibliotecas existentes en el sistema.
Es preciso incorporar de algún modo el código binario de estas funciones a
nuestro ejecutable. En esto consiste la etapa de enlace, donde se reúnen uno o
más módulos en código objeto con el código existente en las bibliotecas.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 10
2. Operadores y Expresiones
2.1. Tipos fundamentales de datos
C no soporta un gran número de tipos de datos predefinidos, pero tiene la
capacidad para crear sus propios tipos de datos. Todos los tipos de datos simples
o básicos de C son, esencialmente, números. Los tipos de datos básicos son:
Enteros
Números de coma flotante
Caracteres
A continuación se detalla su nombre, el tamaño que ocupa en memoria y el rango
de sus posibles valores.
Tipo Ejemplo Tamaño Rango de valores
char „C‟ 1 byte 0 a 255
int 1024 2 bytes -32768 a 32767
float 10.5 4 bytes 3'4 E-38 a 3'4 E+38
double 0.0025 8 bytes 1'7 E-308 a 1'7 E+308
2.1.1. Caracteres (tipo char)
Las variables carácter (tipo char) contienen un único carácter y se almacenan en
un byte de memoria (8 bits). La declaración de variables tipo carácter puede tener
la forma:
char nombre;
char nombre1, nombre2, nombre3;
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 11
Se puede declarar más de una variable de un tipo determinado en una sola
sentencia. Se puede también inicializar la variable en la declaración. Por ejemplo,
para definir la variable carácter letra y asignarle el valor a, se puede escribir:
char letra = ’a’;
A partir de ese momento queda definida la variable letra con el valor
correspondiente a la letra a. Recuérdese que el valor ‟a‟ utilizado para inicializar la
variable letra es una constante carácter.
También puede utilizarse una variable char para dar valor a otra variable de tipo
char:
carácter = letra; /*Ahora carácter es igual a ’z’*/
Como una variable tipo char es un número entero pequeño (entre 0 y 255), se
puede utilizar su contenido de la misma forma que se utiliza un entero, por lo que
están permitidas operaciones como:
letra = letra + 1;
letra _ minúscula = letra _ mayúscula + (’a’ - ’A’);
2.1.2. Números enteros (tipo int)
Una variable tipo int se almacena en 2 bytes (16 bits). Una variable entera se
declara, o se declara y se inicializa en la forma: <tipo de dato> <nombre de la
variable> = <valor inicial>;
Ejemplos:
int numero;
int nota = 10;
int valor1, valor2;
Los enteros son adecuados para aplicaciones que trabajen con datos numéricos.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 12
2.1.3. Números reales (tipo float)
En muchas aplicaciones hacen falta variables reales, capaces de representar
magnitudes que contengan una parte entera y una parte fraccionaria o decimal.
Estas variables son del tipo punto flotante. Estos requieren 4 bytes de memoria
(32 bits).
Las variables tipo float se declaran de la forma: float número _ real; las variables
pueden ser inicializadas en el momento de la declaración, de forma análoga a las
variables tipo int.
Ejemplos:
float valor;
float valor1, valor2;
float valor = 99.99;
2.1.4. Números reales (tipo double)
Las variables tipo float tienen un rango y sobre todo una precisión muy limitada,
insuficiente para la mayor parte de los cálculos técnicos y científicos. Este
problema se soluciona con el tipo double, que utiliza 8 bytes (64 bits) para
almacenar una variable.
Las variables tipo double se declaran de forma análoga a las anteriores:
double real_grande;
Por último, existe la posibilidad de declarar una variable como long double, estas
se declaran en la forma:
long double real;
Ejemplos:
double h;
long double mayor;
double valor = 0.000056;
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 13
2.2. Identificadores
Un identificador es una secuencia de caracteres, letras, dígitos y subrayados
(_).Un identificador es el nombre que damos a las variables y funciones. El primer
carácter debe ser una letra (algunos compiladores admiten carácter de
subrayado). Las letras mayúsculas y minúsculas son diferentes.
Ejemplos:
nombre_clase Indice Dia_Mes_Año
elemento_mayor Cantidad_Total Fecha_Compra_Casa
a Habitacion120 i
En C el identificador puede ser de cualquier longitud; sin embargo, el compilador
ignora cualquier carácter fuera de los 32 primeros. C es sensible a las
mayúsculas. Por consiguiente, C reconoce como distintos los identificadores
ALFA, alfa y ALFa. Un identificador no puede contener espacios en blanco, ni
otros caracteres distintos de los citados, como por ejemplo (*,+, etc.). En un
identificador no pueden ser utilizadas las palabras reservadas, tales como if,
switch o else.
2.3. Palabras Claves
En C, como en cualquier otro lenguaje, existen una serie de palabras clave
(keywords) que el usuario no puede utilizar como identificadores. Estas palabras
sirven para indicar al computador que realice una tarea muy determinada (desde
evaluar una comparación, hasta definir el tipo de una variable) y tienen un especial
significado para el compilador. A continuación vemos algunas de estas palabras
clave:
auto break case char const continue default do
double else enum extern float for goto if
int long register return short signed sizeof static
struct switch typedef union unsigned void volatile while
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 14
2.4. Comentarios
A la hora de programar es conveniente añadir comentarios para poder saber que
función tiene cada parte del código. Además facilitaremos el trabajo a otros
programadores que puedan utilizar nuestro archivo fuente.
Para poner comentarios en un programa escrito en C usamos los símbolos /* y */:
/* Este es un ejemplo de comentario */
/* Un comentario también puede
estar escrito en varias líneas */
El símbolo /* se coloca al principio del comentario y el símbolo */ al final. El
comentario, contenido entre estos dos símbolos, no será tenido en cuenta por el
compilador
2.5. Variables
Variables en Lenguaje C
Las variables en C pueden ser de varios tipos y serán utilizadas en función del tipo
de datos que queramos almacenar en ellas. Las variables NOMBRE, nombre,
Nombre son tres variables totalmente distintas. (Lenguaje Case Sensitive) y el
nombre de una variable no puede comenzar por número (pero puede contener
varios) ni tener caracteres especiales (se admite el guión bajo).
Por ejemplo: numero1, j10a, num_alumno, serían variables válidas y 1abc,
numero?, num/alumnos serían variables inválidas.
Según dónde estén declaradas, las variables pueden ser globales (declaradas
fuera de todo procedimiento o función) o locales (declaradas dentro de un
procedimiento o función). Las primeras serán accesibles desde todo el código
fuente y las segundas sólo en la función donde estén definidas.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 15
Por otra parte, según el tipo de datos a almacenar, las variables serán de tipo
entero (int), flotante (float), doble (double) o carácter (char).
El tipo int se utiliza para guardar números enteros, es decir, que no tienen
decimales. El rango de valores admitidos varía según la CPU utilizada. Para
mostrar los valores contenidos en este tipo de variables, nos serviremos de %i o
%d.
Ejemplo:
/*Programa para mostrar variable en pantalla*/
#include<stdio.h>
main ()
{
int x=8;
int y=9;
printf("%i\n",x);
/*Podemos escribir varias variables en el mismo printf:*/
printf("%i %i",x,y);
}
Siendo el resultado por pantalla:
8
8 9
Para todos los tipos, podemos definir variables en una sola línea: int x=8,y=9;
tendría las mismas consecuencias que el código anterior. Pero, debemos tener
cuidado, pues: int x,y=9; no daría lo mismo: "x" no tendría valor e "y" valdría 9.
Si imprimimos una variable a lo que no hemos asignado valor, el compilador no
reconocerá ningún error, pero se imprimirá un valor cualquiera. Además, toda
variable puede cambiar su valor durante la ejecución del programa y ser impresa
las veces que creamos oportunas.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 16
Tras estas pequeñas aclaraciones, seguiremos comentando el uso de variables
con el tipo float. Este tipo sirve para almacenar números decimales o reales, así
como el double. El rango de valores admitidos en tan amplio que rara vez se nos
quedará obsoleto.
El modificador para operar con estos datos es %f y lo haremos del mismo modo
que con int. En otros artículos entraremos en detalles de cómo calcular el rango de
valores posibles y el tamaño de las variables.
De momento, continuamos el tipo char, capaz de almacenar un único carácter.
Internamente, un carácter es almacenado como un número, comprendido entre 0 y
128, que hace referencia a su código ascii (ascii standard).
Por ejemplo, declarando:
char letra='A'; /*los caracteres siempre entre comillas simples
tendríamos el mismo efecto que: char letra= 65; puesto que el ascii de A
es 65.*/
El modificador del tipo carácter es %c y también es empleado de la misma forma.
Si declaramos una variable char y imprimimos con %d, nos devolverá su código
ascii... así podremos crearnos nuestra propia tabla de caracteres. Y, si,
declaramos una variable de tipo int y escribimos su valor con %c, nos devolverá el
carácter correspondiente a ese número en el código ascii.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 17
2.6. Constantes
Al contrario que las variables, las constantes mantienen su valor a lo largo de todo
el programa.
Para indicar al compilador que se trata de una constante, usaremos la
directiva
#define
#define <identificador> <valor>
Observa que no se indica el punto y coma de final de sentencia ni tampoco el tipo
de dato.
Observa que no se indica el punto y coma de final de sentencia ni tampoco el tipo
de dato.
La directiva #define no sólo nos permite sustituir un nombre por un valor numérico,
sino también por una cadena de caracteres.
El valor de una constante no puede ser modificado de ninguna manera.
/* Uso de las constantes */
#include <stdio.h>
#define pi 3.1416
#define escribe printf
main() /* Calcula el perimetro */
{
int r;
escribe("Introduce el radio: ");
scanf("%d",&r);
escribe("El perimetro es: %f",2*pi*r);
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 18
2.6.1. Constantes enteras:
Una constante entera es una cantidad sin punto decimal(con signo positivo
opcional) cuyos valores mínimo y máximo en precisión sencilla (ENTERO CORTO
que se almacena en 2 bytes) son: -32768 y 32768.
una variante es la entera sin signo (que se almacena en 2 bytes), con los valores
mínimo y máximo de: 0 y 65535
Mientras que en doble precisión (ENTERO LARGO que se almacena en 4 bytes):
-2147843648 y 2147843648
2.6.2. Constantes reales:
Una constante real es una cantidad que incluye punto decimal (con signo positivo
opcional) cuyos valores mínimo y máximo aproximados (en precisión sencilla de 4
bytes) son: -3.4 10-38 y 3.4 1038 (6 dígitos válidos).
Mientras que sus valores aproximados de doble precisión (8 bytes) son: -1.79 10-
308 y 1.79 10308
2.6.3. Constantes alfanuméricas.
Una constante alfanumérica es un conjunto de caracteres delimitados por comillas
dobles como por ejemplo: "-32,768","uno mas uno","ABCFXY","SI","NO"
Con una longitud máxima de 255 caracteres en C. Es importante comentar que si
el valor alfanumérico es un solo carácter, se usa comilla simple, por ejemplo: 'a','u'
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 19
2.7. Inclusión de Archivos en C
La inclusión de archivos no es más que la declaración o la llamada de la o las
librerías donde se encuentran definidas las instrucciones del lenguaje. Las
librerías pertenecen al desarrollo del lenguaje C. Una de las librerías importantes
del lenguaje es la función main(), que sin ella no permite que el programa se
desarrolle, la cual se delimita con llaves.
2.8. Operadores
Son símbolos que indican al compilador que se lleve a cabo específicas
manipulaciones matemáticas o lógicas. Hay cinco clases de operadores
aritméticos, lógicos y bit, relacionales y de Asignación.
2.8.1. Operadores Aritméticos
+ Suma
- Resta
* Multiplicación
/ División
% División Modulo
-- Decremento
++ Incremento
2.8.2. Operadores Relacionales
> Mayor
>= Mayor o Igual
< Menor
<= Menor o Igual
== Igual
!= Distinto o Diferente
2.8.3. Operadores Lógicos
&& and (y lógico)
|| or (o lógico)
! not
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 20
2.8.4. Operadores Lógicos a Nivel de Bit
& Operación and
| Operación or
^ Operación xor
<< Desplazamiento a la Izquierda
>> Desplazamiento a la Derecha
2.8.5. Operadores de Asignación
= Asignación Simple
+= Suma con Asignación
-= Resta con Asignación
*= Multiplicación con Asignación
/= División con Asignación
%= División Módulo con Asignación
2.9. Grupo de Procedencia de Operadores
Categoría del Operador Operadores Asociatividad
Operadores Monarios ( )
-, ++, --, !, sizeof Derecha Izquierda
Multiplicación, División,
División Modulo *, /, % Izquierda Derecha
Suma y resta aritméticas +, - Izquierda Derecha
Operadores relacionales <, <=, >, >= Izquierda Derecha
Operadores de Igualdad ==, != Izquierda Derecha
Operadores Lógicos && Izquierda Derecha
Operador condicional ?: Derecha Izquierda
Operadores de Asignación =, +=, -=, *=, /=, %= Derecha Izquierda
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 21
2.10. Conversiones de tipo
Se conoce como conversión de tipo al proceso mediante el cual se ajusta el valor
de una variable de un tipo para que puede caber o ser usado como una variable
de otro tipo. Las conversiones se hacen todo el tiempo en un programa, por
ejemplo cuando sumamos un valor entero a un valor real. Pero en estos casos el
compilador se encarga de convertir el valor, son conversiones implícitas.
Si al convertir ganamos precisión, por ejemplo ir de un tipo int a float, se conoce
como promoción de tipos. Cuando hay pérdida de precisión, las conversiones se
conocen como democión de tipos. El compilador normalmente emite un aviso o
"warning", cuando se hace una democión implícita, es decir cuando hay una
democión automática.
Existe una forma en que el programador puede controlar estas conversiones, y así
evitar estos warning a esta técnica se le conoce como casting o conversiones
explicitas.
En general, el uso de "casting" es obligatorio cuando se hacen asignaciones, o
cuando se pasan argumentos a funciones con pérdida de precisión. En el caso de
los argumentos pasados a funciones es también muy recomendable aunque no
haya pérdida de precisión. Eliminar los avisos del compilador demostrará que
sabemos lo que hacemos con nuestras variables, aún cuando estemos haciendo
conversiones de tipo extrañas.
Un "casting" tiene una de las siguientes formas:
(<nombre de tipo>)<expresión> ó <nombre de tipo>(<expresión>)
Esta última es conocida como notación funcional.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 22
Ejemplo:
/* Uso de las constantes */
#include <stdio.h>
#define pi 3.1416
#define escribe printf
main() /* Calcula el perimetro */
{
/*Variable tipo entero*/
int x = 10;
/*Variable tipo real*/
double d = 24.251555;
/*Conversion de tipo realizada por el compilador*/
printf("Antes de convertir x = %d y d = %f\n");
x = d; /*El compilador emitira un warning "converting int to
double"*/
printf("Después de convertir por el compilador x = %d y d = %f\n");
/*Conversión explicita por el programador*/
x = (int) d;
/*Otra forma*/
x = int(d);
printf("Después de convertir explicitamente x = %d y d = %f\n");
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 23
3. Elementos de Programación estructurada
3.1. Sentencias de Entrada y Salida de Datos.
3.1.1. Introducción:
Las funciones de Entrada y Salida de Datos se encuentran en la librería stdio.h y
básicamente son getchar, putchar, scanf, printf, gets y puts. Estas son utilizadas
para el flujo o transferencia de datos entre el usuario y la computadora.
3.1.2. Funciones de entrada y salida
3.1.2.1. Función getchar:
Esta no requiere de argumento alguno y su función es la de entrada de un
carácter.
Su sintaxis es:
getchar ();
, por lo tanto asignará y leerá un carácter desde el teclado. También se puede
utilizar para encontrar un fin de archivo (EOF).
Ejemplo:
/*Uso de getchar()*/
#include <stdio.h>
main()
{
char a; /*Declaración de a como carácter*/
a = getchar(); /*Lee un carácter del teclado y lo asigna a la
variable a*/
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 24
3.1.2.2. Función putchar:
Esta complementa a la función putchar, se utiliza para visualizar en pantalla un
carácter, y recibe como parámetro la variable que desea usarse.
Su sintaxis es:
putchar (variable usada);.
Ejemplo:
/*Uso de putchar*/
#include <stdio.h>
void main (void)
{
char b; /* Declara b */
b = getchar(); /* Lee b */
putchar(b); /* muestra en pantalla el valor de b */
}
3.1.2.3. Función scanf:
Se emplea para leer cualquier dato numérico, de caracteres sueltos o en cadena
introducido por el usuario.
Su sintaxis es:
scanf(“cadena de control”, arg1, arg2,....,argn);
en la cual la cadena de control son argumentos formados por el signo de
porcentaje (%) y un carácter de abreviación el cual representa cada tipo de dato.
Y arg1, arg2,… argn son los nombres de las variables que convendrán los datos
que los cuales serán leídos, estos nombres deberán ir precedidos por un
ampersand (&) cada uno, excepto los nombres de formaciones como cadenas de
caracteres.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 25
Especificadores de formato
%d Lee variable entera (decimal)
%c Lee un carácter
%s Lee una cadena de caracteres
%f Lee variable punto flotante
%e Lee variable punto flotante pero con notación científica
%u Lee variable entera sin signos
%x Lee variable hexadecimal
%g Lee variable punto flotante
%h Lee variable entera cortas
%o Lee variable enteras octales
Ejemplo:
/*Uso de scanf*/
#include <stdio.h>
main()
{
int a, b; /*Declara los enteros a y b*/
char c[10]; /*declara la variable c de tipo char*/
/*Lee un entero, un entero sin signo y una cadena de caracteres*/
scanf("%d %u %s", &a, &b, c);
}
3.1.2.4. Función printf:
La función printf se utiliza para la escritura de datos, y esta es análoga a la función
scanf.
Su sintaxis es:
printf(“cadena de control”, arg1, arg2,… argn);
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 26
donde la cadena de control hace referencia a la abreviación de los datos de salida
al igual que con la sentencia de entrada scanf, pero a diferencia de estos sus
argumentos no van precedidos de ampersands ya que no representan direcciones
de memoria.
Especificadores de formato:
Carácter de conversión Significado
C Carácter
D Entero decimal
E, f, g Entero en coma flotante
H Entero corto
I Entero decimal, octal o hexadecimal
O Entero octal
S Cadena de caracteres seguida de un espacio
U Entero decimal sin signo
X Entero hexadecimal
[…] Cadena de caracteres que puede contener
espacios
Ejemplo:
/*Uso de printf*/
#include "stdio.h"
main()
{
char frase[20]="Este es un ejemplo";
printf(" %s", frase); /* Imprime o muestra en pantalla el array
frase*/
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 27
3.1.2.5. Función gets:
Su uso facilita la lectura de datos ya que no recibe mas parámetros que el nombre
de la variable que queremos leer, esta se introducirá del teclado, deberá ser una
cadena de caracteres, podrá contener espacios y terminará con un salto de línea.
Ejemplo:
/*Uso de gets*/
#include <stdio.h>
main()
{
char ejem[16];
/*Lee una cadena con espacio para 15 caracteres mas el carácter
nulo*/
gets(ejem);
}
3.1.2.6. Función puts:
Esta última función de las más comunes de entrada y salida de datos es el
equivalente a gets, posee las mismas características, pero en salida de datos.
Ejemplo:
#include “stdio.h”
void main(void)
{
char arreglo[30];
gets(arreglo); /* Lee un arreglo*/
puts(arreglo); /* imprime el mismo arreglo*/
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 28
3.2. Sentencias de Control
3.2.1. Introducción
Estas se utilizan para hacer comprobaciones de condiciones, para realizar
repetidamente determinadas instrucciones, estas son palabras reservadas, por lo
tanto no necesitan de inclusión de librerías para su uso.
Principalmente son if-then-else, for, while, do-while, switch, break, goto y continue.
3.2.2. Sentencias de Control Simples
3.2.2.1. If – else
Esta sentencia evalúa una condición, para realizar una de dos acciones,
dependiendo si se cumple o no la condición.
Es decir el valor de la expresión a evaluar puede ser verdadero o falso.
La parte else de la sentencia es opcional, y su sintaxis sería:
if(expresión evaluada) instrucción1 ;
else instrucción2
ó también de forma sencilla
if(expresión) instrucción.
Otra característica de esta sentencia es que se pueden anidar otros if-else dentro
de otros.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 29
Ejemplo:
#include <stdio.h>
#include <conio.h>
main()
{
int a, b, c;
scanf( "%d %d %d", &a, &b ,&c);
if( a + b > c)
{
printf(" a + b es mayor que c");
}
else
{
if( a + b < c)
printf(" a + b es menor que c");
}
getch();
}
3.2.2.2. switch
Esta se utiliza para realizar una operación o varias de un grupo de instrucciones.
Su sintaxis es la siguiente:
switch (expresión) instrucción;
3.2.2.3. break
Se usa para terminar la ejecución de bucles o salir de un switch.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 30
Ejemplo:
/*Uso de switch y break:*/
#include <stdio.h>
#include <conio.h>
main()
{
int a;
scanf( "%d" , &a);
switch( a)
{
case 1: printf("Ha introducido un 1");
break;
case 2: printf("Ha introducido un 2");
break;
case 3: printf("Ha introducido un 3");
break;
default: printf("Intente del uno al tres");
}
getch();
}
3.2.2.4. goto
Esta instrucción se utiliza para cambiar el flujo secuencial de un programa,
trasladando el control hacia la parte donde se encuentra la etiqueta.
Su sintaxis es:
goto etiqueta;
y etiqueta es un indicador que se encuentra en la parte donde queremos transferir
el control de la forma etiqueta: instrucción.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 31
Ejemplo:
/*Uso de goto*/
#include <stdio.h>
#include <conio.h>
main()
{
int h, i, j, k;
Mensaje: printf("Los Datos que Introduzca deben ser tres enteros
distintos de cero:\n");
printf("h, i y j: ");
scanf("%d %d %d" , &h, &i, &j);
if( (h!=0) && (i!=0) && (j!=0) )
{
k = h + i - j;
}
else
{
printf("Vuelva a intentarlo:\n");
goto Mensaje;
}
printf("El resultado de h + i - j = %d", k);
getch();
}
3.2.3. Sentencias de Control Cíclicas
En numerosas ocasiones, nos interesa repetir mas de una vez ciertas operaciones
o instrucciones. También nos suele interesar los datos resultantes de algunas
operaciones para saber que operaciones debería ejecutar el programa y que
operaciones no. Para esto existen las sentencias de control de flujo y bucles.
Estas sentencias son: for, while, do while, if, else, switch, break y continue.
Aunque las sentencias if, else, break y continue no se utilizan para repetir un
proceso, se utilizan para cambiar el flujo de control de un programa. En función
al resultado de ciertas condiciones postuladas por el programador.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 32
3.2.3.1. Bucle for:
Su sintaxis es la siguiente:
for( dar valores iniciales ; condiciones ; incrementos )
{
Conjunto de instrucciones a ejecutar en el bucle
}
El bucle funciona recibiendo primero el valor inicial desde donde se va a empezar
a contar el número de repeticiones. Después recibe la condición a validar al fin de
cada ciclo para que el bucle se siga ejecutando. Y después recibe el incremento, o
decremento, que la variable de valor inicial recibirá para que la condición se haga
falsa y el bucle llegue a un fin.
A continuación veremos un ejemplo del uso del bucle for:
/*Uso de for*/
#include <stdio.h> /*declaración de bibliotecas de funciones*/
#include <conio.h>
main() /*inicio de la función principal*/
{
int i,j; /*declaración de variables*/
/*inicio del bucle for*/
for( i=0, j=5; i<10; i++, j=j+5)
{
/*instrucciones a repetir*/
printf("¡Hola!");
printf("\nEsta es la repetición numero: %d.", i);
printf("\nEl valor de j es: %d", j);
}
/*detenemos el control hasta que se oprima una tecla para visualizar
datos*/
getch();
return 0;
} /*fin de función principal*/
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 33
Este ejemplo declara un bucle for que se repetirá desde i=0 hasta i<10 (es decir
10 veces) la oración ¡Hola!, contara el numero de repeticiones e incrementara el
valor de “j” en 5 cada ciclo, hasta que la condición sea falsa.
Como podemos observar en este ejemplo, cada parámetro que recibe el bucle
también puede estar seccionado, separados por comas y pudimos haber tenido
mas de una condición así como también pudimos haber omitido cualquier
parámetro.
3.2.3.2. Bucle while:
Su sintaxis es la siguiente:
while ( condición )
{
bloque de instrucciones a ejecutar
}
El bucle while ejecuta un conjunto de instrucciones mientras la condición
postulada se cumpla.
Por ejemplo:
/*Uso de while*/
#include<stdio.h> /*declaración de bibliotecas de funciones a usar*/
#include<conio.h>
main() /*inicio del programa principal*/
{
int contador=100; /*declaración de valor constante a variable*/
while(contador > 0) /*inicio del bucle while*/
{ /*instrucciones a repetir*/
printf("Faltan %d para terminar el bucle while\n",
contador);
contador--; /*reduce el numero de contador para poder hacer
falsa la condición*/
}
getch(); /*detiene el programa para su visualización*/
return 0;
} /*Fin función principal*/
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 34
En este ejemplo se imprime de manera regresiva el número de veces que hacen
falta para finalizar el bucle. Empezando desde 100. El orden de las instrucciones
es importante en este bucle ya que si intercambiamos de posición las
instrucciones el conteo hubiese empezado desde 99. Es muy importante ponerle
atención al orden de instrucciones en este bucle ya que puede producir errores
muy difíciles de encontrar.
3.2.3.3. Bucle do while:
Su sintaxis es la siguiente:
do
{
Instrucciones a ejecutar
} while ( condición );
La gran diferencia entre el while y el do while es que en este último la condición va
después del conjunto de instrucciones, así dichas instrucciones podrán ejecutarse
por lo menos una vez. Su uso es similar al while.
Tenemos el siguiente ejemplo:
/*Uso de do-while*/
#include<stdio.h> /*declaración de bibliotecas de funciones*/
#include<conio.h>
main() /*inicio de la función principal*/
{
int i=1, j; /*declaración de variables*/
do /*inicio del bucle do-while*/
{
j=i*2; /*sentencias a evaluar dentro del bucle*/
printf("La tabla del dos:");
printf("2 x %d = %d\n",i,j); i++;
}while(i<=10); /*fin del bucle y evaluacion de la condicion*/
getch();
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 35
En este ejemplo se imprime la tabla del 2 desde 1 hasta diez.
Aunque no es recomendado, debido a que se denota como mala practica de
programación, si solo se desea repetir una sola instrucción, utilizando los
diferentes bucles, el programador puede omitir las llaves. Aunque no sea un
problema en programas sencillos, estos pequeños detalles son los que pueden
ahorrar mucho tiempo a la hora de corregir errores en programas mas complejos.
3.2.3.4. Función continue:
Su sintaxis es la siguiente:
continue;
Funciona de forma similar a break. Sin embargo, en vez de forzar la terminación,
break. Sin embargo, en vez de forzar la terminación, continue fuerza una nueva
iteración del bucle y salta cualquier código que exista entremedio.
Para el bucle for, continue hace que se ejecuten las partes de prueba condicional
y de incremento del bucle. Para los bucles while y do-while, el control del
programa pasa a la prueba condicional.
3.3. Variables - Ámbito de Validez
Como ya sabemos una variable en C es un espacio reservado en la memoria del
computador para guardar datos fluctuantes dentro de la ejecución de un programa.
Es importante recordar que todos los datos guardados e interpretados por el
computador son siempre binarios, así que el tipo nos permite decirle al compilador
como interpretar los datos. El tipo de variable determinara el trato que se le dará a
cada variable.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 36
Es en este último punto en donde nos enfocaremos en este capitulo. Como
programador una de las primeras cosas que se aprenden es que el usuario de un
software es muy impredecible en cuanto a los datos que se le piden. Sin importar
que tan claras sean las instrucciones, es necesario saber si lo tecleado por el
usuario se adapta a los datos pedidos o no. Para poder así tomar medidas
adecuadas para evitar el fallo de cualquier aplicación determinada.
Leer lo tecleado por el usuario y comparar si los datos adquiridos concuerdan con
el tipo solicitado es lo que llamamos validación. Este ámbito constituye una función
muy importante dentro de la programación desde sus principios ya que la fiabilidad
de un software depende de los datos que contiene y es uno de los requisitos mas
exigidos. Y los programas desarrollados en C no son una excepción.
Por ejemplo, si se desea verificar, en un programa que pide una carácter
determinado, sea solo caracteres y solamente caracteres se podría utilizar la
siguiente validación:
/*Uso de validacion*/
#include<stdio.h>
#include<ctype.h>
#include<conio.h>
main()
{
int a;
Again: printf("Introduzca un carcter: ");
a = getchar();
tolower(a);
if(a >= 'a' && a <= 'z')
{ printf("El character intorducido fue el: %c\n",a); }
else
{ printf("El dato no es valido, intente nueva mente\n");
goto Again; }
getch();
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 37
Otro ejemplo seria en un programa en donde se quiere abrir un fichero, se tiene
que verificar que el fichero no este vacío y se puede utilizar la siguiente validación:
/*Uso de validación 2*/
#include <stdio.h>
main(){
FILE* archivo;
int f[4];
double d[4];
archivo = fopen("fichero.txt","r");
if(archivo!=NULL){
int f[4],i;
double d[4];
fscanf(archivo,"%d %d %d %d\n",
&f[0],&f[1],&f[2],&f[3]);
fscanf(archivo,"%d, %d, %d, %d\n",
&f[0],&f[1],&f[2],&f[3]);
fscanf(archivo,"%lf ; %lf ; %lf ; %lf\n",
&d[0],&d[1],&d[2],&d[3]);
fclose(archivo);
}
}
En ambos ejemplos se nota la importancia de validar los datos con los que se
trabajan en un programa. En el caso de que un valor inesperado sea leído el
programa tomara un curso apropiado para asegurar que el programa no falle y los
datos obtenidos no sean erróneos.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 38
4. Tipos Estructurados de Datos
4.1. Definición
Arreglos
Es una variable que maneja una gran cantidad de datos del mismo tipo bajo un
mismo nombre .A cada elemento se le asigna un espacio de memoria, según el
orden o secuencia, se puede acceder a cada elemento según el espacio o
localidad de memoria que este elemento tenga.
Un arreglo debe de ir acompañado, del tipo del almacenamiento, este es opcional
los valores por defecto son automático para arreglos definidos dentro de un
función, y externo para arreglos definidos fuera de una función, también de una
especificación de tamaño (numero de elementos), en este caso seria un digito
entero positivo, encerrado entre corchetes.
El lenguaje C si se accede a un arreglo de n términos y el usuario se referencia a
un elemento que sobrepase al n-números de elementos, este no provoca ningún
mensaje de error en 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,
esto es, si se dimensionar. Como programador se es responsable de asegurar que
todos los arreglos sean lo suficientemente grandes para guardar lo que pondrá en
ellos el programa.
Por ejemplo, para declarar un arreglo de enteros llamado demos con diez
elementos se hace de la siguiente forma:
int demos[10];
En C, todos los arreglos usan cero como índice para el primer elemento. Por tanto,
el ejemplo anterior declara un arreglo de enteros con diez elementos desde
demos[0] hasta demos[9].
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 39
Tipo_almacenamiento tipo nombre [número _ elementos];
Tipo: el tipo de dato que poseen cada uno de los elementos.
Nombre: variable que identifica el arreglo.
Numero de elementos: este indica la cantidad de elementos que contendrá el
arreglo, este referenciara el índice del elemento al cual será accedido
NOTA: No es el contenido que tendrá el arreglo.
Tipo_almacenamiento: se refiere al tipo de almacenamiento
4.2. Inicializacion De Arreglos
Un arreglo puede ser inicializado es decir asignárseles valores iniciales, los
arreglos automáticos no pueden ser inicializados, no obstante las definiciones de
arreglos estáticos y externos pueden asignárseles valores iniciales, los valores
inicializados deben de estar encerrados entre corchetes, escritos en el orden en
que serán asignados. Cuando un arreglo es inicializado es opcional número de
elementos, ya que los corchetes pueden estar vacíos.
Ejemplo: /*Uso de arreglos*/
#include <stdio.h>
#include <conio.h>
main()
{
double carro[20];
int i, t = 20;
for(i = 0; i < t; i++)
{
printf("Ingrese el valor %d: ", i+1);
scanf("%lf", &carro[i]);
}
for(i = 0; i < t; i++)
{ printf("%f\n", carro[i]); }
getch();
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 40
Inicializándolo en la misma declaración, en la forma:
float v[6] = {1., 2., 3., 3., 2., 1.};
double d[] = {1.2, 3.4, 5.1};
int f[100] = {0};
int h[10] = {1, 2, 3};
int mat[3][2] = {{1, 2}, {3, 4}, {5, 6}};
Ejemplo:
/*Uso de arreglos*/
#include <stdio.h>
#include <conio.h>
main()
{
float v[6] = {1.0f, 2.0f, 3.0f, 3.0f, 2.0f, 1.0f};
int c;
printf("Valores del arreglo*/
for(c = 0; c < 6; c++)
{
printf("%f\n", v[c]);
}
getch();
}
Donde en el primer y segundo ejemplo es necesario poner un punto decimal tras
cada cifra, para que ésta sea reconocida como un valor de tipo float o double.
Recuérdese que, al igual que las variables escalares correspondientes, los arrays
con modo de almacenamiento external y static se inicializan a cero
automáticamente en el momento de la declaración.
Sin embargo, esto no está garantizado en los arrays auto, y el que se haga o no
depende del compilador.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 41
4.3. Tipos Arreglos
Los arreglos se clasifican en dos grupos:
4.3.1. Unidimensionales:
También llamados vectores llamados así, de una dimensión o lineales; sin
embargo no cambia el ámbito de los elementos, todos contendrán un espacio de
memoria.
Su sintaxis:
Tipo-datos nombre [tamaño];
Si notas el arreglo contiene un par de corchetes que indican que los elementos
del arreglo estarán almacenados de forma lineal en cada espacio de memoria
Ejemplo char Demos [4];
1 2 3 4
Demos [0] Demos [1] Demos [2] Demos [3]
Cada casilla corresponde a un único número de espacio o también llamados
índices el tamaño del arreglo es de 4 elementos como se especifica en el grafico.
En C el primer elemento de un arreglo esta indicado con el índice cero, así si se
quiere acceder al primer elemento de un arreglo se escribirá de esta forma: char
Demo [0], y así sucesivamente i se quiere acceder al n-enésimo elemento seria
char Demo [n-1].
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 42
4.3.2. Multidimensionales
También existen arreglos multidimensionales, C por ser un lenguaje muy
interactivo incorpora en su gama de herramientas, este tipo de arreglos los cuales
permiten almacenar mas información más organizadas, por lo general son
llamadas matrices.
Las dimensiones que se incluyen en este grupo son arreglos bidimensionales (dos
pares de corchetes o 2 índices) se podría representar gráficamente como una
tabla con filas y columnas, tridimensionales (3 pares de corchetes o 3 índices)
Este es comúnmente usado para trabajos gráficos en 3D.
En general la definición de un arreglo multidimensional será;
Tipo de dato nombre [tamaño1] [tamaño2] [tamano3]…. [Tamaño n].
En el siguiente ejemplo se ilustra un pequeño programa en c en el cual se utiliza 1
for para leer las filas y otro para leer las columnas de un arreglo bidimensional.
Hemos visto que un arreglo unidimensional de n elementos puede ser visto como
un espacio de memoria lineal ya que todos serán almacenados de manera
secuencial .Similarmente bidimensionales de pxd puede ser como una tabla de
valores que tiene pxm filas y d columnas tal como se ilustra en la figura:
Se tiene un arreglo bidimensional A[p][d]:
x Columnas
Filas
A00 A01 A02 A03 A04 A05 A06
A10 A11 A12 A13 A14 A15 A16
A20 A21 A22 A23 A24 A25 A26
A30 A31 A32 A33 A34 A35 A36
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 43
Cada variable en cada cuadro representa la posición en arreglo los subíndices
00,01,02,03…, el primer número no es más que la posición de fila y el segundo
indica a la columna. Así A11 indica que el elemento está ubicado en la fila 1 (a
simple vista podríamos decir que se refiere a la segunda fila)y en la columna 1 y
así sucesivamente.
Al acceder al valor de un elemento específico se deberá jugar con estas
posiciones, por esto es importante saber cuál es la posición en memoria.
4.4. Generalidades
Ya hemos visto como operan los arreglos a nivel de hardware, ahora, como
escribirlo en C:
He aquí una m lista de declaración de arreglos bidimensionales:
char frutas[2][3]={manzana, pera , uva, naranja, noni, mango};
int valores[23][45];
static double lista[20][15];
float grupo [45][80];
La primera línea define un arreglo de frutas de 2 filas y 3 columnas (2*3=6, lo cual
serian 6 elementos) y si notas esta inicializado con los elementos manzana en la
posición 0,0, pera 0,1, uva 0,2, naranja 1,0, noni 1,1, mango 1,2,
Si un arreglo bidimensional es inicializado se debe tener cuidado se debe tener
cuidado ya que el orden natura l de los elementos se pueden alterar formando
grupo de valores iniciales encerrados entre llaves ({..}).Los valores dentro de cada
par interno de llaves serán asignados alos pares elementos del arreglo cuyo ultimo
índice varíe mas rápidamente .Por e ejemplo en un arreglo bidimensional los
valores almacenados dentro de un par de llaves serán asignados alos elementos
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 44
de una fila , ya que el segundo índice (columna) se incrementara más
rápidamente .Si hay pocos elementos dentro de cada par de llaves , al resto de
cada par de filas se le asigna aran 0. Loor otra parte, el numero de valores dentro
de cada par de llaves no p puede exceder del tamaño de fila definido. Es de
agrupación puede ser útil en la generalización de arreglos de mayor dimensión.
Ejemplo:
int valores[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
Esta asignación seria así:
Valores[0][0]=1 Valores[0][1]=2 valores[0][2]=3 valores[0][3]=4
Valores[1][0]=5 Valores[1][1]=6 valores[1][2]=7 valores[1][3]=8
Valores[2][0]=9 valores[1][2]=10 valores[2][2]=11 valores[2][3]=12
Una variación a este caso con la utilización de la n llaves internas es :
int valores[3][4]={
{1, 2,3}
{4, 5,6}
{7, 8,9}
{10, 11,12,}
}
Esto nos lleva alo mismo, los cuatro valores del primer par de llaves internas son
asignados a los elementos de la primera fila del arreglo los valores de segundo
par de llaves son asignados a la segunda fila del arreglo .Nótese que el par de
llaves internos se necesitan para contener las llaves internas. En caso de que en
las llaves sean agrupados más elementos de los que se especifican en el tamaño
del arreglo ejemplo:
int valores[][]= {
{1, 2, 3, 4,5}
{6, 7, 8, 9,10}
{11, 12, 13, 14,15}
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 45
El compilador lo detectara como un error, ya que el número de elementos excede
al tamaño del arreglo ya declarado.
Si en algún dado caso el arreglo contuviese un elemento menos del tamaño
especificado, es decir,
int valores[3][4]={1,2,3,4,5,6,7,8,9,10,11};
El elemento será rellenado con cero en este caso será de la siguiente manera:
Valores[0][0]=1 Valores[0][1]=2 valores[0][2]=3 valores[0][3]=4
Valores[1][0]=5 Valores[1][1]=6 valores[1][2]=7 valores[1][3]=8
Valores[2][0]=9 valores[1][2]=10 valores[2][2]=11 valores[2][3]=0
4.5. Arreglos y Cadenas
4.5.1. ¿Qué es una cadena?
Una cadena no es más que la secuencia de caracteres consecutivos incluyendo
espacios en blancos esta parte de la pronación es muy importante de conocer ya
que se trabajo al menos en con la manipulación de cadenas de caracteres, ya que
la mayoría de programas se utiliza esta naturaleza de nuestro lenguaje.
A veces resulta complicado trabajar con cadenas de caracteres si no es con la
auxiliarían de los arreglos como anteriormente lo habíamos dicho en un arreglo
unidimensional será fácil, trabajar con cadenas de caracteres, porque cada
carácter de una cadena se estacionaria en cada casilla un carácter, Pero a veces
las cadenas de caracteres necesitan operar como entidades propias es decir ser
almacenadas en un solo espacio de memoria (casilla).
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 46
El siguiente programa es un boleto que trabaja con arreglos bidimensionales para
almacenar en un espacio de memoria los nombres de las personas que viajaran
en un determinado vuelo, y también se utilizan dos arreglos unidimensionales para
almacenar el destino y categoría que el cliente desee. La aerolínea necesita que
luego que el usuario ingrese sus datos y preferencias se imprima de manera
implícita el número de boletos, que el usuario a su preferencia llenara. A
continuación el código en el compilador de C:
/*Programa de un boleto en c*/
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
main()
{
char nomb[50][20];
int i,bol;
int dest[50], cat[50];
/******************************************************/
printf("BIENVENIDOS A SU AEROLINEA DOSSELL\ndigite la cantidad
de boletos: ");
scanf("%d",&bol);
for(i=0; i < bol; i++)
{
printf("\nNombre: ");
scanf("%s", nomb[i]);
printf("\nDestino:\n(1):Bluefields\n(2):San juan\nDestino
seleccionado ");
scanf("%d", &dest[i]);
printf("Categoria:\n(1):Alta\n(2):Media\n(3):Baja\nCategoria
seleccionada ");
scanf("%d", &cat[i]);
}/*fin del primer for*/
system("cls");
/*Imprime los boletos con los datos antes almacenados, */
for(i=0; i < bol; i++)
{
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 47
printf("\n\nAerolinea Dossell\n");
printf("Numero de boletos: %d\n",i+1);
printf("Nombre: %s\n",nomb[i]);
if(dest[i] == 1)
{
printf("Destino: Bluefields");
printf("\nHora de salida: 9:00 am\nHora de llegada:
12:00 pm\n");
}
else
{
printf("Destino: San juan ");
printf("\nHora de salida: 1:00pm\nHora de llegada:
3:00pm");
}
if(cat[i]==1)
{
printf("\nCategoria: Alta\nCosto:C$250 ");
}
else if(cat[i]==2)
{
printf("\nCategoria: Media\nCosto:C$230\n");
}
else
{
printf("\nCategoria: Baja\nCosto:220\n");
}
printf("Asiento numero: %d\n",i);
getch();
}
system("cls");
printf("Buen viaje");
getch();
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 48
La salida del programa seria la siguiente:
Cuantos boletos desea? 2
Nombre: dorissell Narváez Pérez
Destino:
(1):Bluefields.
(2):San juan.
Destino seleccionado 1
Categoría:
(1):Alta
(2):Media
(3):Baja
Categoria seleccionada: 3
Nombre: Jorge Luis Meyer
Destino:
(1):Bluefields.
(2):San juan.
Destino seleccionado 1
Categoría:
(1):Alta
(2):Media
(3):Baja
Categoria seleccionada: 2
/* después de un entre*/
Aerolínea Dossell
Numero de boleto:1
Nombre: dorissell Narváez Pérez
Destino:Bluefields hora de salida: 9:00 am hora de llegada: 12:00 pm
Categoria: Baja
Costo$ 220.
Asiento numero:0
Aerolínea Dossell
Numero de boleto:2
Nombre:jorge Luis Meyer
Destino:Bluefields hora de salida: 9:00 am hora de llegada: 12:00 pm
Categoria: Mediana
Costo$ 230.
Asiento numero:0
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 49
Nótese que el número de asiento será siempre cero ya que la variable i esta
inicializada en cero. Si usted desea que el compilador arroje el número de boletos
igual como el número de asiento lo único que abría que modificar es que al
imprimir sustituya la variable i por la variable que almacena el número del boleto,
de esta manera estamos diciendo que según el número de boleto es el número del
asiento del cliente. O bien si usted desea agregarle un digito de entrada se podría
hacer de las siguiente forma.
printf(“Asiento numero:2104-0%d”,i);
Y esto aparecerá de la siguiente manera:
Aerolínea Dossell
Numero de boleto:2
Nombre: Jorge Luís Meyer
Destino: Bluefields hora de salida: 9:00 am hora de
llegada: 12:00 pm
Categoria: Mediana
Costo$ 230.
Asiento numero: 2104-02.
La lógica del programador es lo que hace la calidad y eficiencia de un programa y
además lo más importante que hace lo más completo y comprensible que sea
para el usuario.
Es importante añadir a este tema la funciones de biblioteca para manipular
cadenas, estas librerías son comúnmente conocidas como funciones de strings
tales como: stdlib.h y ctype.h que incluyen una gama de funciones de biblioteca
como atof, que convierte la porción inicial de una cadena a una representación
de double., y la función atoi, que al contrario convierte la porción inicial en y un
entero.
Su sintaxis es la siguiente:
atof(variable que indica la cadena )
atoi(variable que indica la cadena )
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 50
Ejemplo 1:
El siguiente programa convierte un cadena a double y a una cadena de
representación entero. Nótese que en la utilización de las funciones de biblioteca
son incluidas sus librerías antes declaradas en la cabecera del programa.
/*Uso de atoi y atof*/
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
main()
{
char n1[20] = "10254.4116";
printf("Cadena \'%s\' convertida a double = %f\n", n1, atof(n1));
printf("Cadena \'%s\' convertida a entero = %d\n", n1, atoi(n1));
getch();
}
Ejemplo 2:
El siguiente código es de un programa para leer una cadena de caracteres
minúscula a mayúscula. Se utilizan la función toupper: esta función tiene como
parámetro la variable que almacena el arreglo de cadena de caracteres, la cual
convertirá a mayúscula , en caso de que está este en minúscula.
# include<stdio.h>
# include<conio.h> /* declaración de librerías */
# include<ctype.h>
main()
{
char cadena[]="la victoria se consigue cayendo levantándose y volver
a caer";
int i;
printf("Cadena antes de convertir:\n%s\n", cadena);
for(i=0;cadena[i];i++)
{
cadena[i]=toupper(cadena[i]);
}
printf("Cadena despues de convertir:\n%s\n", cadena); getch();
} /*fin del programa principal*/
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 51
Ejemplo 3:
En el siguiente código muestra más funciones strings como:
sprintf(): imprime un mensaje .
strstr(cad 1, cad 2):localiza la primera aparición en la cadena apuntada por la
constante escrita en el argumento de la secuencia de caracteres
(excluyendo el carácter nulo), es decir la primera cadena , en la
segunda cadena .
strcat(): añade una copia de la primera cadena escrita en el argumento de la
función , incluyendo el carácter nulo en la segunda cadena que se
escribió en el argumento de la función.
# include<stdio.h>
# include<string.h> /*declaración de librerías */
# include<conio.h>
main()
{
char mensaje[50];
char mens1[30]= "lo siento mucho ";/*declaración de arreglos de
cadenas */
char mens2[6]="mucho";
char mens3[]="mu";
printf("strstr(mens1[], mens2[])=%s\n",strstr(mens1, mens2));
if (strstr(mens2,mens3))/*localiza la primera letra ´m'*/
{
strcat(mens1,mens2);/*copia 'mucho' de ultimo de la cadena
'lo siento mucho'*/
sprintf(mensaje,"\nhola %s", mens1);
puts(mensaje);
}
getch();
}/*fin del programa principal*/
La salida del programa seria “hola lo siento mucho mucho”
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 52
Ejemplo 4:
El siguiente código es acerca de un programa interactivo Para definir la profesión
del usuario.
Se utilizan las funciones, sprintf () y puts (): esta última tiene la misma función
como la conocemos pone un mensaje existente en el sistema y lo visualiza.
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
int main()
{
char nombre[10],mensaje[200];
unsigned int edad =0, U;
system("cls");
printf("\nCual es su nombre: "); gets(nombre);
printf("\nCual es su edad: "); scanf("%d", &edad);
printf("\nCual ES LA UNIVERISDAD EN LA QUE ESTUDIAS\n");
printf("1):UNI \n2):ex\n3);UNAN\nSeleccione una opcion: ");
scanf("%d", &U);
system("cls");
if(U==1)
{
sprintf(mensaje, "\nHola%s.\ntienes %d años de edad \n serás
un ingeniero de la %s", nombre,edad, "UNI");
}
else if(U==2)
{
sprintf(mensaje, "\n\nHola %stienes%d años\n serás un
licenciado en ciencias comerciales de: %s", nombre,edad, "EX");
}
else
{
sprintf(mensaje, "\n\nHola%s, tienes%d años\n y serás un
licenciado de la %s ", nombre,edad, "UNAN");
}
puts (mensaje); getch();
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 53
Ejemplo 5:
Este programa utiliza las funciones strcpy (copia dos cadenas), strcat, strcmp
(compara dos cadenas).
/*programa que lee tres cadenas para formar dos copias las comparala y
define su longitud**/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
main()
{
char pn[50]="DORISSELL", cn[50]=" PEREZ", tn[50]="NARVAEZ",
sn[50]="SOCORRO ", *n, *p;
int c;
system("cls");
strcat(pn," del "); /* copia "DORISSELL del" es decir 'del ' a
la cadena pn*/
n=strcat(pn, sn); /* copia "DORISSELL del SOCORRO", añadió */
printf("%s\n",n); /* imprime la cadena r almacenada en la varible
n*/
p=strcat(tn,cn);
printf("%s\n",p);
c=strcmp(pn,tn); /*Compararlas dos cadenas "DORISSELL" y
"NARVÁEZ"*/
printf("pn es ");
if (c<0)
printf("menor que");
else if(c == 0)
printf("igual que");
else
printf("mayor que");
printf(" tn\n");
strcat(pn,tn);
printf("pn=%s\n",pn);
printf("la cadena pn contiene: %d caracteres\n", strlen(pn));
getch();
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 54
4.6. Procesamiento de arreglos.
No se permiten operaciones que impliquen arreglos completos. Por lo tanto para
realizar operaciones de asignación de dos arreglos similares (mismos tipo de
datos, misma dimensionalidad y mismo tamaño), deben de realizarse elemento
por elemento. Esto es posible normalmente dentro de un bucle donde cada paso
del bucle se usa para procesar un elemento del arreglo.
4.7. Paso de arreglos a funciones.
Un arreglo completo puede ser pasado a una función, para tal hecho el nombre
del arreglo debe de aparecer solo, es decir sin: corchetes ni índices como un
argumento actual dentro de la llamada a la función. El correspondiente argumento
formal se escribe de la misma manera, pero debe de ser declarado como un
arreglo de la función.
Al escribir prototipos de funciones que incluyan argumentos de arreglos una pareja
vacía de corchetes debe seguir el nombre del arreglo, es posible omitir el nombre
del argumento del arreglo pero entonces una pareja vacía de corchetes debe
seguir al tipo de datos de argumento de arreglo.
Cuando se pasa una arreglo a una función no se pasan a esta (función) los
valores de los elementos del arreglo, porque el nombre del arreglo se interpreta
como la dirección del primer elemento del arreglo por lo tanto lo arreglos se dice
que son pasados por referencia a una función.
Si un elemento del arreglo es modificado dentro de la función, esta modificación
será reconocida en la parte del programa desde la que se hizo la llamada.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 55
Ejemplo:
/*Programa que pasa un arreglo de 4 elementos a una función donde son
modificados los valores se escriben en tres ocasiones para mostrar los
cambios*/
#include <stdio.h>
#include <conio.h>
#define max 4
void cambiar (int numeros []);
main()
{
int sum, numeros[max];
clrscr();
printf("\t\ndesde el programa principal antes de llamar a la
función\n\n");
for(sum=0; sum<=max; ++sum)
{
numeros[sum]= sum +1;
printf("a[%d]=%d\n",sum,numeros[sum]);
}
cambiar(numeros);
printf("\ndesde el programa principal después de llamar a la
función:\n\n");
for(sum=0; sum<=max; ++sum)
printf("numeros[%d]=%d\n", sum, numeros[sum]); getch();
}
void cambiar(int numeros[])
{
int sum;
printf("desde la función después de modificar los elementos\n\n");
for(sum=0;sum<=max;++sum)
{
numeros[sum]=10;
printf("numeros[%d]=%d\n", sum, numeros[sum]);
}
return;
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 56
En este ejemplo podemos observar que al arreglo se le asigna los valores de
a[0]=1, a[1]=2, a[2]=3 en la función main, luego estos valores se trasladan a la
función cambiar , donde se le asigna el valor de 10 a cada elemento del arreglo
estos valores se escriben en main, y finalmente arreglo es escrtto una vez que se
le pasa el control a main.
Podemos llegar a la conclusión que los elementos de “numeros” se modifican
dentro de main después de hacer una llamada a la función cambiar.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 57
5. Funciones
5.1. Que son funciones
Las funciones en el lenguaje “C” son subprogramas, cuya finalidad es hacer una
tarea específica y retornar un valor al programa principal, si es requerido. Otra
función de las funciones es el de hacer el programa principal no muy extenso.
5.2. Declaración de funciones
Las funciones se declaran, se definen, son llamadas desde el programa principal,
son hechas o creadas por el usuario, y están compuestas por una cabecera y el
cuerpo de la función.
La sintaxis de una función es la siguiente: (lo que esta entre corchetes no es
siempre necesario utilizarlo)
[clase] [tipo] nombre (
tipo argumento formal 1,
tipo argumento forma 2, ...,
tipo argumento formal n)
{
[Declaraciones]; --> parámetros actuales
Instrucciones;
return (expresión);
}
El return devuelve el carácter convertido al punto del programa de llamada de la
función.
El parámetro actual es aquel que se recibe en la función y debe tener dato. El
parámetro actual es declarado dentro del programa principal.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 58
El parámetro o argumento formal es aquel que se recibe en la definición de la
función y los parámetros, tanto formales como actuales debe de ser iguales.
La declaración de las funciones puede ser implícita o explicita. La implícita se da
cuando la función es llama y no existe una declaración previa, y por defecto “C”
construye una función prototipo de resultado entero. En la explicita se permite
conocer las características de la función antes de ser usada, es decir se conoce el
tipo. La declaración de funciones se hace después de la declaración de las
librerías. La llamada de las funciones de hacen desde el programan principal, es
decir dentro del main. Con la siguiente sintaxis:
[Variable] = nombre _ función ([argumentos actuales o parámetros
actuales]);.
Ahora veremos algunos ejemplos de lo descrito anteriormente:
Ejemplo:
#include <stdio.h> /* librerías */
#include <conio.h>
/* declaración explicita (no es necesaria si el valor de retornos es
entero) */
int escribir (int y);
main()
{ /* Inicio del Programa Principal */
int r, b=5; /* declaración de variables */
/* asignación a r el valor de la función con parámetro actual llamando a
la función */
r = escribir (b);
printf("r = %d \n", r); /* salida del valor r */
getch(); /* espera una tecla para salir */
} /* fin del programa principal */
int escribir (int y) /* definición de la función con parámetro formal
*/
{
return (y *3); /* instrucción que retorna el valor a la
función */
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 59
Ejemplo:
/* librerías */
#include <stdio.h>
#include <conio.h>
#include <math.h>
/*declaración explicita (no es necesaria si el valor de retornos es
entero)*/
float hipotenusa (float a, float b);
main()
{ /* Inicio del Programa Principal */
/* declaración de variables */
float a, b, h;
clrscr(); /*borra pantalla*/
printf("Escriba el valor de los catetos: ");
scanf("%f %f", &a, &b);
h = hipotenusa(a,b); /* llamada de la función hipotenusa*/
printf("el valor de la hipotenusa es = %f",h); /*imprime el valor
de la hipotenusa*/
getch(); /* espera una tecla para salir */
} /* fin del programa principal */
/* definición de la función con parámetro formal */
float hipotenusa (float a, float b)
{
/*instrucción de la función*/
float y;
y = sqrt ((pow (a,2))+ (pow (b,2)));
/* instrucción que retorna el valor a la función */
return y;
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 60
5.3. Llamadas a función
Se conoce como llamada a función cuando se invoca o manda a ejecutar una
función previamente creada, ya sea que esta se encuentre el las librerías o sea
programada por el usuario. Una llamada a función es una expresión con todo lo
que ello implica.
Para llamar a una función se escribe el nombre de la función y entre paréntesis se
especifican los valores que se desean dar a los argumentos.
Ej:
mi_funcion(expr1, expr2, …)
Cada Expresión se evalúa y el valor devuelto es pasado como argumento a la
función, la expresión debe devolver un tipo compatible al argumento de la función,
o se puede forzar otro tipo mediante la conversión de tipos.
No es obligatorio guardar el valor devuelto por la función, si es que esta devuelve
alguno, hay momentos en que no necesitamos saber que devuelve la función, por
ejemplo: la función printf y scanf devuelven un valor entero al ser llamadas, pero
es poco común para la mayoria utilizar estos valores.
5.4. Parámetros por valor.
Existen dos formas mediante las cuales se pueden pasar valores a las función, las
formas en que utilicemos una u otra forma depende de la necesidad que se nos
presente, el primero es el paso por valor, los que significa que el realidad nosotros
el la función recibimos una copia del dato mandado, no el dato original, por lo tanto
cualquier modificación sobre este dato no afectará al valor de donde fue llamada la
función, un ejemplo practico sería intercambiar los valores de dos variables:
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 61
Ejemplo:
#include <stdio.h>
#include <conio.h>
/*Declaracion de la funcion*/
void intercambiar(int a, int b);
main() /*Programa principal*/
{
/*Declaracion de variables*/
int a = 10, b = 20;
printf("Valores antes de llamar a la funcion:\na=%d\tb=%d\n", a,
b);
intercambiar(a, b); /*Llamada a la funcion*/
printf("Valores después de llamar a la funcion:\na=%d\tb=%d\n", a,
b);
getch();
}/*Fin del main*/
/*Definicion de la funcion*/
void intercambiar(int a, int b)
{
int c;
c = a;
a = b;
b = c;
}
Resultado:
Valores antes de llamar a la función:
a=10 y b=20
Valores después de llamar a la función:
a=10 y b=20
Como podemos ver a pesar de haber modificado los valores recibidos dentro de la
función los valores originales no fueron alterados.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 62
5.5. Parámetros por referencia
Se les conoce como parámetros por referencia porque en realidad cuando se
llama a la función no se manda una copia del dato como parámetro sino una
referencia al dato original, o en otras palabras donde se encuentra este dato, esto
permite a la función modificar el dato original ya que esta tiene la dirección donde
se encuentra almacenado y simplemente precede a modificar el valor de esa
dirección, veamos el ejemplo anterior pero con parámetros por referencia.
#include <stdio.h>
#include <conio.h>
/*Declaracion de la funcion*/
void intercambiar(int *a, int *b);
main() /*Programa principal*/
{
/*Declaracion de variables*/
int a = 10, b = 20;
printf("Valores antes de llamar a la funcion:\na=%d\tb=%d\n", a,
b);
intercambiar(&a, &b); /*Llamada a la funcion*/
printf("Valores después de llamar a la funcion:\na=%d\tb=%d\n", a,
b);
getch();
}/*Fin del main*/
/*Definicion de la funcion*/
void intercambiar(int *a, int *b)
{
int c;
c = *a;
*a = *b;
*b = c;
}
Resultado:
Valores antes de llamar a la función: a=10 y b=20
Valores después de llamar a la función: a=20 y b=10
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 63
A diferencia del ejemplo anterior ahora los valores de las variables si fueron
modificados, para declarar parámetros por referencia utilizamos el símbolo * antes
del nombre del parámetro, además de esto para poder acceder al valor del
parámetro y no su dirección se debe anteponer *, esto le indica al compilador que
queremos acceder al dato no a la dirección, también es importante aclarar que a
la hora de enviar los parámetros se debe utilizar el operador & el cual devuelve la
dirección de memoria donde se encuentra almacenado el dato. De manera general
la declaración de parámetros por referencia quedaría de la forma.
<tipo> <nombre_funcon> (
<tipo> *<identificador>,
<tipo> *<identificador>,
...)
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 64
6. Punteros Y Manejo De Archivos
6.1. Punteros
6.1.1. Concepto de Puntero.
Un Puntero contiene una dirección de memoria.
Cuando una variable contiene la dirección de otra variable se dice que la primera
variable apunta a la segunda.
Dicho de forma específica, un puntero es una variable que contiene la dirección de
memoria de otra variable. Se utilizan para pasar información entre una función y
sus puntos de llamada.
6.1.2. Declaración de punteros
La forma general para declarar una variable puntero es:
tipo *nombre;
donde tipo es cualquier tipo valido de C (también llamado tipo base) y nombre es
el nombre de la variable puntero.
6.1.3. Operadores de Punteros.
Existen dos Operadores especiales de punteros: & y *. Estos dos operadores son
monarios y no tienen nada que ver con los operadores binarios de multiplicación
(*) y de and a nivel de bits (&).
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 65
6.1.3.1. Operador &.
& es un operador monario que devuelve la dirección de memoria de su operando.
Por ejemplo, si queremos guardar en el puntero x la dirección de memoria de la
variable num, deberemos hacer lo siguiente:
x=#
Ejemplo:
#include<stdio.h>
#include<conio.h>
/*Funcion principal*/
main()
{
int x = 10; /*Declaracion de variable*/
/*Se muestra el valor y la direccion de memoria de x*/
printf("x=%d\n&x =%p\n",x,&x);
getch();
}
Salida del ejemplo anterior:
x=10
&x = 8FBC: OFFE.
Nota:
El valor y formato de las direcciones de las variables que se imprimen en esta
lección (con el código de formato %p) son dependientes de la implementación. En
mi caso las direcciones se escriben en formato segmento offset, y e valor de la
dirección de una variable es distinto según cuando y desde donde se ejecuta el
programa
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 66
6.1.3.2. Operador*
El operador * es el complemento de &. Es un operador monario que devuelve el
valor de la variable localizada en la dirección que sigue es decir devuelve el valor
de la variable cuya dirección es contenida por el puntero. Este ejemplo sitúa el
contenido de la variable apuntada por x, es decir num, en la variable a:
a=*x;
Ejemplo1:
#include<stdio.h>
#include<conio.h>
/*Funcion principal*/
main()
{
int x = 10; /*Declaracion de x*/
printf("x = %d \n",x); /*Se muestra el valor de x*/
printf("*&x = %d", *&x); /*Se muestra el valor apuntado por direccion
&x*/
getch();
}
Salida del ejemplo1:
x = 10
*&x = 10
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 67
Ejemplo 2:
#include<stdio.h>
#include<conio.h>
main()
{
int x = 10;
int *px;
px = &x;
printf("x = %d\n",x);
printf("&x = %p\n", &x);
printf("px = %p\n", px);
printf("&px = %p\n", &px);
printf("*px = %d", *px);
getch();
}
Salida del ejemplo:
x = 10
&x = 8FC4: OFFE
px = 8FC4: OFFE
&px = 8FC4: OFFA
*px = 10
Gráficamente:
En el ejemplo anterior se observa que hay tres valores asociados a los punteros:
Dirección en la que se encuentra.
Dirección a la que apunta.
Valor contenido en la dirección apuntada.
Nota:
Al ejecutar el siguiente programa los resultados pueden ser inesperados ya que
estamos asignado el valor 10 en una posición de memoria no reservada.
px
8FC4: OFFE
8FC4: OFFE
x
8FC4: OFFE
10
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 68
#include<stdio.h>
#include<conio.h>
main()
{
int *p;
printf("Dirección: %p", p);
printf("Valor antes de asignar: %d", *p);
*p = 10;
printf("Valor asignado: %d", *p);
getch();
}
Seria correcto:
#include<stdio.h>
#include<conio.h>
main()
{
int x; /*se reserva memoria para x*/
int *p; /*se reserva memoria para la variable p, no para la
posición de memoria a la que apunta p*/
printf("Direccion de:\n&x = %p\np = %p\n", &x, p);
p = &x; /*p apunta a valor de x*/
printf("Direccion despues de asignar:\n&x = %p\np = %p\n", &x, p);
*p = 10; /*equivalente a x = 10*/
printf("Valor de x: %d\nValor apuntado por *p: %d\n", x, *p);
getch();
}
6.1.4. Aritmética de Punteros.
Existen 4 operadores que realizan operaciones aritméticas que pueden utilizarse
con punteros. Es posible desplazar un puntero recorriendo posiciones de memoria.
Para ello podemos usar los operadores de suma, resta, incremento y decremento
(+, -, ++, - -). Si tenemos un puntero ( p1 ) de tipo int ( 2 bytes ), apuntando a la
posición 30000 y hacemos: p1=p1+5; el puntero almacenará la posición 30010,
porque apunta 5 enteros por encima ( 10 bytes más ).
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 69
Ejemplo:
#include<stdio.h>
#include<conio.h>
main ()
{
int *p;
#define imprimir_p printf("\np = %p",p);
imprimir_p;
printf ("\tsizeof (*p) = %d",sizeof(*p));
p++; imprimir_p;
p -=5; imprimir_p;
getch();
}
Salida del ejemplo:
p = 7203:8D51 sizeof (*p) = 2
p = 7203:8D53
p = 7203:8D49
En el ejemplo se aprecia que si hacemos p++; no aumenta el valor de p en 1 sino
que aumenta en 2, que s el tamaño en bytes de un int, es decir, el tamaño del
objeto al que apunta.
Por lo tanto, la sentencia p++ hay que interpretarla de la siguiente forma:<<p
apunta al siguiente elemento del tipo base>>. Lo mismo se puede decir e los
demás operadores aritméticos aplicados a los punteros.
Toda la aritmética de punteros esta en relación con el tipo base del puntero por lo
que el puntero esta siempre apuntado al elemento apropiado del tipo base.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 70
6.1.5. Asignación dinámica de Memoria.
Supóngase que queremos hacer un programa que lea n valores enteros
introducidos por teclado por el usuario, los almacene en un vector y los imprima en
un orden inverso.
Solución:
#include<stdio.h>
#include<conio.h>
main()
{
#define NMAX 100 /*número máximo de elementos*/
int v[NMAX]; /*vector*/
int n = 0; /*número de elementos introducidos*/
int varaux;/*variable auxiliar*/
register int i; /*indice*/
do
{
printf("\nIntroduce número de valores a leer (1 - %d): ",
NMAX);
scanf ("%d", &n);
} while (n < 1 || n > NMAX);
for (i = 0; i <= n - 1; i++)
{
printf("Introduce valor %d: ", i+1);
scanf("%d", &varaux);
v[i] = varaux;
}
printf("\n\nvalores en orden inverso: \n");
for(i = n - 1; i >= 0; i--)
printf("Valore %d = %d\n", i+1, v[i]);
getch();
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 71
Si el usuario introduce como valor de n, el valor 10, estaremos desperdiciando, si
un int ocupa 2 bytes, 90*2 bytes de memoria. Además, el usuario no puede
introducir más de NMAX valores. Estas restricciones vienen impuestas porque el
tamaño de un array en la declaración ha de ser una expresión constante. La
asignación de memoria en este caso se dice que es estática porque se determino
en el momento de la compilación. Cuando la asignación de memoria se determina
en tiempo de ejecución se dice que es asignación dinámica.
Veamos como se haría el programa anterior con asignación dinámica y luego
pasamos a explicarlo:
#include<stdio.h>/*printf(), scanf()*/
#include<conio.h>/*getch();*/
#include<malloc.h>/*malloc(), free()*/
main() {
int *v; /*vector*/
/*número de elementos introducidos y variable auxiliar*/
int n = 0, varaux;
register int i; /*indice*/
printf("\nIntroduce número de valores a leer: ");
scanf("%d", &n);
v = (int *) malloc(n * sizeof(int));
if (v == NULL)
printf("Memoria insuficiente.");
else {
for (i = 0; i <= n - 1; i++)
{
printf("Introduce valor %d: ", i+1);
scanf("%d", &varaux); v[i] = varaux;
}
printf("\n\n valores en orden inverso: \n");
for(i = n - 1; i >= 0; i--)
printf("Valor %d = %d", i+1, v[i]);
free(v);
}
getch();
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 72
La primera sentencia de main() es:
int *x;
En esta declaración estamos declarando v como un puntero a entero.
La siguiente línea <<extraña>> para nosotros es:
v = (int *) malloc (n * sizeof (int));
La función malloc reserva memoria; acepta como argumento los bytes de memoria
a reserva y devuelve un puntero al primer byte de la zona de memoria reservada;
los bytes de memoria solicitados los reserva en un espacio de memoria contiguo.
Si no hay suficiente memoria, devuelve NULL. Un puntero que tiene el valor NULL
es un puntero que no apunta a ningún sitio.
El prototipo de esta función se encuentra en el fichero malloc.h (de ahí el incluir
este fichero en nuestro ejemplo) y es el siguiente:
void * malloc (unsigned in bytes);
Veamos que devuelve un puntero a void; esto quiere decir que devuelve un
puntero que apunta a cualquier tipo base, o dicho de otro modo, un puntero que
apunta a una dirección de memoria sin tener tipo base.
El c de Kernighan y Ritchie, como no tiene tipo void, el prototipo de esta función
es:
char * malloc (unsigned int bytes);
En ambos casos, el tratamiento por parte del usuario de esta función es
exactamente el mismo. Veamos otra vez la llamada a esta función en nuestro
ejemplo:
v = (int *) malloc (n * sizeof (int));
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 73
Al valor devuelto por la función malloc (puntero a void o puntero a char) siempre
se le realiza un moldeado (debemos recordar que esto se hacia con: (tipo)) para
adecuarlo al tipo base de nuestro puntero que va a apuntar a esa zona de
memoria reservada. En nuestro caso el molde es:
(int *x) /*puntero a entero*/
El puntero que le pasamos a malloc ha de ser el número de bytes de memoria a
reservar. Esto siempre se hace siguiendo la fórmula:
número_de_elementos * sizeof (tipo_de_cada_elemento).
Que traducido a nuestro caso queda:
n * sizeof (int)
Otra forma e hacer lo mismo es:
N * sizeof (*x)
Que suele ser muy corriente. Las dos formas son equivalentes.
La memoria asignada por malloc se desasigna con la función free(). Esta memoria
asignada no se desasigna al salir del bloque de código en que fue asignada como
ocurre con las variables locales sino con la función free(liberar) o al terminar el
programa. Por lo tanto, siempre que asignemos memoria con malloc, tenemos que
desasignarla con free cuando ya no nos sea necesaria.
El prototipo de la función free se encuentra en el fichero malloc.h y es el siguiente:
void free (void *p);
Un puntero a void como parámetro indica que acepta cualquier puntero,
independientemente del tipo base al que apunta. El puntero que se le pasa a free
como argumento ha de ser un puntero que apunta al principio de una zona
reservad anteriormente por malloc; sino es así, se puede caer el sistema.
El resto del ejemplo no tiene ya ninguna dificultad.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 74
Hay otra función, en la librería <alloc.h> equivalente a malloc, que es la función
calloc cuyo prototipo es el siguiente:
void * calloc (
unsigned número_de_elementos_a_reservar,
unsigned tamaño_en_bytes_de_cada_elemento);
Esta función es igual que malloc con la única diferencia de sus parámetros.
En turbo C, los prototipos de las funciones malloc(), calloc() y free(), además de
estar en el fichero alloc.h, también están en el fichero stdlib.h
6.1.6. Punteros y arrays.
Existe una estrecha relación entre los punteros y los arrays. El nombre de un array
es un puntero al primer elemento del array.
A cualquier elemento de un array podemos acceder mediante la aritmética de
punteros y viceversa, cualquier puntero lo podemos indexar con los []:
6.1.6.1. Arrays unidimensionales:
p[i] == *(p+i)
6.1.6.2. Arrays bidimensionales:
p[i][j] == *(p+(i *longitud_fila)+k) == *(*(p+i)+j)
6.1.6.3. Arrays muldimensionales:
Se sigue cualquiera de los dos procedimientos ejemplarizados en los arrays
bidimensionales.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 75
Ejemplo de acceso a un Array con un Puntero.
#include<stdio.h>
#include<conio.h>
main()
{
/*Arreglo unidimensional*/
float v[3] = {1.1, 2.2, 3.3};
/*Se muestran los valores*/
printf ("v[1] = %g; *(v+1) = %g", v[1], *(v+1));
getch();
}
Salida: v[1] = 2.2; *(v+1) = 2.2
Ejemplo de acceso a elementos indexando un puntero con [ ].
#include<stdio.h> /*printf (), NULL*/
#include<malloc.h> /*malloc (), free ()*/
#include<conio.h>
main()
{
float *p;
if ((p = (float *)malloc(3*sizeof (float)))== NULL)
printf("\nERROR: Memoria insuficiente.");
else
{
*p = 1.1; *(p+1) = 2.2; *(p+3) = 3.3;
printf ("*(p+1) =%g p[1] =%g",*(p+1), p[1]);
free(p);
}
getch();
}
Salida: *(p+1) = 2.2; p[1] = 2.2
Nota:
Los programadores profesionales de C suelen utilizar la notación puntero en vez
de la notación array porque es bastante más rápido y más cómodo, aunque para
los no acostumbrados a esta notación se ve un poco extraño al principio.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 76
Vemos que en la notación array, para acceder a un determinado elemento, el
compilador tiene que hacer una serie de cálculos para averiguar en que posición
está, mientras que en la notación puntero basta con una simple suma.
No obstante, cuando el código queda más claro en la notación array que con la
notación puntero es preferible la primera notación.
Cuando se trabaja con cadena de caracteres si se debe utilizar la notación
puntero, no ya solo por eficiencia sino también por convención.
Una estructura común en C es el array de punteros. Recordar que el argumento
array de la función main () es un array de punteros a caracteres.
Hay tres formas equivalentes de declarar el argumento argv en la función main():
main (int argc, char argv[ ][ ]);
main (int argc, char *argv[ ]);
main (int argc, char **argv);
Se observa que en la primera declaración no se ha especificado el tamaño de la
segunda dimensión de argv cuando habíamos dicho antes que era necesario, esto
solo está permitido hacerlo en la función main().
La declaración para un array de 10 punteros a int es:
int *x[10];
Para asignar la dirección de una variable entera llamada ver al tercer elemento del
array de punteros, se escribe:
x[2] = &var;
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 77
Ejemplo de array de cadenas de caracteres.
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
void error (int numero_de_error)
{
char *errores[ ] =
{
"error al intentar abrir fichero",
"error al intentar cerrar fichero",
"error al intentar leer de fichero",
"error al intentar escribir en fichero"
};
printf ("Error %d. %s.",
numero_de_error, errores [numero_de_error]);
getch(); exit(1);
}
main()
{
int n = 1;
error(n);
}
Un array de puntero es lo mismo que punteros a punteros.
Este ejemplo comprueba dicha afirmación.
#include<stdio.h>
#include<conio.h>
main ()
{
int x, *p, **q;
x = 10; p = &x; q = &p;
printf("x = %d; *p = %d; **q = %d",x,*p,**q);
getch();
}
Salida:
x = 10; *p = 10; **q = 10.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 78
Vamos a mostrar dos formas de implementar la siguiente función: la función a
implementar acepta como argumentos una matriz de enteros y un elemento, y
devuelve 1 si ese elemento se encuentra en la matriz o 0 si dicho elemento no se
encuentra en la matriz.
/*Las funciones a buscar_en_matriz_versión_1()
buscar_en_matriz_versión_2() son equivalentes pero la segunda es mucho
más eficiente*/
#include<stdio.h>
#include<conio.h>
#define N 3
int buscar_en_matriz_version_1(int m[N][N], int x)
{
register int i, j;
int encontrado = 0;
for (i = 0; ! encontrado && i < N; i++)
for (j = 0; ! encontrado && j< N; j++)
if (m[i][j]== x)
encontrado = 1;
return (encontrado);
}
int buscar_en_matriz_version_2 (int m[N][N], int x)
{
register int i;
int encontrado = 0;
int *pm = (int*)m; /*declara pm como puntero a int y lo hace a
puntar a m*/
for (i = 1; ! encontrado && i<= N *N; i++)
{
if (*pm == x)
encontrado = 1;
else
pm++;
}
return(encontrado);
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 79
main()
{
int matriz [N][N] = {{1,2,3}, {-1,-2,-3}, {5,6,7}};
int resultado_1 = buscar_en_matriz_version_1 (matriz,6);
int resultado_2 = buscar_en_matriz_version_1 (matriz,8);
int resultado_3 = buscar_en_matriz_version_2 (matriz,6);
int resultado_4 = buscar_en_matriz_version_2 (matriz,8);
printf("\n resultado_1 = %d", resultado_1);
printf("\n resultado_2 = %d", resultado_2);
printf("\n resultado_3 = %d", resultado_3);
printf("\n resultado_4 = %d", resultado_4);
getch();
}
Salida:
resultado_1 = 1
resultado_2 = 0
resultado_3 = 1
resultado_4 = 0
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 80
6.1.7. Inicializaciones de punteros
Un puntero que tiene el valor NULL es un puntero que no apunta a ningún sitio.
Una inicialización muy común en C se ilustra con el siguiente ejemplo:
char *p = “cadena\n”;
En este caso el compilador guarda “cadena\n” en memoria y hace que p apunte al
principio de la cadena, es decir, el carácter „c‟.
Ejemplo de inicializaciones equivalentes.
int x = 10; <==> int x = 10;
int *p = &x; int *p;
p = &x;
int x, *p, y; <==> int x;
int *p;
int y;
int *p, *q, r = 10; <==> int *p ;
int *q;
int r =10;
int v[2] = {1,2}, f (void), *p, x = 3; <==> int v[2] = {1,2};
int f (void);
int *p;
int x = 3;
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 81
6.1.8. Punteros a funciones.
Una característica algo confusa pero muy útil de C es el puntero a función. La
confusión surge porque una función tiene una posición física en memoria que
puede asignarse a un puntero aunque la función no es una variable. La dirección
de la función es el punto de entrada de la función; por tanto, un puntero a función,
puede utilizarse para llamar a la función.
Ejemplo:
#include<stdio.h> /*printf ()*/
#include<conio.h>
main()
{
/*escribir es una función que acepta un int como argumento y no devuelve
nada*/
void escribir (int);
/*pf es un puntero a una función que acepta un int como argumento y no
devuelve nada*/
void (*pf)(int);
pf = escribir;
escribir (1); /*llama a la función escribir*/
(*pf)(2); /*llama a la función escribir a través e un puntero*/
getch();
}
void escribir (int numero)
{
printf ("%d", numero);
}
Salida: 12
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 82
6.1.9. Modificador de acceso const y punteros.
El modificador de acceso const aplicado a punteros ofrece varias posibilidades.
Veámosla en los siguientes ejemplos:
main()
{
char *p1 = "abc"; /*puntero*/
const char *p2 = "abc"; /*puntero a constante*/
char *const p3 = "abc"; /*puntero constante*/
const char *const p4 = "abc"; /*puntero constante a constante*/
*p1 = 'd'; /*correcto*/
*p2 = 'd'; /*error*/
*p3 = 'd'; /*correcto*/
*p4 = 'd'; /*error*/
p1++; /*correcto*/
p2++; /*correcto*/
p3++; /*error*/
p4++; /*error*/
p1 = p2; /*warning*/
p1 = p3; /*correcto*/
p1 = p4; /*warning*/
p2 = p1; /*correcto*/
p2 = p3; /*correcto*/
p2 = p4; /*correcto*/
p3 = p1; /*error*/
p3 = p2; /*error*/
p3 = p4; /*error*/
p4 = p1; /*error*/
p4 = p2; /*error*/
p4 = p3; /*error*/
}
Las líneas que contienen el mensaje de error provocan un error de compilación.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 83
Las líneas que contienen el mensaje de warning provocan en algunos
compiladores un mensaje de conversión de puntero sospechosa que se puede
solucionar haciendo la conversión de una forma explícita:
p1 = (char *) p2;
p1 = (char *) p4;
Si ahora hacemos:
*p1 = „d‟;
Estamos modificando los valores apuntados por p2 y p4; es decir, los valores
apuntados por p2 y p4; es decir, los valores apuntados por p2 y p4 no pueden ser
modificados por estos punteros pero si pueden ser modificados indirectamente por
otro puntero.
Otro ejemplo de cómo se puede modificar el valor de una constante
indirectamente a través de un puntero:
Const int x = 10; /*x es una variable constante*/
x = 20; /*esto provoca un error en compilación*/
*(int*)&x = 20; /*esto es correcto: obtenemos su dirección, que
es del tipo (const int*) y la moldeamos al tipo
(int *), una vez hecho esto accedemos al valor de
esa dirección con el operador de contenido* */
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 84
6.1.10. Declaraciones curiosas.
El C permite la creación de formas de datos muy elaboradas.
Cuando se hace una declaración, el nombre (o “identificador”) que usamos se
puede modificador añadiéndole uno o varios modificadores:
Modificador Significado
* indica puntero
() indica una función
[] indica un array
La clave para desentrañar las declaraciones que mostraremos a continuación es
averiguar el orden en que se aplican los modificadores.
Para ello se siguen tres reglas:
La prioridad de un modificador es tanto mayor cuanto más próximo este el identificador.
Los modificadores [ ] y () tienen mayor prioridad que *.
Se pueden usar paréntesis para agrupar parte de la expresión otorgándole la máxima
prioridad.
Ejemplo:
main ()
{
/*array de arrays de int*/
int x1 [8][8];
/*puntero a puntero a int*/
int **x2;
/*array de 10 punteros a int*/
int *x3[10];
/*puntero a array de 10 int*/
int (* x4)[10];
/*array de 3 punteros a array de 4 int*/
int **x5 [3][4];
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 85
/*puntero a array de 3*4 int*/
int (**x6)[3][4];
/*puntero a función que devuelve un tipo int*/
int (*x8) (void);
/*función que acepta un puntero a char como argumento y que devuelve un
puntero a un puntero a una función que devuelve un carácter*/
char(*(* x11(char*)))(void);
/*puntero a función que devuelve un puntero a puntero a carácter y acepta
dos argumentos: el primero es un puntero a puntero a puntero a carácter,
y el segundo es un array de 10 punteros a carácter*/
char**(*x12) (char ***, char *[10]);
/*función que acepta un puntero a puntero a puntero a constante y
devuelve un puntero constante a constante*/
const void *const x13 (const void ***);
/*función que no devuelve nada y acepta como argumento un puntero a
función que no devuelve nada y acepta como argumento un puntero a función
que no devuelve nada y no acepta ningún argumento*/
void x14 (void (*) (void(*)(void)));
/*función que acepta un int como argumento y devuelve un puntero a una
función que acepta un int como argumento y que devuelve un int*/
int(*(x15 (int))) (int);
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 86
6.2. Estructuras
6.2.1. Concepto de estructura
Una estructura es un conjunto de una o más variables, de distinto tipo, agrupadas
bajo un mismo nombre para que su manejo sea más sencillo. Su utilización más
habitual es para la programación de bases de datos, ya que están especialmente
indicadas para el trabajo con registros o fichas.
6.2.2. Declaración
La sintaxis de su declaración es la siguiente:
struct tipo_estructura
{
tipo_variable nombre_variable1;
tipo_variable nombre_variable2;
tipo_variable nombre_variable3;
};
Donde tipo_estructura es el nombre del nuevo tipo de dato que hemos creado. Por
último, tipo_variable y nombre_variable son las variables que forman parte de la
estructura.
Para definir variables del tipo que acabamos de crear lo podemos hacer de varias
maneras, aunque las dos más utilizadas son éstas:
Una forma de definir la estructura:
struct trabajador {
char nombre[20];
char apellidos[40];
int edad;
char puesto[10];
};
struct trabajador fijo, temporal;
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 87
Otra forma:
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
char puesto[10];
}fijo, temporal;
En el primer caso declaramos la estructura, y en el momento en que necesitamos
las variables, las declaramos. En el segundo las declaramos al mismo tiempo que
la estructura. El problema del segundo método es que no podremos declarar más
variables de este tipo a lo largo del programa. Para poder declarar una variable de
tipo estructura, la estructura tiene que estar declarada previamente. Se debe
declarar antes de la función main.
El manejo de las estructuras es muy sencillo, así como el acceso a los campos ( o
variables ) de estas estructuras. La forma de acceder a estos campos es la
siguiente:
variable.campo;
Donde variable es el nombre de la variable de tipo estructura que hemos creado, y
campo es el nombre de la variable que forma parte de la estructura. Lo veremos
mejor con un ejemplo basado en la estructura definida anteriormente:
temporal.edad=25;
Lo que estamos haciendo es almacenar el valor 25 en el campo edad de la
variable temporal de tipo trabajador.
Otra característica interesante de las estructuras es que permiten pasar el
contenido de una estructura a otra, siempre que sean del mismo tipo
naturalmente:
fijo=temporal;
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 88
Al igual que con los otros tipos de datos, también es posible inicializar variables de
tipo estructura en el momento de su declaración:
struct trabajador fijo={"Pedro","Hernández Suárez", 32, "gerente"};
Si uno de los campos de la estructura es un array de números, los valores de la
inicialización deberán ir entre llaves:
struct notas
{
char nombre[30];
int notas[5];
};
struct notas alumno={"Carlos Pérez",{8,7,9,6,10}};
6.2.3. Estructuras y funciones
Podemos enviar una estructura a una función de las dos maneras conocidas:
6.2.3.1. Por valor
Su declaración sería:
void visualizar(struct trabajador);
Después declararíamos la variable fijo y su llamada sería:
visualizar(fijo);
Por último, el desarrollo de la función sería:
void visualizar(struct trabajador datos)
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 89
Ejemplo:
/*Paso de una estructura por valor*/
#include <stdio.h>
#include <conio.h>
struct trabajador
{
char nombre [20];
char apellidos [40];
int edad;
char puesto [10];
};
void visualizar (struct trabajador);
main() /*rellenar y visualizar*/
{
struct trabajador fijo;
printf ("Nombre: ");
scanf("%s", fijo.nombre);
printf("\nApellidos: ");
scanf("%s", fijo.apellidos);
printf("\nedad: ");
scanf("%d", &fijo.edad);
printf("\nPuesto: ");
scanf("%s", fijo.puesto);
visualizar(fijo);
getch();
}
void visualizar (struct trabajador datos)
{
printf("Nombre: %s", datos.nombre);
printf("\nApellidos: %s", datos.apellidos);
printf("\nedad: %d", datos.edad);
printf("\npuesto: %s", datos.puesto);
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 90
6.2.3.2. Por referencia:
Su declaración sería:
void visualizar(struct trabajador *);
Después declararemos la variable fijo y su llamada será:
visualizar(&fijo);
Por último, el desarrollo de la función será:
void visualizar(struct trabajador *datos)
Fíjate que en la función visualizar, el acceso a los campos de la variable datos se
realiza mediante el operador ->, ya que tratamos con un puntero. En estos casos
siempre utilizaremos el operador ->. Se consigue con el signo menos seguido de
mayor que.
Ejemplo:
/* Paso de una estructura por referencia. */
#include <stdio.h>
#include <conio.h>
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
char puesto[10];
};
void visualizar(struct trabajador *);
main() /* Rellenar y visualizar */
{
struct trabajador fijo;
printf("Nombre: ");
scanf("%s",fijo.nombre);
printf("\nApellidos: ");
scanf("%s",fijo.apellidos);
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 91
printf("\nEdad: ");
scanf("%d",&fijo.edad);
printf("\nPuesto: ");
scanf("%s",fijo.puesto);
visualizar(&fijo);
getch();
}
void visualizar(struct trabajador *datos)
{
printf("Nombre: %s",datos->nombre);
printf("\nApellidos: %s",datos->apellidos);
printf("\nEdad: %d",datos->edad);
printf("\nPuesto: %s",datos->puesto);
}
6.2.4. Arrays de estructuras
Es posible agrupar un conjunto de elementos de tipo estructura en un array. Esto
se conoce como array de estructuras:
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
};
struct trabajador fijo[20];
Así podremos almacenar los datos de 20 trabajadores. Ejemplos sobre como
acceder a los campos y sus elementos: para ver el nombre del cuarto trabajador,
fijo[3].nombre;
Para ver la tercera letra del nombre del cuarto trabajador, fijo[3].nombre[2];. Para
inicializar la variable en el momento de declararla lo haremos de esta manera:
struct trabajador fijo[20]=
{{"José","Herrero Martínez",29},{"Luis","García Sánchez",46}};
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 92
6.2.5. Typedef
El lenguaje 'C' dispone de una declaración llamada typedef que permite la
creación de nuevos tipos de datos. Ejemplos:
/* acabamos de crear un tipo de dato llamado entero */
typedef int entero;
entero a, b=3; /* declaramos dos variables de este tipo */
Su empleo con estructuras está especialmente indicado. Se puede hacer de varias
formas:
Una forma de hacerlo:
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
};
typedef struct trabajador datos;
datos fijo,temporal;
Otra forma:
typedef struct
{
char nombre[20];
char apellidos[40];
int edad;
}datos;
datos fijo,temporal;
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 93
6.3. Ficheros
Ahora veremos la forma de almacenar datos que podremos recuperar cuando
deseemos. Estudiaremos los distintos modos en que podemos abrir un fichero, así
como las funciones para leer y escribir en él.
6.3.1. Apertura
Antes de abrir un fichero necesitamos declarar un puntero de tipo FILE, con el que
trabajaremos durante todo el proceso. Para abrir el fichero utilizaremos la
funciónfopen().
Su sintaxis es:
FILE *puntero;
puntero = fopen ( nombre del fichero, "modo de apertura" );
donde puntero es la variable de tipo FILE, nombre del fichero es el nombre que
daremos al fichero que queremos crear o abrir. Este nombre debe ir encerrado
entre comillas.
También podemos especificar la ruta donde se encuentra o utilizar un array que
contenga el nombre del archivo ( en este caso no se pondrán las comillas ).
Algunos ejemplos:
puntero=fopen("DATOS.DAT","r");
puntero=fopen("C:\\TXT\\SALUDO.TXT","w");
Un archivo puede ser abierto en dos modos diferentes, en modo texto o en modo
binario. A continuación lo veremos con más detalle.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 94
6.3.1.1. Modo texto
w crea un fichero de escritura. Si ya existe lo crea de nuevo.
w+ crea un fichero de lectura y escritura. Si ya existe lo crea de nuevo.
a abre o crea un fichero para añadir datos al final del mismo.
a+ abre o crea un fichero para leer y añadir datos al final del mismo.
r abre un fichero de lectura.
r+ abre un fichero de lectura y escritura.
6.3.1.2. Modo binario
wb crea un fichero de escritura. Si ya existe lo crea de nuevo.
w+b crea un fichero de lectura y escritura. Si ya existe lo crea de nuevo.
ab abre o crea un fichero para añadir datos al final del mismo.
a+b abre o crea un fichero para leer y añadir datos al final del mismo.
rb abre un fichero de lectura.
r+b abre un fichero de lectura y escritura.
La función fopen devuelve, como ya hemos visto, un puntero de tipo FILE. Si al
intentar abrir el fichero se produjese un error ( por ejemplo si no existe y lo
estamos abriendo en modo lectura ), la función fopen devolvería NULL. Por esta
razón es mejor controlar las posibles causas de error a la hora de programar. Un
ejemplo:
FILE *pf;
pf=fopen("datos.txt","r");
if (pf == NULL) printf("Error al abrir el fichero");
freopen(): Esta función cierra el fichero apuntado por el puntero y reasigna este
puntero a un fichero que será abierto. Su sintaxis es:
freopen(nombre del fichero,"modo de apertura",puntero);
Donde nombre del fichero es el nombre del nuevo fichero que queremos abrir,
luego el modo de apertura, y finalmente el puntero que va a ser reasignado.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 95
6.3.2. Cierre
Una vez que hemos acabado nuestro trabajo con un fichero es recomendable
cerrarlo. Los ficheros se cierran al finalizar el programa pero el número de estos
que pueden estar abiertos es limitado.
Para cerrar los ficheros utilizaremos la función fclose();. Esta función cierra el
fichero, cuyo puntero le indicamos como parámetro. Si el fichero se cierra con
éxito devuelve 0.
fclose (puntero);
Un ejemplo ilustrativo aunque de poca utilidad:
FILE *pf;
pf=fopen("AGENDA.DAT","rb");
if ( pf == NULL ) printf ("Error al abrir el fichero");
else fclose(pf);
6.3.3. Funciones de Escritura y lectura
A continuación veremos las funciones que se podrán utilizar dependiendo del dato
que queramos escribir y/o leer en el fichero.
6.3.3.1. Función fputc
fputc( variable_caracter , puntero_fichero );
Escribimos un carácter en un fichero (abierto en modo escritura). Un ejemplo:
FILE *pf;
char letra='a';
/* otra forma de controlar si se produce un error */
if (!(pf=fopen("datos.txt","w")))
{ printf("Error al abrir el fichero"); exit(0); }
else fputc(letra,pf);
fclose(pf);
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 96
6.3.3.2. Función fgetc
fgetc( puntero_fichero );
Lee un carácter de un fichero (abierto en modo lectura). Deberemos guardarlo en
una variable. Un ejemplo:
FILE *pf; char letra;
/* controlamos si se produce un error */
if (!(pf=fopen("datos.txt","r")))
{
printf("Error al abrir el fichero");
exit(0); /* abandonamos el programa */
}
else
{
letra=fgetc(pf);
printf("%c",letra);
fclose(pf);
}
6.3.3.3. Función putw
putw( variable_entera, puntero_fichero );
Escribe un número entero en formato binario en el fichero. Ejemplo:
FILE *pf;
int num=3;
if (!(pf=fopen("datos.txt","wb"))) /* controlamos si se produce un
error */
{
printf("Error al abrir el fichero");
exit(0); /* abandonamos el programa */
}
else
{
fputw(num,pf); /* también podíamos haber hecho directamente:
fputw(3,pf); */
fclose(pf);
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 97
6.3.3.4. Función getw
getw( puntero_fichero );
Lee un número entero de un fichero, avanzando dos bytes después de cada
lectura. Un ejemplo:
FILE *pf;
int num;
if (!(pf=fopen("datos.txt","rb"))) /* controlamos si se produce un
error */
{
printf("Error al abrir el fichero");
exit(0); /* abandonamos el programa */
}
else {
num=getw(pf);
printf("%d",num);
fclose(pf);
}
6.3.3.5. Función fputs
fputs( variable_array, puntero_fichero );
Escribe una cadena de caracteres en el fichero. Ejemplo:
FILE *pf;
char cad="Me llamo Vicente";
/* controlamos si se produce un error */
if (!(pf=fopen("datos.txt","w")))
{
printf("Error al abrir el fichero");
exit(0); /* abandonamos el programa */
}
Else {
/* o también así: fputs("Me llamo Vicente",pf); */
fputs(cad,pf);
fclose(pf);
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 98
6.3.3.6. Función fgets
fgets( variable_array, variable_entera, puntero_fichero );
Lee una cadena de caracteres del fichero y la almacena en variable_array. La
variable_entera indica la longitud máxima de caracteres que puede leer. Un
ejemplo:
FILE *pf;
char cad[80];
if (!(pf=fopen("datos.txt","rb"))) /* controlamos si se produce un
error */
{
printf("Error al abrir el fichero");
exit(0); /* abandonamos el programa */
}
else
{
fgets(cad,80,pf);
printf("%s",cad);
fclose(pf);
}
6.3.3.7. Función fprintf
fprintf( puntero_fichero, formato, argumentos);
Funciona igual que un printf pero guarda la salida en un fichero. Ejemplo:
FILE *pf;
char nombre[20]="Santiago"; int edad=34;
/* controlamos si se produce un error */
if (!(pf=fopen("datos.txt","w"))) {
printf("Error al abrir el fichero");
exit(0); /* abandonamos el programa */
}
else {
fprintf(pf,"%20s%2d\n",nombre,edad);
fclose(pf);
}
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 99
6.3.3.8. Función fscanf
fscanf( puntero_fichero, formato, argumentos );
Lee los argumentos del fichero. Al igual que con un scanf, deberemos indicar la
dirección de memoria de los argumentos con el símbolo & ( ampersand ). Un
ejemplo:
FILE *pf;
char nombre[20];
int edad;
if (!(pf=fopen("datos.txt","rb"))) /* controlamos si se produce un
error */
{
printf("Error al abrir el fichero");
exit(0); /* abandonamos el programa */
}
else
{
fscanf(pf,"%20s%2d\",nombre,&edad);
printf("Nombre: %s Edad: %d",nombre,edad);
fclose(pf);
}
6.3.3.9. Función fwrite
fwrite( *buffer, tamaño, nº de veces, puntero_fichero );
Se utiliza para escribir bloques de texto o de datos, estructuras, en un fichero. En
esta función, *buffer será la dirección de memoria de la cuál se recogerán los
datos; tamaño, el tamaño en bytes que ocupan esos datos y nº de veces, será el
número de elementos del tamaño indicado que se escribirán.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 100
6.3.3.10. Función fread
fread( *buffer, tamaño, nº de veces, puntero_fichero );
Se utiliza para leer bloques de texto o de datos de un fichero. En esta función,
*buffer es la dirección de memoria en la que se almacenan los datos; tamaño, el
tamaño en bytes que ocupan esos datos y nº de veces, será el número de
elementos del tamaño indicado que se leerán.
Puedes encontrar ejemplos sobre la apertura y cierre de ficheros, así como de la
lectura y escritura de datos, en el archivo IMAGECAT.C. Se trata de un programa
que crea un catálogo en formato HTML a partir de las imágenes que se
encuentran en un directorio determinado.
6.3.3.11. Función rewind
rewind( puntero_fichero );
Sitúa el puntero al principio del archivo.
6.3.3.12. Función fseek
fseek( puntero_fichero, long posicion, int origen );
Sitúa el puntero en la posicion que le indiquemos. Como origen podremos poner:
0 o SEEK_SET, el principio del fichero
1 o SEEK_CUR, la posición actual
2 o SEEK_END, el final del fichero
6.3.3.13. Función rename
rename( nombre1, nombre2 );
Su función es exactamente la misma que la que conocemos en MS-DOS. Cambia
el nombre del fichero nombre1 por un nuevo nombre, nombre2.
6.3.3.14. Función remove
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 101
remove( nombre );
Como la función del DOS del, podremos eliminar el archivo indicado en nombre.
6.3.3.15. Función feof
Detección de final de fichero
feof( puntero_fichero );
Siempre deberemos controlar si hemos llegado al final de fichero cuando estemos
leyendo, de lo contrario podrían producirse errores de lectura no deseados. Para
este fin disponemos de la función feof( ). Esta función retorna 0 si no ha llegado al
final, y un valor diferente de 0 si lo ha alcanzado.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 102
7. Gráficos en el Leguaje C
7.1. Programa texto vs. programa grafico
Antes de comenzar con el modo gráfico es necesario adentrarse en los conceptos
básicos que permiten conocerlo a plenitud. Los programas que hemos hecho
hasta ahora se basan en un modo pobre en el que sólo puede mostrarse
caracteres, dicho modo es conocido como modo texto o alfanumérico.
En estos modos, generalmente la pantalla se encuentra dividida por una matriz de
80 columnas y 25 filas. Cada celda de dicha matriz sólo puede contener un
carácter.
El inconveniente de este modo es que si quieres hacer un gráfico tienes que
ilustrarlo con base a caracteres y lo peor es la resolución limitada (80 x 25). Los
modos gráficos se caracterizan por su variada combinación de resolución y
colores.
Un píxel es la unidad mínima de dibujo en una pantalla. En las pantallas actuales,
un píxel tiene forma cuadrada y su color propio el cual es formado de una
combinación de tres colores básicos (Rojo, Verde, Azul).
Resolución es la cantidad de píxeles por fila y columna de una pantalla en ese
modo. Por ejemplo, 640 píxeles por fila y 480 por columna caben en el modo VGA.
Cuando vayamos a utilizar los modos gráficos debemos tener en cuenta que la
esquina superior izquierda es el origen de coordenadas y que la máxima
coordenada posible es (x-1,y-1).
Por ejemplo, si la resolución es VGA (640x480) la coordenada máxima posible es
(639,479) medida a partir de la esquina superior izquierda.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 103
Podemos ver, entonces las grandes diferencias entre los modos texto y grafico. Se
puede crear fácilmente un dibujo a partir de conjuntos de píxeles y colores, pero
es no quiere decir que no puedan mostrarse caracteres, ya que estos son también
conjuntos de puntos.
7.2. Inicialización del modo grafico
Para poder utilizar el modo gráfico, antes que nada debe iniciarse. Primero hay
que detectar el adaptador:
int gdriver=DETECT, gmode;
Donde gdriver=DETECT especifica que queremos que se auto-detecte el tipo
genérico de tarjeta gráfica que tenemos instalada y gmode abrirá el modo y
combinación de colores predeterminado. Frecuentemente la tarjeta grafica más
habitual es la VGA y el modo grafico 640 x 480 con 16 colores.
Otra alternativa para detectar la tarjeta grafica es usar la instrucción detectgraph:
detectgraph(int *adaptador, int *modo);
En el primer argumento se guarda la tarjeta grafica detectada, en modo se
almacena el modo por defecto.
initgraph(&gdriver,&gmode,”bgi”);
La instrucción initgraph inicia el modo grafico con los argumentos
correspondientes. El primer argumento corresponde al tipo de tarjeta grafica que
tengamos, el segundo refiere al modo grafico que queremos, y el tercer parámetro
al directorio donde se encuentran los ficheros BGI, los cuales contienen
información acerca de los modos de videos y funciones graficas.
Cabe destacar que el uso de ficheros BGI (Borland Graphics Interface) es un
inconveniente en cuanto a que estos ficheros deben mantenerse en la misma de
carpeta del programa compilado para que funcione, es una opción enlazarlos con
el programa ya compilado pero esto aumenta notablemente el tamaño del
programa.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 104
int graphresult( );
Reporta el estado del modo gráfico. Envía 0 si no ocurrió error, de lo contrario
retorna un valor en el rango[-1,-16].
char grapherrormsg(int error_id);
Manda un apuntador al mensaje de error respecto al valor retornado en
graphresult.
Si queremos terminar el modo grafico y regresar al modo texto usaremos la
función closegraph(). No es necesario ningún argumento.
closegraph();
La función restorecrtmode() nos devuelve al modo original.
restorecrtmode();
7.3. Dibujar en el modo gráfico (Primitivos Gráficos)
7.3.1. Pintar un punto:
putpixel(int x, int y, int color);
(x,y) es la ubicación del punto que queremos pintar.
En color se indica una constante COLORS o bien el numero del color
correspondiente.
0 BLACK 4 RED 8 DARKGRAY 12 LIGHTRED
1 BLUE 5 MAGENTA 9 LIGHTBLUE 13 LIGHTMAGENTA
2 GREEN 6 BROWN 10 LIGHTGREEN 14 YELLOW
3 CYAN 7 LIGHTGRAY 11 LIGHTCYAN 15 WHITE
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 105
7.3.2. Dibujar un segmento de recta:
line(int x1,int y1, int x2,int y2);
(x1,y1) y (x2,y2) puntos extremos del segmento de recta
7.3.3. Para dibujar una circunferencia:
circle(int x, int y, int r);
(x,y) es el centro de la circunferencia.
r es el radio de la circunferencia.
7.3.4. Para trazar un rectángulo:
rectangle(int x1 , int y1 , int x2 , int y2);
(x1,y1) es la esquina superior izquierda del rectángulo.
(x2,y2) es la esquina inferior derecha del rectángulo.
7.3.5. Dibujar un arco circular:
arc(int x , int y, int ai , int af , int r);
(x,y) es el centro del arco circular.
(ai-af) ángulo inicial y final del arco circular
r radio del arco circular
7.3.6. Dibujar un arco elíptico:
ellipse(int x, int y, int ai, int af, int rx, int ry);
(x,y) centro del arco eliptico
(ai-af) ángulo inicial y final del arco eliptico
rx, ry radio horizontal y vertical del arco eliptico
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 106
7.4. Cambiar el estilo de línea:
setlinestyle(int estilo, int separación ,int ancho);
Estilo especifica el estilo a usar.
0 Línea Continua 3 Guiones largos
1 Guiones 4 Puntos
2 Guiones largos y cortos
Separación especifica la separación entre los elementos de una línea no continua.
Ancho, el grosor de línea.
7.5. Funciones de relleno
7.5.1.1. Rellenar una región
floodfill(int x , int y , int frontera);
Rellena a partir del punto (x,y) el área limitada por el color frontera.
En frontera se indica una constante COLORS o bien el numero del color
correspondiente.
0 BLACK 4 RED 8 DARKGRAY 12 LIGHTRED
1 BLUE 5 MAGENTA 9 LIGHTBLUE 13 LIGHTMAGENTA
2 GREEN 6 BROWN 10 LIGHTGREEN 14 YELLOW
3 CYAN 7 LIGHTGRAY 11 LIGHTCYAN 15 WHITE
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 107
7.5.1.2. Elegir patrón de relleno
setfillstyle(int patron, int color);
El patrón puede tomar un valor de 0 a 12.
0 Patrón Vacío 7 Líneas horizontales y verticales cruzadas
1 Sólido 8 Líneas diagonales cruzadas
2 Líneas horizontales gruesas 9 Líneas diagonales cruzadas muy juntas
3 Líneas diagonales finas 10 Puntos separados.
4 Líneas diagonales gruesas 11 Puntos cercanos
5 Líneas diagonales gruesas 12 Patrón definido por el usuario
6 Líneas diagonales finas
En color se indica una constante COLORS o bien el numero del color
correspondiente.
0 BLACK 4 RED 8 DARKGRAY 12 LIGHTRED
1 BLUE 5 MAGENTA 9 LIGHTBLUE 13 LIGHTMAGENTA
2 GREEN 6 BROWN 10 LIGHTGREEN 14 YELLOW
3 CYAN 7 LIGHTGRAY 11 LIGHTCYAN 15 WHITE
7.5.1.3. Dibujar una barra rectangular
bar(int x1 , int y1, int x2 , int y2);
(x1,y1) esquina superior izquierda de la barra
(x2,y2) esquina inferior derecha de la barra
7.5.1.4. Dibujar una barra 3-D
bar3d(int x1, int y1, int x2, int y2, int altura, int tapa);
(x1,y1) esquina superior izquierda de la barra
(x2,y2) esquina inferior derecha de la barra
Altura es la profundidad de la barra
Tapa especifica si la barra debe incluir o no tapa
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 108
7.5.1.5. Dibujar un sector circular
pieslice(int x, int y, int ai , int af , int r);
(x,y) centro del arco del sector circular
(ai-af) ángulo inicial y final del arco del sector circular
r radio del arco del sector circular.
7.6. Funciones de escritura de texto
7.6.1. Mostrar texto en un lugar específico
outtextxy(int x , int y , char *texto);
Muestra el texto indicado a partir del punto (x,y).
7.6.2. Seleccionar el estilo del texto
settextstyle(int fuente , int orientación , int tamaño);
fuente define el tipo de letra.
0 Normal 3 Sans Seriff
1 Triplex 4 Gótica
2 Fuente pequeña
Orientación describe si el texto es horizontal o vertical.
Tamaño aumenta las dimensiones de los caracteres.
7.6.3. Cambiar los límites del dibujo.
setviewport(int x1, int y1, int x2, int y2, int recorte);
Un viewport es una zona rectangular delimitada para el dibujo
Las coordenadas en un viewport son relativas a su esquina superior izquierda.
(x1, y1) y (x2, y2) esquinas superior izquierda e inferior derecha del viewport.
El recorte especifica si se ve sólo el viewport o todo.
UNI – Universidad Nacional de Ingeniería
_________________________________________________________________________
_________________________________________________________________________
Programación II Página 109
7.6.4. Limpiar el viewport actual:
clearviewport();
7.6.5. Otras
Obtener la coordenada máxima horizontal.
Horizontal: int getmaxx( ); Vertical: int getmaxy();
Obtener la coordenada actual.
Horizontal: int getx( ); Vertical: int gety( );
Moverse a una coordenada especifica.
moveto(int x , int y);
Cambiar color de
Dibujo y texto: setcolor(color);
Fondo de pantalla: setbkcolor(color);
0 BLACK 4 RED 8 DARKGRAY 12 LIGHTRED
1 BLUE 5 MAGENTA 9 LIGHTBLUE 13 LIGHTMAGENTA
2 GREEN 6 BROWN 10 LIGHTGREEN 14 YELLOW
3 CYAN 7 LIGHTGRAY 11 LIGHTCYAN 15 WHITE
Obtener el color actual de.
Dibujo y texto: int getcolor( );
Fondo de pantalla: int getbkcolor( );
Un pixel en particular: int getpixel(int x , int y);
(x,y) coordenadas del pixel al cual se quiere
extraer el color
Borrar toda la pantalla
cleardevice( );