"APUNTES M3S1 PRINCIPIOS DE PROGRAMACION EN LA SOLUCION DE
PROBLEMAS"
ASIGNATURA
M3S1 "Aplicar los principios de programación en la solución de
problemas"
PERIODO
Febrero-Julio 2009
PRESENTA
Oscar Raúl Fuentes Martínez
Profesor CECYT I
INDICE
1 INTRODUCCION 5
2 INTRODUCCIÓN A LA PROGRAMACIÓN 6
2.1 Resolución de problemas a través de la computadora. 6
2.1.1 Definición del Problema 6
2.1.2 Análisis del Problema 6
2.1.3 Codificación 6
2.1.4 Prueba y Depuración 6
2.1.5 Documentación 6
2.1.6 Mantenimiento 7
3 ALGORITMOS 7
3.1.1 Definición y características de los algoritmos. 7
3.1.2 Reglas para la construcción de algoritmos. 8
3.1.2.1 Tipos de algoritmos. 8
3.1.3 Diagramas de Flujo Lineales 10
3.1.3.1 Tipos de Datos 10
3.1.3.1.1 Datos Numéricos 10
3.1.3.1.2 Cadenas 11
3.1.3.1.3 Lógicos 12
3.1.3.1.4 Tipos Compuestos 12
3.1.3.2 Constantes y variables 13
3.1.3.3 Operadores 13
3.1.3.3.1 Operadores Aritméticos 14
3.1.3.3.2 Operadores Div y Mod 14
3.1.3.3.3 Operadores Relacionales 15
3.1.3.3.4 Prioridad De Operadores Aritméticos y Relacionales 16
3.1.3.3.5 Operadores Lógicos 18
3.1.3.3.6 Prioridad De Los Operadores Lógicos 19
3.1.3.4 Asignación 19
3.1.3.4.1 Asignación Aritmética 20
3.1.3.4.2 Asignación Lógica 20
3.1.3.4.3 Asignación de caracteres 20
3.1.3.5 Entrada y Salida de Información 21
3.1.4 Estructuras Algorítmicas. 21
3.1.4.1 Estructura Secuencial. 22
3.1.4.2 Estructuras condicionales 24
3.1.4.2.1 Alternativa Simple (Si-Eentonces/If-Then). 24
3.1.4.2.2 Alternativas Múltiples (según_sea, caso de / case) 25
3.1.4.3 Estructuras Repetitivas. 27
3.1.4.3.1 Estructura Mientras (while) 27
3.1.4.3.2 Estructura Repetir (repeat) 29
3.1.4.3.3 Estructura Desde/Para (for) 29
4 LENGUAJE DE PROGRAMACIÓN C 31
4.1 Historia de C 31
4.2 Características clave de C 32
4.3 Compiladores de C 33
4.4 Palabras reservadas de C (ANSI-C) 34
4.5 Primer programa en C 35
4.6 Identificadores 36
4.7 Constantes y Variables 36
4.7.1 Variables 36
4.7.1.1 Reglas para nombres de variables 36
4.7.1.2 Tipos de Datos numéricos 37
4.7.1.3 Declaración de Variables 37
4.7.1.4 Inicialización de Variables. 37
4.7.2 Constantes 37
4.7.2.1 Constantes Literales. 38
4.7.2.2 Constantes simbólicas 38
4.7.2.3 Declaración de Constantes 38
4.7.2.3.1 La directiva #Define 38
4.7.2.3.2 Const 38
4.8 Enunciados, expresiones y operadores. 39
4.8.1 Enunciados 39
4.8.2 Expresiones 39
4.8.3 Operadores 39
4.8.3.1 Operador de asignación 39
4.8.3.2 Operadores matemáticos. 39
4.8.3.2.1 Operadores Unuarios: 39
4.8.3.2.2 Operadores Binarios 40
4.8.3.3 Operadores de Relación 41
4.8.3.4 Operadores lógicos 41
4.9 Entrada y Salida de datos. 42
4.9.1 Función puts() 42
4.9.2 Función Printf() 42
4.9.3 Función Scanf() 42
4.9.4 Función Gets() 43
4.10 Estructuras de Control 43
4.10.1 Estructuras condicionales 43
4.10.1.1 El Enunciado IF 43
4.10.1.2 El enunciado IF….Then…..Else 44
4.10.1.3 EL enunciado Switch 45
4.10.2 Estructuras ciclicas 46
4.10.2.1 El enunciado For 46
4.11 Funciones 47
4.11.1 Prototipo de Función 47
4.11.2 Definición de Función 47
4.11.3 Estructura de una función 47
4.11.4 Llamado de funciones 47
4.11.5 Paso de Argumentos 47
4.12 Arreglos 48
4.12.1 Arreglos Unidimensionales (Vectores) 48
4.12.1.1 Inicializar un arreglo 49
4.12.1.2 Asignación de un arreglo: 49
4.12.2 Arreglos Bidimensionales (MATRICES) 50
4.12.2.1 Definición 50
4.13 REGISTROS 53
4.13.1 Conceptos Generales 53
4.13.1.1 Concepto de Campo 53
4.13.1.2 Concepto de registro 53
4.13.2 Estructuras 53
4.13.3 Enumeraciones 55
4.13.4 Uniones 56
4.13.5 Definición de nuevos nombres para tipos de datos 58
4.13.6 Convirtiendo tipos de datos (cast) 58
4.13.7 Ejercicios 61
4.14 ARCHIVOS 61
4.14.1 Clasificación de Archivos 62
4.14.2 Creación de un archivo de datos. 65
4.14.3 Archivos de Acceso Aleatorio 67
4.14.4 Calcular la longitud de un fichero 68
4.14.5 Borrar registros 68
4.14.6 Ejemplo: 69
4.15 Apuntadores 72
4.15.1 Declaración de punteros. 73
4.15.2 Obtener punteros a variables. 73
4.15.3 Diferencia entre punteros y variables. 74
4.15.4 Correspondencia entre arrays y punteros 74
4.15.5 Operaciones con punteros. 75
4.15.6 Asignación. 76
4.15.7 Operaciones aritméticas. 76
5 BIBLIOGRAFIA 79
INTRODUCCION
El presente apunte pretende poner a disposición de los alumnos del 4to
semestre de la carrera de informática temas y ejercicios que cubre el
contenido que marca el programa de estudio del M3S1 "APLICAR LOS PRINCIPIOS
DE PROGRAMACION EN LA SOLUCION DE PROBLEMAS". El contenido inicia con los
temas básicos sobre los principios de programación y más adelante con los
temas y ejercicios en lenguaje C.
Sobre el contenido de la habilidad 1. Desarrollar el proceso de solución de
un problema, el presente apunte contiene información sobre el proceso para
la resolución de problemas por medio de la computadora. Elaboración de
algoritmos, diagramas de flujo y pseudocódigos.
Sobre la habilidad 2. Codificar la solución de un problema en un lenguaje
de programación estructurado, el presente apunte contiene información del
lenguaje estructurado C, tipos de datos, variables y constantes, funciones,
estructuras de control, registros, arreglos y apuntadores; Además de
ejercicios que muestran la aplicación del lenguaje C.
Sobre la habilidad 3 Poner a punto el programa mediante el uso de un
editor de lenguaje de programación estructurado, el presente apunte
contiene información de los compiladores de C más utilizados.
INTRODUCCIÓN A LA PROGRAMACIÓN
1 Resolución de problemas a través de la computadora.
El proceso de resolución de problemas con computadoras conduce a la
escritura de un programa y su ejecución en la misma. Aunque el proceso de
diseñar programas es esencialmente un proceso creativo, se puede considerar
una serie de fases o pasos comunes que generalmente deben seguir los
programadores. Estas fases son las siguientes:
1 Definición del Problema
Esta fase está dada por el enunciado del problema, el cual requiere una
definición clara y precisa. Es importante que se conozca lo que se desea
que realice la computadora; mientras esto no se conozca del todo no tiene
mucho caso continuar con la siguiente etapa.
2 Análisis del Problema
Una vez que se ha comprendido lo que se desea de la computadora, es
necesario definir:
1. Los datos de entrada.
2. Cuál es la información que se desea producir (salida).
3. Los métodos y fórmulas que se necesitan para procesar los datos.
Una recomendación muy práctica es el que nos pongamos en el lugar de la
computadora y analicemos que es lo que necesitamos que nos ordenen y en que
secuencia para producir los resultados esperados.
3 Codificación
La codificación es la operación de escribir la solución del problema (de
acuerdo a la lógica del diagrama de flujo o pseudocódigo), en una serie de
instrucciones detalladas, en un código reconocible por la computadora, la
serie de instrucciones detalladas se le conoce como código fuente, el cual
se escribe en un lenguaje de programación o lenguaje de alto nivel.
4 Prueba y Depuración
Los errores humanos dentro de la programación de computadoras son muchos y
aumentan considerablemente con la complejidad del problema. El proceso de
identificar y eliminar errores, para dar paso a una solución sin errores se
le llama depuración. La depuración o prueba resulta una tarea tan creativa
como el mismo desarrollo de la solución, por ello se debe considerar con el
mismo interés y entusiasmo.
5 Documentación
Es la guía o comunicación escrita es sus variadas formas, ya sea en
enunciados, procedimientos, dibujos o diagramas. A menudo un programa
escrito por una persona, es usado por otra. Por ello la documentación sirve
para ayudar a comprender o usar un programa o para facilitar futuras
modificaciones.
La documentación se divide en tres partes:
1. Documentación Interna: son los comentarios o mensajes que se añaden al
código fuente para hacer más claro el entendimiento de un proceso.
2. Documentación Externa: se define en un documento escrito los
siguientes puntos:
Descripción del Problema.
Nombre del Autor.
Algoritmo (diagrama de flujo o pseudocódigo).
Diccionario de Datos.
Código Fuente (programa).
1. Manual del Usuario: describe paso a paso la manera cómo funciona el
programa, con el fin de que el usuario obtenga el resultado deseado.
6 Mantenimiento
Se lleva a cabo después de terminado el programa, cuando se detecta que es
necesario hacer algún cambio, ajuste o complementar al programa para que
siga trabajando de manera correcta. Para poder realizar este trabajo se
requiere que el programa este correctamente documentado.
ALGORITMOS
1 Definición y características de los algoritmos.
Un algoritmo es una secuencia de pasos lógicos necesarios para llevar a
cabo una tarea específica, como la solución de un problema. Los algoritmos
son independientes tanto del lenguaje de programación en que se expresan
como de la computadora que los ejecuta. En cada problema el algoritmo se
puede expresar en un lenguaje diferente de programación y ejecutarse en una
computadora distinta; sin embargo el algoritmo será siempre el mismo.
A primera vista, se puede pensar que el conocimiento de estos algoritmos
y estructuras de datos no tienen una aplicación práctica inmediata. Sin
embargo, su conocimiento y correcta aplicación sirven para producir
programas mejores, en el sentido de que aprovechan mejor la memoria del
sistema, son más rápidos, eficientes, robustos y tolerantes a fallos.
Las aplicaciones de estos algoritmos en algunos casos son inmediatas; por
ejemplo, hallar el trayecto más corto entre dos estaciones es algo que
interesa a muchos viajeros del metro y se pueden obtener aproximaciones
bastante buenas del mundo real utilizando algunos de los algoritmos que
obtienen distancias mínimas. Otros algoritmos sirven para procesar cadenas,
lo cual sirve de base para analizadores léxicos o algoritmos
criptográficos, por ejemplo.
Además, tener conocimientos adecuados de algoritmia y estructuras de
datos facilita el poder pasar de un lenguaje de programación a otro con
mucha mayor facilidad: puesto que ya se tiene la base, sólo hace falta
superar las dificultades técnicas particulares de cada lenguaje.
Las características fundamentales que debe cumplir todo algoritmo son:
Un algoritmo debe ser preciso e indicar el orden de realización de
cada paso.
Un algoritmo debe estar definido. Si se sigue un algoritmo dos veces,
se debe obtener el mismo resultado cada vez.
Un algoritmo debe ser finito. Si se sigue un algoritmo, se debe
terminar en algún momento; o sea debe de tener un número finito de
pasos.
La definición de un algoritmo debe describir tres partes: Entrada, Proceso
y Salida.
2 Reglas para la construcción de algoritmos.
1 Tipos de algoritmos.
* Algoritmo determinista: en cada paso del algoritmo se determina de
forma única el siguiente paso.
* Algoritmo no determinista: deben decidir en cada paso de la ejecución
entre varias alternativas y agotarlas todas antes de encontrar la solución.
Todo algoritmo tiene una serie de características, entre otras que requiere
una serie de recursos, algo que es fundamental considerar a la hora de
implementarlos en una máquina. Estos recursos son principalmente: · El
tiempo: período transcurrido entre el inicio y la finalización del
algoritmo. · La memoria: la cantidad (la medida varía según la máquina)
que necesita el algoritmo para su ejecución. Obviamente, la capacidad y el
diseño de la máquina pueden afectar al diseño del algoritmo.
En general, la mayoría de los problemas tienen un parámetro de entrada que
es el número de datos que hay que tratar, esto es, N. La cantidad de
recursos del algoritmo es tratada como una función de N. De esta manera
puede establecerse un tiempo de ejecución del algoritmo que suele ser
proporcional a una de las siguientes funciones:
1: Tiempo de ejecución constante. Significa que la mayoría de las
instrucciones se ejecutan una vez o muy pocas.
logN: Tiempo de ejecución logarítmico. Se puede considerar como una
gran constante. La base del logaritmo (en informática la más común es
la base 2) cambia la constante, pero no demasiado. El programa es más
lento cuanto más crezca N, pero es inapreciable, pues logN no se
duplica hasta que N llegue a N2.
N: Tiempo de ejecución lineal. Un caso en el que N valga 40, tardará
el doble que otro en que N valga 20. Un ejemplo sería un algoritmo que
lee N números enteros y devuelve la media aritmética.
Nylon: El tiempo de ejecución es Nylon. Es común encontrarlo en
algoritmos como Quick Sort y otros del estilo divide y vencerás. Si N
se duplica, el tiempo de ejecución es ligeramente mayor del doble.
N2: Tiempo de ejecución cuadrático. Suele ser habitual cuando se
tratan pares de elementos de datos, como por ejemplo un bucle anidado
doble. Si N se duplica, el tiempo de ejecución aumenta cuatro veces.
El peor caso de entrada del algoritmo Quick Sort se ejecuta en este
tiempo.
N3: Tiempo de ejecución cúbico. Como ejemplo se puede dar el de un
bucle anidado triple. Si N se duplica, el tiempo de ejecución se
multiplica por ocho.
2N: Tiempo de ejecución exponencial. No suelen ser muy útiles en la
práctica por el elevadísimo tiempo de ejecución. El problema de la
mochila resuelto por un algoritmo de fuerza bruta -simple vuelta atrás-
es un ejemplo. Si N se duplica, el tiempo de ejecución se eleva al
cuadrado.
* Algoritmos polinomiales: aquellos que son proporcionales a Nk. Son en
general factibles.
* Algoritmos exponenciales: aquellos que son proporcionales a kN. En
general son infactibles salvo un tamaño de entrada muy reducido.
- Notación O-grande
En general, el tiempo de ejecución es proporcional, esto es, multiplica por
una constante a alguno de los tiempos de ejecución anteriormente
propuestos, además de la suma de algunos términos más pequeños. Así, un
algoritmo cuyo tiempo de ejecución sea T = 3N2 + 6N se puede considerar
proporcional a N2. En este caso se diría que el algoritmo es del orden de
N2, y se escribe O (N2) Los grafos definidos por matriz de adyacencia
ocupan un espacio O(N2), siendo N el número de vértices de éste.
La notación O-grande ignora los factores constantes, es decir, ignora si se
hace una mejor o peor implementación del algoritmo, además de ser
independiente de los datos de entrada del algoritmo. Es decir, la utilidad
de aplicar esta notación a un algoritmo es encontrar un límite superior del
tiempo de ejecución, es decir, el peor caso.
A veces ocurre que no hay que prestar demasiada atención a esto. Conviene
diferenciar entre el peor caso y el esperado. Por ejemplo, el tiempo de
ejecución del algoritmo Quick Sort es de O(N2). Sin embargo, en la práctica
este caso no se da casi nunca y la mayoría de los casos son proporcionales
a N·logN. Es por ello que se utiliza esta última expresión para este método
de ordenación.
Una definición rigurosa de esta notación es la siguiente: Una función g(N)
pertenece a O(f(N)) si y sólo si existen las constantes c0 y N0 tales
que:"g(N)" <= "c0·f(N)" , para todo N >= N0.
3 Diagramas de Flujo Lineales
1 Tipos de Datos
Un dato se define como la expresión general que describe los
objetos con los cuales opera una computadora. Los datos de entrada se
transforman por el programa, después de las etapas intermedias, en datos de
salida.
Los datos se clasifican en diversas categorías, según el tipo de
máquina o del lenguaje en uso. Generalmente podemos encontrar las
siguientes categorías :
"["Numéricos "
"p" "
"i" "
"c" "
"]" "
"["Lógicos "
"p" "
"i" "
"c" "
"]" "
"["Cadenas "
"p" "
"i" "
"c" "
"]" "
1 Datos Numéricos
Son aquéllos que representan una cantidad o valor determinado. Su
representación se lleva a cabo en los formatos ya conocidos (enteros, punto
y fracciones decimales si estas existen).
Estos pueden representarse en dos formas distintas :
"["Tipo Numérico Entero (integer). "
"p" "
"i" "
"c" "
"]" "
"["Tipo Numérico Real (real). "
"p" "
"i" "
"c" "
"]" "
Enteros
Es un conjunto finito de los números enteros. Los enteros son
números completos, no tienen componentes fraccionarios o decimales y pueden
ser negativos y positivos.
Algunos ejemplos son :
"["3 7 "
"p" "
"i" "
"c" "
"]" "
"["-10 9 "
"p" "
"i" "
"c" "
"]" "
"["15.25 "
"p" "
"i" "
"c" "
"]" "
"["50 "
"p" "
"i" "
"c" "
"]" "
Reales
Consiste en un subconjunto de los números reales. Estos números
siempre tienen un punto decimal y pueden ser positivos o negativos. Un
número real consiste de un número entero y una parte decimal. Algunos
ejemplos son :
"["0.52 664.32 "
"p" "
"i" "
"c" "
"]" "
"["6.579 8.0 "
"p" "
"i" "
"c" "
"]" "
"["-9.3 -47.23 "
"p" "
"i" "
"c" "
"]" "
2 Cadenas
Son los datos que representan información textual (palabras,
frases, símbolos, etc). No representan valor alguno para efectos numéricos.
Pueden distinguirse porque son delimitados por apóstrofes o comillas.
Se clasifica en dos categorías :
" "Datos tipo carácter (char) "
" "Datos tipo Cadena (string) "
Datos Tipo Carácter
Es un conjunto finito y ordenado de caracteres que la computadora
reconoce. Un dato de este tipo contiene solo un carácter.
Reconoce los siguientes caracteres :
"["Caracteres Alfabéticos (A,B,C,…Z,a,b,c…z) "
"p" "
"i" "
"c" "
"]" "
"["Caracteres Numéricos (0,1,2,…9) "
"p" "
"i" "
"c" "
"]" "
"["Caracteres Especiales (+, -, *, /, ^, . , ;, <, >, $, …….) "
"p" "
"i" "
"c" "
"]" "
Datos Tipo Cadena (string)
Es una sucesión de caracteres que se encuentran delimitados por una
comilla (apóstrofe) o dobles comillas, según el tipo de lenguaje de
programación. La longitud de una cadena de caracteres es el número de ellos
comprendidos entre los separadores o delimitadores.
Ejemplos :
"["'Hola Mortimer' "
"p" "
"i" "
"c" "
"]" "
"["'12 de octubre de 1496' "
"p" "
"i" "
"c" "
"]" "
"["'Enunciado cualquiera' "
"p" "
"i" "
"c" "
"]" "
Nota: Los símbolos disponibles para la formulación de caracteres y
de cadenas son aquéllos que se encuentran en el código ASCII.
ASCII (American Standard Code for Information Interchange).
3 Lógicos
También se le denomina Booleano, es aquél dato que solo puede tomar
uno de dos valores : Falso y verdadero.
Se utiliza para representar las alternativas (si/no) a determinadas
condiciones. Por ejemplo, cuando se pide si un valor entero sea primo, la
respuesta será verdadera o falsa, según sea.
Las categorías y tipos que se mencionaron anteriormente se conocen
como Tipos Simples, puesto que no poseen una estructura compleja.
En forma adicional, cada lenguaje puede proporcionar la utilización
de Tipos Compuestos, siendo estos, datos que tienen una estructura
predeterminada.
4 Tipos Compuestos
Entre los principales tipos compuestos se encuentran los siguientes:
a.- SUBRANGO : Son aquéllos en los que se específica con precisión el
intervalo de valores válidos para un dato.
Ejemplos:
0..100 (son enumerativos de tipo entero)
'A'..'Z' (son enumerativos de tipo cadena)
Los Reales no son válidos para crear enumerativos, ya que su intervalo no
está definido.
b.- ENUMERATIVOS : Son aquéllos en los que se definen individualmente
los valores para un dato.
Ejemplos:
(0,25,40,52) Siempre deben ponerse netre
paréntesis.
c.- DEFINIDOS POR EL USUARIO : Son aquéllos que el programador crea
para satisfacer las necesidades del programa en diseño.
2 Constantes y variables
Una Constante es aquélla que no cambia de valor durante la
ejecución de un programa (o comprobación de un algoritmo en este caso).
Se representa en la forma descrita para cada categoría.
Las Variables son aquéllas que pueden modificar su valor durante la
ejecución de un programa (idem).
Su representación se da a través de letras y símbolos generalmente
numéricos a los que se les asigna un valor.
Ejemplos:
" "Constantes "Variables "
"Numéricos " 36 "A "
" " 450.35 "Nom "
" " 0.58 "Edad "
" " "Ciudad "
" " "Estatura "
" " " "
"Cadena " 'A' " "
" " 'Juan' " "
" " 'La Paz' " "
"Lógicos " Falso " "
" " Verdadero " "
Operadores y Operandos
3 Operadores
Un operador es el símbolo que determina el tipo de operación o
relación que habrá de establecerse entre los operandos para alcanzar un
resultado.
Los operadores se clasifican en tres grupos:
"["Aritméticos. "
"p" "
"i" "
"c" "
"]" "
"["Relacionales. "
"p" "
"i" "
"c" "
"]" "
"["Lógicos. "
"p" "
"i" "
"c" "
"]" "
1 Operadores Aritméticos
Son aquéllos que permiten la realización de cálculos aritméticos.
Utilizan operandos numéricos y proporcionan resultados numéricos.
"Operador "Operación "
"+ "Suma "
"- "Resta "
"* "Multiplicación "
"/ "División real "
"Div "División entera "
"Mod "Residuo "
"^ "Exponenciación "
Ejemplos:
7+3 = 10 10 Div 4 = 2
7-3 = 4 20 Mod 3 = 2
7*3 = 21 5 Mod 7 = 5
10/4= 2.5 4 ^ 2 = 16
En la expresión 7+3, los valores 7 y 3 se denominan operandos. El
valor de la expresión 7+3 se conoce como resultado de la expresión.
Todos los operadores aritméticos no existen en todos los lenguajes
de programación, por ejemplo, en Fortran no existen Div y mod.
2 Operadores Div y Mod
El símbolo / se utiliza para la división real, y el operador Div representa
la división entera.
"Expresión "Resultado "Expresión "Resultado "
"10.5/3.0 "3.5 "10 Div 3 "3 "
"1/4 "0.25 "18 Div 2 "9 "
"2.0/4.0 "0.5 "30 Div 30 "1 "
"30/30 "1.0 "10 Mod 3 "1 "
"6/8 "0.75 "10 Mod 2 "0 "
3 Operadores Relacionales
Permiten realizar comparaciones de valores de tipo numérico o
carácter. Estos operadores sirven para expresar las condiciones en los
algoritmos. Proporcionan resultados lógicos.
"Operador "Significado "
"< "Menor que "
"> "Mayor que "
"= "Igual que "
"<= "Menor o igual que "
">= "Mayor o igual que "
"<> "Diferente de "
El formato general para las comparaciones es:
expresión1 operador de relación expresión2
El resultado de la operación será Verdadero o Falso. Así por ejemplo, si
A=4 y B=3, entonces:
A>B Es Verdadero
(A-2) < (B-4) Es Falso
Los operadores de relación se pueden aplicar a cualquiera de los
cuatro tipos de datos estándar: enteros, real, lógico y carácter.
'A' < 'K' = Verdadero
'A' > 'a' = Falso
'MARIA' < 'JUAN' = Falso (se considera la primera letra)
'JAIME' > 'JORGE' = Falso
Nota: La comparación de cadenas se rige por el código ASCII.
4 Prioridad De Operadores Aritméticos y Relacionales
Determina el orden en que habrán de realizarse las operaciones en
una expresión determinada. Para obtener la prioridad se deben conocer las
siguientes reglas:
" "Las operaciones que están encerradas entre paréntesis se evalúan"
" "primero. Si existen diferentes paréntesis anidados (interiores "
" "unos a otros), las expresiones más internas se evalúan primero. "
" "Las operaciones aritméticas dentro de una expresión suelen "
" "seguir el siguiente orden de prioridad. "
"Operador "Prioridad "
"^ "Alta "
"*, /, Div " "
"+, -, Mod " "
"Relacionales "Baja "
En caso de coincidir varios operadores de igual prioridad en una
expresión o subexpresión encerrada entre paréntesis, el orden de prioridad
en este caso es de izquierda a derecha.
Cuando se desea realizar una operación con baja prioridad por
adelantado, debe agruparse a los operandos involucrados.
4 + 12 /2 = 10 (sin agrupar)
(4 + 12) /2 = 8 (con agrupador)
Ejemplo:
Obtener los resultados de las expresiones:
-4 * 7 + 2 ^ 3 / 4 - 5
Solución:
-4 *7 + 2 ^ ¾
Resulta:
-4 * 7+ 8/4 -5
-28 + 8/4 -5
-28 + 2 - 5
-26 - 5
-31
Los paréntesis tienen prioridad sobre el resto de las operaciones.
A * (B+3) La constante 3 se suma primero al valor de B,
después este resultado se multiplica por el
valor de A.
(A*B) +3 A y B Se multiplican primero y a continuación se suma 3.
A + (B/C) + D Esta expresión equivale a A+ B/C + D
5 Operadores Lógicos
Son aquéllos que permiten la combinación de condiciones para formar
una sola expresión lógica. Utilizan operandos lógicos y proporcionan
resultados lógicos también.
"Operador "Relación "
"Not "Negación (No) "
"And "Conjunción (Y) "
"Or "Disyunción (O) "
"Xor "Disyunción "
" "Exclusiva (O/SOLO)"
"Se obtiene Verdadero si: "
"NOT "El operando es falso "
"AND "Ambos operandos son "
" "verdaderos "
"OR "Al menos un operando es "
" "verdadero "
"XOR "Solo uno de los operandos "
" "son verdadero "
"X "Y "NOT(X) "NOT(Y) "X AND Y "X OR Y "X XOR Y "
"F "F "V "V "F "F "F "
"V "F "F "V "F "V "V "
"F "V "V "F "F "V "V "
"V "V "F "F "V "V "F "
6 Prioridad De Los Operadores Lógicos
Los operadores aritméticos seguían un orden específico o de
prioridad cuando existía más de un operador en las expresiones. De modo
similar los operadores lógicos y relacionales tienen un orden de prioridad.
Ejemplos:
Not 4 > 6 Produce un error, ya que el
operador not se aplica a 4.
Not (4 > 14) Produce un valor verdadero.
(1.0
valor falso.
4 Asignación
La operación de asignación es el modo de darle valores a una
variable. La operación de asignación se representa por el símbolo u
operador . La operación de asignación se conoce como instrucción o
sentencia de asignación cuando se refiere a un lenguaje de programación.
A fin de manejar datos por medio de variables, estos pueden recibir
valores determinados. El tipo de los valores que pueden recibir dependen
de la declaración previa de tales variables.
En una asignación se resuelve, primeramente la expresión (al lado
derecho del símbolo de asignación) y se asigna el resultado en la variable.
El formato general de asignación es:
Nom_variable Expresión
Donde Expresión puede ser una variable o constante, operación, función.
Ejemplo:
A 9
Significa que la variable A se le ha asignado el valor 9. La acción
de asignar es destructiva, ya que el valor que tuviera la variable antes de
la asignación se pierde y se reemlaza por el nuevo valor. Así en la
secuencia de operaciones:
A 30
A 189
A 9
Cuando se ejecutan, el último valor que toma A será 9, ya que los valores
anteriores a este han desaparecido.
Las acciones de asignación se clasifican según sea el tipo de
expresiones : Aritméticas, Lógicas y de Caracteres.
1 Asignación Aritmética
Las expresiones en las operaciones de asignación son aritméticas:
Suma 5+10+2 Se evalúa la expresión 5+10+2 y se asigna a
la variable Suma, es decir, 17 será
el valor que toma Suma.
2 Asignación Lógica
La expresión que se evalúa en la operación de asignación es lógica.
Supóngase que M, N, y P son variables de tipo lógico.
M 8 < 5
N M o (7 <= 12)
P 7 > 6
Tras ejecutar las operaciones anteriores, las variables M,N,P toman
los valores, falso, verdadero, verdadero respectivamente.
3 Asignación de caracteres
La operaciómn que se evalúa es de tipo caracter.
x '3 de Mayo de 1999'
La acción de asignación anterior asigna la cadena de caracteres '3 de Mayo
de 1999' a la variable de tipo carácter x.
5 Entrada y Salida de Información
Los cálculos que realizan las computadoras requieren para ser
útiles la Entrada de los datos necesarios para ejecutar las operaciones que
posteriormente se convertirán en resultados, es decir, Salida.
Las operaciones de entrada permiten leer determinados valores y
asignarlos a determinadas variables. Esta entrada se conoce como operación
de Lectura (read). Los datos de entrada se introducen al procesador
mediante dispositivos de entrada (teclado, unidades de disco, etc). La
salida puede aparecer en un dispositivo de salida (pantalla, impresora,
etc). La operación de salida se denomina escritura (write).
En la escritura de algoritmos las acciones de lectura y escritura
se representan por los formatos siguientes
leer ( Nom_variable )
escribir (lista de variables de salida)
Las expresiones son combinaciones de constantes, variables, símbolos de
operadores, paréntesis y nombres de funciones especiales. Las mismas ideas
son utilizadas en notación matemática tradicional ; por ejemplo :
a + b ( b+2) Aquí los paréntesis indican el orden de cálculo.
Cada expresión toma un valor que se determina tomando los valores
de las variables y constantes implicadas y la ejecución de las operaciones
indicadas.
4 Estructuras Algorítmicas.
`
1 Estructura Secuencial.
Es aquélla en la que una acción (instrucción) sigue a otra en
secuencia. Las tareas se suceden de tal modo que la salida de una es la
entrada de la siguiente y así sucesivamente hasta el fin del proceso. La
estructura secuencial tiene una entrada y una salida. Su representación
gráfica es la siguiente:
Estructura Secuencial
....
Pseudocodigo de una estructura secuencial
Inicio
:
:
acciones
:
:
fin
Ejemplo:
Calcular el salario neto de un trabajador en función del número de
horas trabajadas, precio de la hora de trabajo y considerando unos
descuentos fijos al sueldo bruto en concepto de impuestos (20 por 100).
Pseudocódigo
Inicio
{cálculo salario neto}
leer nombre, horas, precio_hora
salario_bruto horas * precio
impuestos 0.20 * salario_bruto
salario_neto salario_bruto_impuestos
escribir nombre, salario_bruto, salario_neto_bruto, salario_neto
Fin
Diagrama de flujo
2 Estructuras condicionales
La especificación formal de algoritmos tiene realmente utilidad cuando
el algoritmo requiere una descripción más complicada que una lista sencilla
de instrucciones. Este es el caso cuando existen un número de posibles
alternativas resultantes de la evaluación de una determinada condición.
Las estructuras selectivas se utilizan para tomar decisiones
lógicas; de ahí que se suelan denominar también estructuras de decisión o
alternativas.
En las estructuras selectivas se evalúa una condición y en función del
resultado la misma se realiza una opción u otra. Las condiciones se
especifican usando expresiones lógicas. La representación de una estructura
selectiva se hace con palabras en pseudocódigo (if, then, else o bien en
español si, entonces, sino), con una figura geométrica en forma de rombo o
bien con un triángulo en el interior de una caja rectangular.
1 Alternativa Simple (Si-Eentonces/If-Then).
La estructura alternativa simple si-entonces (en inglés if-then o
bien IF-THEN) ejecuta una determinada acción cuando se cumple una
determinada condición. La selección si-entonces evalúa la condición y
Si la condición es verdadera, entonces ejecuta la acción S1 (o
acciones en caso de ser S1 una acción compuesta y constar de varias
acciones).
Si la condición es falsa, entonces no hacer nada.
A continuación se muestra la gráfica de la estructura condicional simple.
Pseudocódigo en español Pseudocódigo en inglés
Si
entonces If then
Fin_si end_if
2 Alternativas Múltiples (según_sea, caso de / case)
Cuando existen más de dos elecciones (alternativas) posibles, es
cuando se presenta el caso de alternativas múltiples. Si el número de
alternativas es grande puede plantear serios problemas de escritura del
algoritmo y naturalmente de legibilidad.
La estructura de decisión múltiple evaluará una expresión que
podrá tomar n valores distintos 1, 2, 3, 4, .., n . Según que elija uno de
estos valores en la condición, se realizará una de las n acciones, o lo que
es igual, el flujo del algoritmo seguirá un determinado camino entre los n
posibles.
La representación gráfica se muestra a continuación:
Diagrama de Flujo
Pseudocódigo.
En inglés la estructura de decisión múltiple se representa
Case expresión of
[e1]: acción S1
[e2]: acción S2
:
[en]: acción Sn
else
acción Sx
end_case
Ejemplo:
Se desea diseñar un algoritmo que escriba los nombres de los días
de la semana en función del valor de una variable DIA introducida por
teclado.
Los días de la semana son 7; por consiguiente, el rango de valores
de DIA será 1..7, y caso de que DIA tome un valor fuera de este rango se
deberá producir un mensaje de error advirtiendo la situación anómala.
Inicio
Leer DIA
Según_sea DIA hacer
1: escribir('Lunes')
2: escribir('Martes')
3: escribir('Miércoles')
4: escribir('Jueves')
5: escribir('Viernes')
6: escribir('Sabado')
7: escribir('Domingo')
else
escribir('Error')
fin_según
fin
3 Estructuras Repetitivas.
Las estructuras que repiten una secuencia de instrucciones un
número determinado de veces se denominan Bucles y se denomina Iteración al
hecho de repetir la ejecución de una secuencia de acciones. Entre las
estructuras repetitivas se encuentran:
" "Mientras (while) "
" "Repetir (repeat) "
" " "
" "Desde (for) "
1 Estructura Mientras (while)
La estructura repetitiva while, es aquélla en que el cuerpo del bucle
se repite mientras se cumple una determinada condición, su representación
gráfica es:
Pseudocódigo en español Pseudocódigo en inglés
Mientras condición hacer while condición
do
Acción S1
Acción S2
:
:
End_while
acción Sn
Fin_mientras
Ejemplo:
Contar los números enteros positivos introducidos por teclado. Se
consideran dos variables enteras NUMERO y CONTADOR (contará el número de
enteros positivos). Se supone que se leen números positivos y se detiene
el bucle cuando se lee un número negativo o cero.
Pseudocódigo
Inicio
contador 0
Leer (numero)
Mientras numero > 0 hacer
contador contador+1
Leer (numero)
Fin_Mientras
Escribir('El número de enteros positivos es : ',
contador)
Fin
Diagrama de Flujo
2 Estructura Repetir (repeat)
Esta estructura se ejecuta hasta que se cumpla una condición
determinada que se comprueba hasta el final del bucle. Se ejecuta al menos
una vez. El bucle repetir-Hasta_que se repite mientras el valor de la
expresión booleana de la condición sea falsa, justo la opuesta de la
sentencia mientras.
Pseudocódigo en Español Pseudocódigo en
Inglés
Repetir
Repeat
:
:
Hasta que Until
Diagrama de Flujo de Repetir
3 Estructura Desde/Para (for)
En muchas ocasiones se conoce de antemano el número de veces que se
desean ejecutar las acciones de un bucle. En estos casos en el que el
número de iteraciones es fija, se debe usar la estructura desde o para.
La estructura Desde ejecuta las acciones del cuerpo del bucle un
número específico de veces y de modo automático controla el número de
iteraciones o pasos a través del cuerpo del bucle.
Pseudocódigo en Español
Pseudocódigo en Inglés
Desde variable(v)= vi Hasta vf hacer For variable
(v)= vi To vf Do
:
Fin_desde
Donde:
v: Variable índice
vi, vf: Valores inicial y final de la variable
Diagrama de Flujo de la estructura Desde/Para
2
LENGUAJE DE PROGRAMACIÓN C
1
2 Historia de C
En 1970 Ken Thompson de los laboratorios Bell se había propuesto
desarrollar un compilador para el lenguaje Fortran que corría en la primera
versión del sistema operativo UNIX tomando como referencia el lenguaje
BCPL; el resultado fue el lenguaje B (orientado a palabras) que resulto
adecuado para la programación de software de sistemas. Este lenguaje tuvo
la desventaja de producir programas relativamente lentos.
En 1971 Dennis Ritchie, con base en el lenguaje B desarrolló NB que luego
cambio su nombre por C; en un principio sirvió para mejorar el sistema UNIX
por lo que se le considera su lenguaje nativo. Su diseño incluye una
sintaxis simplificada, la aritmética de direcciones de memoria (permite al
programador manipular bits, bytes y direcciones de memoria) y el concepto
de apuntador; además, al ser diseñado para mejorar el software de sistemas,
se busco que generase códigos eficientes y uno portabilidad total, es decir
el que pudiese correr en cualquier máquina. Logrados los objetivos
anteriores, C se convirtió en el lenguaje preferido de los programadores
profesionales.
En 1980 Bjarne Stroustrup de los laboratorios Bell de Murray Hill, New
Jersey, inspirado en el lenguaje Simula67 adiciono las características de
la programación orientada a objetos (incluyendo la ventaja de una
biblioteca de funciones orientada a objetos) y lo denomino C con clases.
Para 1983 dicha denominación cambio a la de C++. Con este nuevo enfoque
surge la nueva metodología que aumenta las posibilidades de la programación
bajo nuevos conceptos.
3 Características clave de C
4 Compiladores de C
5 Palabras reservadas de C (ANSI-C)
Las palabras reservadas son identificadores predefinidos que tienen un
significado especial para el compilador C. Un identificador definido por el
usuario, no puede tener el mismo nombre que una palabra reservada.
auto
continue
else
for
long
sizeof
typedef
wile
break
default
num
goto
register
static
union
main
case
do
extern
if
return
struct
unsigned
char
double
float
int
short
switch
void
signed
Algunas versiones de compiladores pueden tener palabras adicionales, asm,
ada, fortran, pascal, etc. Cabe hacer mención que el lenguaje que
analizaremos es el ANSI C, y éste debe de compilarse en cualquier
compilador y cualquier plataforma, que soporte el ANSI C (LINUX, UNIX, MS-
DOS, etc.).
6 Primer programa en C
/* Mi primer programa en C */
#include
int main (int argc, char *argv)
{
printf ("Hola");
return O;
}
La primera línea, entre / * y * /, es un comentario (algo que no forma
parte del código del programa en sí pero que se incluye como aclaración
para facilitar su lectura).
La línea #include le indica al preprocesador de C que incluya
en el programa el contenido del fichero de cabecera stdio.h, donde están
las declaraciones de las funciones estándar de entrada/salida en C.
La línea int main (...) define la cabecera de la función main, el punto
donde comienza la ejecución de un programa en C.
Las llaves { } sirven para delimitar la secuencia de instrucciones que
forman parte de la función main.
La llamada a la función printf ("Hola "); le indica al ordenador que
muestre por pantalla el mensaje que se le indica entre comillas.
La sentencia return indica el valor que devuelve la función
main (por convención, O indica que "todo fue bien").
7 Identificadores
Los identificadores son nombres dados a constantes, variables, tipos,
funciones y etiquetas de un programa.
8 Constantes y Variables
1 Variables
Una variable es un identificador que tiene asociado un valor que puede
cambiar a lo largo de la ejecución del programa
.
1 Reglas para nombres de variables
1.- El nombre puede contener letras, dígitos y el carácter de
subrayado (__).
2.- EL primer carácter del nombre debe ser una letra. El carácter de
subrayado también puede ser un carácter inicial aceptado, pero no
se recomienda su uso.
3.- Las mayúsculas y minúsculas se consideran diferentes.
Ejemplo: SUMA, suma. Son variables diferentes
4.- Los identificadores no pueden coincidir con las palabras
reservadas de C. Ejemplo. For, Void, goto etc.
5.- EL identificador de variable puede ser hasta de 31 caracteres de
largo.
Ejemplos de Variables Validos
Porcentaje /* Valido */.
Y2x5_fg7h /* Valido */
_ordenar /* Valido pero no recomendable*/
Cuenta#basico /* Ilegal */
Doublé /* Ilegal */
3contante /* Ilegal */
mi variable /* Ilegal */
cañada /* Ilegal */
nombre.largo /* Ilegal */
camión /* Ilegal */
2 Tipos de Datos numéricos
"Tipo de Variable "Palabra clave "Bytes Requeridos "Rango "
"Carácter "Char "1 "0 a 255 "
"Números Enteros "Int "2 "-32767 a 32767 "
"Entero corto "Short "1 "-128 a 127 "
"Entero largo "Long "4 "-2,147,483,648 a "
" " " "-2,147,483,647 "
"Números en Punto "float "4 "6 dígitos de "
"flotante " " "precisión "
" " " "3.4X10-38 a "
" " " "3.4X10+38 "
"Punto flotante "double "8 "1.7X10 -308 a "
"doble " " "1.7X10 +308 "
"Doble largo "long double "8 "3.4 X10-4932 a "
" " " "1.1X10+4932 "
3 Declaración de Variables
Antes de ser utilizada una variable en C, esta debe ser declarada. Una
declaración de variable le informa al compilador el nombre y tipo de la
variable y opcionalmente inicia la variable a un valor especifico.
Tipo Nombre de variable ,Nombre de Variable,…:
Ejemplo.
Int contador;
Int numero, suma, inicio;
double porcentaje;
4 Inicialización de Variables.
Una variable puede ser inicializada en el momento de su declaración ej.
Int contador = 19;
2 Constantes
Recordemos que el valor de una constante no puede ser cambiado durante la
ejecución del programa. El C tiene dos tipos de constantes.
1 Constantes Literales.
Una constante literal es un valor que es tecleado directamente en el código
fuente cada vez que se necesita.
IVA = subtotal*.15;
2 Constantes simbólicas
Una constante simbólica es una constante que esta representada por un
nombre (identificador). Cada vez que se necesite el valor de la constante
en el programa se usa su nombre.
3 Declaración de Constantes
Las constante se pueden definir de dos maneras, por medio de la directiva
#define o la palabra clave const. Por conveniencia los nombres de las
constantes se escriben en mayúscula.
1 La directiva #Define
La acción precisa de esta directiva es dar instrucciones al compilador
para que "en el código fuente reemplace a NOMBRE DE CONSTANTE con el
valor". Cuando se declara una constante con esta directiva no lleva punto y
coma al final.
#define NOMBREDECONSTANTE literal
Ejemplo
#define PI 3.1416
#IVA .15
2 Const
La palabra clave const es otra manera de definer una constante, con la
diferencia que esta si reserva espacio en memoria para almacenar su valor.
Su sintaxis es la siguiente.
Const tipo de variable nombre de variable;
Const Int contador =100;
Const Float PI=3.1416;
Const long deuda =120000, float tasa_impuesto = 0.21;
10 Enunciados, expresiones y operadores.
1 Enunciados
Un enunciado es una indicación completa que le da instrucciones a la
computadora , los enunciados son escritos por lo general, uno en cada línea
aunque algunos pueden extenderse a varias líneas. Los enunciados en C
siempre terminan en punto y coma y Los espacios en blanco son ignorados por
el compilador.
Ejemplo X=2+3;
Es igual a
X =
2
+
3;
2 Expresiones
Las expresiones son variables y constantes conectadas por operadores por
ejemplo:
3+5
Var * 3
3 Operadores
Un operador es un símbolo que le da instrucciones al C para que ejecute
alguna operación o acción, en uno o más operandos. Los operadores en C se
agrupan en varias categorías.
1 Operador de asignación
EL operador de asignación es el signo de igual (=). En programación es
usado en forma ligeramente diferente a las matemáticas normales. Si se
escribe
X=y;
En un programa C no significa "x es igual a y". En cambio, significa
"Asigna el valor de y a x". Esto es del lado derecho debe ser una expresión
y del izquierdo una variable.
Variable = expresión;
2 Operadores matemáticos.
1 Operadores Unuarios:
Los operadores unarios toman un solo operando.
"Operador "Símbolo "Acción "Ejemplo "
"Incremento "++ "Incrementa al operando"++x, x++ "
" " "en 1 " "
"Decremento "-- "Decrementa el operando"--x,x-- "
" " "en 1 " "
++X;
--Y;
Son equivalentes a
x=x+1
y=y-1;
Cuando se usan en modo prefijo, los operadores de incremento y decremento
modifican a su operando antes de que sea usado y en modo prefijo modifican
a su operando después de que sea usado.
Ejemplo.
X=10;
Y=x++;
Después de que estos enunciados se ejecutan x tiene el valor de 11 y y
tiene el valor de 10: el valor de x fue asignado a y y luego fue
incrementado. Por el contrario, los enunciados
X=10;
Y=++x;
Da como resultado que tanto y como x tienen el valor de 11: x fue
incrementado y luego fue asignado a y.
2 Operadores Binarios
Operadores Matemáticos
"Operador "Símbolo "Acción "Ejemplo "
"Suma "+ "Suma sus dos operadores "X+y "
"Resta "- "Resta el segundo operando "X-Y "
" " "del primero " "
"Multiplicación "+ "Multiplica sus dos "X*y "
" " "operandos " "
"División "/ "Divide el primer operando "x/y "
" " "entre el segundo " "
"Modulo "% "Da el residuo cuando el "X%y "
" " "primer operando es dividido" "
" " "entre el segundo " "
" " " " "
Precedencia de los operadores matemáticos del C-
"Operadores "Precedencia relativa "
"++-- "1 "
"*/% "2 "
"+ - "3 "
Si una expresión contiene más de un operador con el mismo nivel de
precedencia, los operadores se ejecutan en orden de izquierda a derecha.
Ejemplo 12%5*2
3 Operadores de Relación
"Operador "Símbolo "Pregunta "Ejemplo "
"Igual "== "¿ Es el Primer "X==y "
" " "operando igual " "
" " "que el segundo? " "
"Mayor que "> "¿ Es el operando "X>y "
" " "mayor que el " "
" " "segundo? " "
"Menor que "< "¿ Es el operando "X
" " "menor que el " "
" " "segundo? " "
"Mayor que o igual">= "¿ Es el operando "X>=y "
"a " "mayor que o igual" "
" " "al segundo? " "
"Menor o igual a "<= "¿ Es el operando "X<=y "
" " "Menor que o igual" "
" " "al segundo? " "
"Diferente "!= "¿ Es el operando "X!=y "
" " "mayor que el " "
" " "segundo? " "
4 Operadores lógicos
"Operador "Símbolo "Ejemplo "
"Y "&& "Exp1 && exp2 "
"O """ "Exp1 "" Exp2 "
"No "! "!exp1 "
Precedencia de los operadores lógicos.
"Operador "Precedencia "
"No "! "
"Y "&& "
"O """ "
12 Entrada y Salida de datos.
En la mayoría de los programas se necesita desplegar información en la
pantalla o leer información del teclado., en el lenguaje C las funciones
mas comunes de salida es Printf y Puts() y de entrada es Scanf() y gets( ),
1 Función puts()
La función puts() se usa para desplegar mensajes de texto y añade
automáticamente una línea nueva al final; Esta función no puede desplegar
variables numéricas. Ejemplo.
Puts("Hola");
2 Función Printf()
En la función printf( ) solo se necesita pasarle el mensaje entre comillas
dobles ej.
Printf( "Mensaje de Prueba");
Si deseamos desplegar el valor de una variable junto al texto, la función
se utilizara de la siguiente manera Ej.
Printf( " EL valor de x es %d", x);
El formato debe contener un especificador de conversión que en el ejemplo
es %d, ya que el valor a imprimir es un valor int o long.
La función puede imprimir más de una variable ejemplo.
Printf("Tasa= %f, cantidad = %d", tasa, cantidad);
Los especificadores de conversión mas comúnmente usados son:
"Especificador "Significado "
"%c "Un solo carácter "
"%d "Entero decimal "
"%f "Float, double "
"%s "Cadena de caracteres "
"%u "Entero decimal sin signo "
3 Función Scanf()
La función scanf( ) lee datos numéricos del teclado de acuerdo con un
formato especificado, y asigna los datos de entrada a una o mas variables
del programa. El formato utiliza los mismo especificadores de conversión
que la función printf(). Ejemplo
Scanf("d%", x);
Lee un entero decimal del teclado y lo asigna a la variable entera X.
4 Función Gets()
La función gets() lee todos los caracteres del teclado hasta que se
encuentra el primer carácter de nueva línea (Enter).
Char cadena(50);
Puts("Escribe una cadena");
Gets(cadena);
Printf("la cadena que escribiste es: %S, cadena);
13 Estructuras de Control
Los enunciados en un programa en C normalmente se ejecutan de arriba hacia
abajo, las estructuras de control como son el IF, Switch, for y do_While,
modifican la ruta que seguirá el programa.
1 Estructuras condicionales
1 El Enunciado IF
En su forma básica, el enunciado If evalua una expresión y nos permite
elegir si se ejecuta o no una instrucción o un bloque de instrucciones.
Cuando solo es una sentencia la que se ejecuta después de una evaluación la
forma es la siguiente:
If( expresión)
Sentencia;
Cuando es un grupo de dos o más sentencias se encierran entre llaves de la
siguiente manera.
If (expresión){
Sentencia1;
Sentencia2;
Sentencia3;
}
Nota: La sentencia IF no debe llevar punto y coma.
Ejemplo: Diseña un programa que lea un numero, si es positivo que lo
multiplique por 2 y lo muestre.
#include
#include
#include
Int num;
Main()
{
Prinft("Escribe un numero");
Scanf("%d", &num);
IF(num>0)
{
Num=num*2;
Printf ("El numero es %d", num);
}
1 El enunciado IF….Then…..Else
Un enunciado if puede incluir una clausula else de la siguiente manera
If (expresión)
Sentencia1;
Else
Sentencia2;
Si la expresión es cierta, se ejecuta la sentencia1. Si es falsa, se
ejecuta la sentencia 2.
If(condicion) {
Bloque1
} Else {
Bloque2
}
Ejemplo:
IF anidados.
If(expresión)
Sentencia1;
Else if (expresión)
Sentencia 2;
Else
Sentencia3;
Ejemplo: Diseña un programa en C que lea la edad de una persona, si la
edad es menor que 18, entonces que imprima menor de edad, de lo contrario
si es menor que 65 entonces que imprima "Adulto", si pasa los 65 entonces
que imprima Anciano.
If(edad<18)
Printf("Menor de edad");
Else if(edad<65)
Printf("Adulto");
Else
Printf("Anciano");
1 EL enunciado Switch
Permite seleccionar entre varias alternativas posibles, su sintaxis es la
siguiente:
switch (expresión) {
case expr_cte1:
sentencia1;
case expr_cte2:
sentencia2;
case expr_cte3:
sentencia3;
default:
sentencia;
}
Se selecciona a partir de la evaluación de una única expresión.
La expresión del switch debe ser de tipo entero.
Los valores de cada caso del switch han de ser constantes.
La etiqueta default marca el bloque de código que se ejecuta
por defecto (cuando al evaluar la expresión se obtiene un valor
no especificado por los casos del switch).
En C, se ejecutan todas las sentencias incluidas a partir del
caso correspondiente, salvo que explícitamente usemos break:
void main()
{
int nota;
printf("Calificación: ");
scanf("%d", ¬a);
switch (nota) {
case 0:
case 1:
case 2:
case 3:
case 4:
printf("Suspenso");
break;
case 5:
case 6:
printf("Aprobado");
break;
case 7:
case 8:
printf("Notable");
break;
case 9:
printf("Sobresaliente");
break;
case 10:
printf("Matrícula");
break;
default:
printf("Error");
}
}
2 Estructuras ciclicas
16
1 El enunciado For
EL enunciado for, permite ejecutar una instrucción o un bloque de
instrucciones durante una cantidad de veces; En esta sentencia se debe
conocer el número de veces
EL enunciado For tiene la siguiente estructura:
For(inicial; condición; incremento){
Enunciado
}
Ejemplo :
for (i=1; i<=n; i++) {
factorial *= i;
Realiza un programa que C que lea 20 calificaciones e imprima su promedio.
WHILE
EL enunciado WHILE, permite ejecutar una instrucción o un bloque de
instrucciones mientras se cumple una condicion;
En el cuerpo del bucle debe existir algo que haga variar el valor
asociado a la condición que gobierna la ejecución del bucle.
While (condición){
Bloque
}
Programa en C que permite introducir números mientras sean positivos y al
terminar calcula e imprime el promedio
Main ()
{
Int num, suma, resp;
Float promedio;
Printf ("Desea introducir un valor 1.-Si 2.-No")
Scanf( resp;
While (resp=1)
Printf ("Escribe un numero");
Scanf("%d",&num);
Suma=suma+num;
Printf ("Desea introducir un valor 1.-Si 2.-No")
Scanf( resp;
}
17 Funciones
Una función es una sección de código de C que tiene un nombre
independiente, que ejecuta una tarea específica y opcionalmente regresa un
valor al programa que la llama.
1 Prototipo de Función
Proporciona al compilador la descripción de una función que será definida
mas adelante en el programa. Un prototipo de función contiene el nombre de
la función, una lista de variables que se les debe pasar, el tipo de
variable que regresa, en caso de haberla y el punto y coma al final
ejemplo
Float suma (float, float);
2 Definición de Función
La definición contiene el código que será ejecutado. La primera línea de la
definición de función, llamada el encabezado de la función, debe ser
idéntica al prototipo de la función, a excepción del punto y coma. A
continuación del encabezado se encuentra el cuerpo de la función. El cuerpo
debe comenzar con una llave izquierda y terminar con una llave derecha. Si
el tipo de retorno de la función es otro diferente a void, se debe incluir
un enunciado return que regrese un valor.
Float suma (float a, float b)
{
Float resultado;
resultado=a+b;
return(resultado);
}
3 Estructura de una función
Type nombre_de_funcion (param1,param2,….)
Type= Tipo de retorno
Nombre de función= Nombre de la función
param1, param2= parámetros
4 Llamado de funciones
Cualquier función puede ser llamada simplemente con su nombre y lista de
argumentos en un enunciado.
resultado=suma(x,y);
5 Paso de Argumentos
Para pasar argumentos de una función se enlista entre paréntesis a
continuación del nombre. LA cantidad de argumentos y el tipo de cada uno de
ellos deben coincidir con los parámetros del encabezado y prototipo de
función
#include
Float suma (float, float);
Void()
{
Float x,y,resultado ;
Printf ("Escribe el primer valor");
Scanf("%f",&x);
Printf ("Escribe el segundo valor");
Scanf("%f",&y);
resultado=suma(x,y);
printf("El resultado de la suma es %f", resultado);
}
Float suma (float a, float b)
{
Float resultado;
resultado=a+b;
return(resultado);
}
Escriba un encabezado de función llamada hazlo( ), que tome tres argumentos
de tipo char y regrese un tipo float al programa que la llama.
Escriba una función que reciba dos números como argumentos y regrese el
valor de su producto
Escriba una función que reciba dos números como argumentos, La función debe
dividir el primer número entre el segundo. No divida cuando el segundo
número es cero.
Escribe un programa que use una función para encontrar el promedio de cinco
valores tipo float tecleados por el usuario.
18 Arreglos
Un arreglo es una colección ordenada de elementos de un mismo tipo.
Ordenada significa que cada elemento tiene una ubicación determinada dentro
del arreglo y debemos conocerla para accederlo.
Un arreglo puede tener una o varias dimensiones. A los arreglos
unidimensionales se les conocen como vectores y a los bidimensionales como
matrices.
1 Arreglos Unidimensionales (Vectores)
Un vector es un arreglo de una sola dimensión, para indicar a qué elemento
nos referimos, se necesita especificar un sólo número o índice, que
corresponde a la posición relativa de dicho elemento dentro del arreglo.
Definición de un arreglo:
nombre_variable[longitud];
"["tipo puede ser cualquier tipo de dato (int, float, char, etc.). "
"p" "
"i" "
"c" "
"]" "
"["nombre_de_variable es el nombre del arreglo. "
"p" "
"i" "
"c" "
"]" "
"["Longitud: corresponde al número de posiciones del arreglo "
"p" "
"i" "
"c" "
"]" "
1 Inicializar un arreglo
Si se desea la inicializar arreglos, es decir asignar valores al arreglo en
el momento de ser declarados debe hacerse con el siguiente formato:
tipo nombre_arr[ tam1= {valor1,valor2….valor n};
Ejemplo:
int i[10] = {1,2,3,4,5,6,7,8,9,10};
"Posicione"0"1"2"3"4"5"6 "
"s " " " " " " " "
"Contenido"F"u"l"a"n"o"\0"
2 Asignación de un arreglo:
nombre_variable[índice] = valor
Esta instrucción asigna el valor a la posición índice del arreglo. El
índice debe ser una expresión del tipo entero en el rango [0, longitud-1].
Acceso al contenido de un arreglo:
nombre_variable[índice] es valor del tipo que puede ser
asignado a una variable, o pasado como parámetro, imprimirlo, etc. Aquí
también vale la aclaración de que el índice debe estar dentro del rango de
definición del arreglo, C++ no chequeará que esto sea cierto y devolverá lo
contenido en la posición de memoria correspondiente a un arreglo de mayor
longitud, el dato obtenido de esta manera es basura.
EJEMPLO:
El siguiente programa realiza la lectura de 10 datos y calcula la suma de
los mismos:
#include
main()
{
int i;
int sum;
int x[10];
sum=0;
for (i=0; i<10; i++)
{
printf ("INTRODUCE EL DATO % d ", i);
scanf (%d", &x[i]);
sum = sum + x[i];
}
printf ("EL VECTOR ORIGINAL ES :");
for (i = 0; i<10; i ++]);
printf ("%d ", x[i]);
printf ("/n LA SUMA DEL VECTOR ES : % d", sum);
}
2 Arreglos Bidimensionales (MATRICES)
Una matriz es un arreglo de dos dimensiones, y para especificar cualquier
elemento, debemos hacer referencia a dos índices (que representan la
posición como renglón y columna). Aunque no se justificará aquí, es
conveniente mencionar que la representación matricial es puramente
conceptual y con el único fin de facilitar al programador el manejo de los
elementos, ya que la computadora almacena los datos en una forma totalmente
diferente.
1 Definición
tipo nombre_de_variable [rango1][rango2];
donde:
"["tipo puede ser cualquier tipo de dato (int, float, char, etc.). "
"p" "
"i" "
"c" "
"]" "
"["nombre_de_variable es el nombre del arreglo. "
"p" "
"i" "
"c" "
"]" "
"["rango 1 corresponde al número de renglones que conforman el "
"p"arreglo. "
"i" "
"c" "
"]" "
"["rango 2 corresponde al número de columnas. "
"p" "
"i" "
"c" "
"]" "
El siguiente programa realiza la lectura de una matriz de 7 renglones y 15
columnas (de una matriz de orden [7x15]) renglón por renglón).
#include
#define ren 7
#define col 15
main ()
{
int i, j;
int x [ren][col];
for (i=0; i
for(j=0; j
{
printf ("INTRODUCE EL ELEMENTO A (%d, %d) ", I, j);
scanf ("%d", &x [i] [j]);
}
}
Ejercicios:
1. Hacer un programa que lea diez valores enteros en un arreglo desde el
teclado y calcule y muestre: la suma, el valor medio, el mayor y el menor.
2. Hacer un programa que lea diez valores enteros en un arreglo y los
muestre en pantalla. Después que los ordene de menor a mayor y los vuelva a
mostrar. Y finalmente que los ordene de mayor a menor y los muestre por
tercera vez. Para ordenar la lista usar el método de la burbuja.
3. Hacer un programa que lea 25 valores enteros en una tabla de 5 por 5, y
que después muestre la tabla y las sumas de cada fila y de cada columna.
Procura que la salida sea clara.
4. Escribir un programa que lea un arreglo de cualquier tipo (entero,
flotante, char), y revise el arreglo para encontrar un valor en particular
digitado por el usuario, la salida es la posición del arreglo en que se
encuentra.
5. Leer un texto, un carácter a la vez desde la entrada estándar (que es el
teclado), e imprimirlo en forma invertida. Leer hasta que se encuentre un
final-de-datos (teclear CONTROL-Z para generarlo).
6. Escribir un programa para leer un texto hasta el fin-de-datos, y mostrar
información del texto , es decir número de palabras, longitud de las
palabras, etc.
19 Registros
1 Conceptos Generales
1 Concepto de Campo
En informática un campo es un espacio de almacenamiento para un dato
particular. En las bases de datos, un campo es la mínima unidad de
información a la que se puede acceder. En las hojas de cálculo los campos
son llamados celdas. La mayoría de los campos tienen atributos asociados a
ellos. Por ejemplo, algunos campos son numéricos mientras otros almacenan
texto, también varía el tamaño de estos. Adicionalmente, cada campo tiene
un nombre.
2 Concepto de registro
En informática, y concretamente en el contexto de una base de datos
relacional, un registro (también llamado fila o tupla) representa un ítem
único de datos implícitamente estructurados en una tabla. En términos
simples, una tabla de una base de datos puede imaginarse formada de filas y
columnas o campos. Cada fila de una tabla representa un conjunto de datos
relacionados, y todas las filas de la misma tabla tienen la misma
estructura.
La estructura implícita de un registro y el significado de los valores
de sus campos exige que dicho registro sea entendido como una sucesión de
datos, uno en cada columna de la tabla. La fila se interpreta entonces como
una variable relacional compuesta por un conjunto de tuplas, cada una de
las cuales consta de dos ítems: el nombre de la columna relevante y el
valor que esta fila provee para dicha columna.
REGISTRO: Es una estructura de datos homogéneos o no a los cuales se les
llama campos tales que se accede a ellos mediante su propio nombre. Tienen
un número ilimitado de elementos y no tienen porque guardar un orden
natural en memoria.
2 Estructuras
Una ESTRUCTURA es una colección de datos elementales (básicos) de
diferentes tipos, lógicamente relacionados. A una estructura se le da
también el nombre de REGISTRO. Crear una estructura es definir un nuevo
tipo de datos. En la definición de la estructura se especifican los
elementos que la componen así como sus tipos. Cada elemento de una
estructura se denomina miembro (CAMPO). Declaración de una estructura:
struct nombre_estructura
{
tipo nombre_variable;
tipo nombre_variable;
tipo nombre_variable;
...
} variables_estructura;
Ejemplo:
struct Registro {
int a;
float b;
unsigned int c;
unsigned int d;
};
Esto permite agrupar un conjunto de variables en una única estructura. Para
crear una variable de este tipo, bastará con hacer referencia a esta
estructura. Las variables o campos de esta estructura se referencian
utilizando un punto.
MANIPULACION
MANIPULACION DE CAMPOS
MANIPULACION DE REGISTROS
Por ejemplo, podría utilizarse la estructura anterior en la siguiente
forma:
void main (void)
{
struct Registro Reg;
Reg.a = -10;
Reg.b = 20.3;
Reg.c = 15;
Reg.d = Registro.c * 2;
}
Es posible cargar una estructura con un valor inicial. Por ejemplo, en el
caso anterior podría escribirse:
void main (void)
{
struct Registro Reg = {-10, 20.3, 15, 15*2};
struct Registro Reg2;
Reg2 = Reg; /* Es posible copiar registros como cualquier otra
variable */
}
No existe ninguna función que imprima el contenido de un registro. Para
ello debe definirse una función que lo haga.
3 Enumeraciones
El tipo de dato enumeración consiste en un conjunto de constantes
numéricas, llamadas enumeradores ('enumerators' en inglés). Dichas
constantes pueden tomar cualquier valor arbitrario (entero). Si no se lo
especifica, se entiende cero para el primer valor, y uno más que el
anterior para los restantes. Por ejemplo:
enum DiasSemana
{
DOMINGO,
LUNES,
MARTES,
MIERCOLES,
JUEVES,
VIERNES,
SABADO
};
Para definir una variable de este tipo deberá utilizarse:
enum DiasSemana Dia;
/* En C++ está permitido omitir la palabra enum al
definir una variable, en C no */
Dia = LUNES;
En este caso, DOMINGO tendrá el valor 0, LUNES el 1 y así sucesivamente.
Es posible asignar un valor específico a cada elemento, incluso valores
repetidos:
enum DiasSemana
{
DOMINGO = 10,
LUNES, /* Se utilizará el valor del elemento de arriba + 1 */
MARTES,
MIERCOLES = 15,
JUEVES,
VIERNES,
SABADO = 10 /* Repito un valor ya utilizado */
};
En este caso, tanto SABADO como DOMINGO tendrán el valor 10, LUNES tendrá
el 11, MARTES el 12, MIERCOLES el 15, JUEVES el 16 y VIERNES 17.
Si bien una variable de tipo enum almacenará un entero no está permitido
asignarle directamente valores de este tipo. Por ejemplo, el código:
Dia = 15;
sería incorrecto, aún cuando MIERCOLES tenga el valor 15.
Sí es posible realizar esta operación utilizando algo llamado cast, que se
verá más adelante, en este mismo capítulo. Por ahora simplemente diremos
que es posible realizar esta operación de la siguiente forma.
Dia = (enum DiasSemana) 15;
También es sintácticamente posible asignar un valor inválido, como en el
siguiente ejemplo:
Dia = (enum DiasSemana) 4257;
pero en este caso los resultados no están definidos.
4 Uniones
Una unión es una agrupación de variables bajo una única denominación, pero
en la cual todas ellas comparten una misma zona de memoria. Según como se
la acceda, dicha zona de memoria será utilizada para almacenar un tipo de
dato diferente. Veamos un ejemplo:
Ejemplo : Uniones
union MiUnion{
char c;
int n;
float f;
double d;
};
void main(void)
{
MiUnion u;
u.d = 10.0 / 3.0;
printf ("u como double = %f\n", u.d);
printf ("u como float = %f\n", u.f);
printf ("u como int = %d\n", u.n);
printf ("u como char = %d\n", u.c);
}
Este ejemplo es correcto. Sin embargo no debe leerse nunca una variable de
una forma diferente a como fue escrita, por ejemplo, si u se escribe como
int no debe leerse nunca como double. Las uniones son utilizadas
principalmente para almacenar datos particulares de un tipo de dato más
general. En C++ este problema se resuelve de una forma diferente, con el
concepto de herencia que se verá más adelante, razón por la cual las
uniones son rara vez utilizadas.
Para terminar con el tema, veamos un par de ejemplos, el los que se
utilicen estructuras, enumeraciones y uniones:
Ejemplo : Estructuras complejas (1)
/* estructura para almacenar información sobre una figura,
a dibujar en pantalla */
enum TipoFigura {CUADRADO, RECTANGULO,CIRCULO};
struct TPosicion
{
double x0, y0;
};
struct TFigura{
enum TipoFigura;
unsigned Color;
TPosicion Pos;
union Datos
{
double Lado;
struct Lados
{
double Ancho;
double Alto;
}
double Radio;
};
};
Ejemplo : Estructuras complejas (2)
enum TipoMisil {TIERRATIERRA, TIERRAAIRE, AIRETIERRA, AIREAIRE};
enum TipoDureza {DEBIL, MEDIA, FUERTE}
/* Estructura para almacenar los datos de un misil */
struct TMisil
{
double x,y,z; /* Posición del misil */
double vx, vy, vz; /* Velocidad */
enum TipoMisil;
union Blanco
{
struct Avion
{
double x,y,z;
double vx,vy,vz;
};
struct Edificio
{
double x,y;
TipoDureza Dureza;
}
};
};
Esta estructura permite almacenar la información de un misil. En este caso,
el blanco u objetivo del misil nunca será un avión y un edificio al mismo
tiempo. Por ello se utiliza una unión para almacenar los datos del
objetivo. Ambas estructuras utilizarán la misma área de memoria, y el
programador deberá saber como tratarla.
5 Definición de nuevos nombres para tipos de datos
Pueden definirse tipos de datos o nuevos nombres para los ya existentes
usando la palabra reservada typedef. Esta se utiliza de la siguiente forma:
typedef definición_del_tipo nuevo_nombre_del_tipo;
Por ejemplo, si se quisiese llamar byte al tipo de dato unsigned char,
debería escribir:
typedef unsigned char byte;
También podría utilizar typedef para abreviar el nombre de una estructura.
Por ejemplo, en ejemplo de la sección anterior, donde se definía el tipo de
dato struct Registro, podría abreviar estas dos palabras en una sóla
escribiendo:
struct Registro {
int a;
float b;
unsigned int c;
unsigned int d;
};
typedef struct Registro TRegistro;
Estas líneas pueden escribirse en forma más resumida como sigue:
typedef struct Registro {
int a;
float b;
unsigned int c;
unsigned int d;
} TRegistro;
con lo cual, para crear este tipo de dato podría escribir:
TRegistro Reg;
6 Convirtiendo tipos de datos (cast)
En C está permitido realizar conversiones de un tipo de dato a otro, sin
mayor inconveniente. Por ejemplo, podría escribirse:
Ejemplo : Conversión de tipos (cast) (1)
void main(void)
{
int a;
unsigned int b;
a = 10;
b = a;
}
¿Qué valor tomará b? La respuesta es simple, 10. ¿Pero que ocurriría en el
caso siguiente?:
void main (void)
{
int a;
unsigned int b;
a = -1;
b = a;
}
Para responder esta pregunta es necesario analizar la representación de -1
en binario. Un valor entero -1 tiene la siguiente representación binaria,
en un sistema de 16 bits: 11111111 11111111. Si tratamos de leer este valor
en decimal, considerándolo un número positivo obtendremos en valor 65535,
es decir que b recibirá este valor.
Para evitar problemas de este tipo, el compilador suele advertir con un
warning cuando se realizan conversiones de datos no especificadas.
Pueden realizarse también operaciones entre números enteros y de punto
flotante. En este caso el compilador se encarga de la conversión. Por
ejemplo:
{
float a;
int b;
a = 20 / 3;
b = a;
}
En este caso a recibirá en valor 6.66666, y b este valor, pero con la parte
decimal truncada a 0, es decir, 6.
La mayoría de los compiladores emiten mensajes de advertencia cuando se
realizan operaciones entre datos de distinto tipo, y lo correcto es no
hacer esto. Para realizar operaciones entre datos de distinto tipo, debe
realizarse una conversión de tipo. Esto se hace encerrando entre paréntesis
el tipo de dato al que se quiere convertir. Esto se conoce con el nombre de
cast, y la operación de realizar esta conversión con el nombre de casting.
Por ejemplo, en los casos anteriores se tendría:
{
int a;
unsigned int b;
float c;
a = -1;
b = (unsigned int) a;
c = (float) a;
}
Ejemplo : Casting
typedef unsigned char byte;
byte prom (byte a, byte b, byte c)
{
byte result;
result = (byte)(((unsigned)a + (unsigned)b + (unsigned)c) / 3);
return retult;
}
La conversión de tipos puede realizarse aún sin que se cambie el tipo de
variable. Supongamos que se requiera calcular el promedio de 3 números
enteros de 8 bits, sin signo, llamados a, b y c.
Desde ya que la función promedio retornará un entero de 8 bits, ya que el
resultado estará acotado a los valores recibidos. Vimos anteriormente que
este cálculo no lo podemos realizar en la forma: a/3+b/3+c/3, ya que al
realizarse la división se trunca la parte decimal. Por ejemplo, el promedio
de 2, 2, 2 sería 0.
La segunda alternativa consiste en realizar la suma primero, y luego la
división (a+b+c)/3.
¿Pero qué ocurriría si la suma superase el número 255, que es el máximo
entero representable con 8 bits?
Rta: El resultado de la suma se trucaría quedándose únicamente con los 8
bits menos significativos, y el resultado sería incorrecto.
¿Cómo solucionar este problema? La respuesta consiste en realizar este
cálculo con números de mayor precisión. Veamos el ejemplo:
typedef unsigned char byte;
byte prom (byte a, byte b, byte c)
{
byte result;
result = (byte) (((unsigned)a + (unsigned)b + (unsigned)c) / 3);
return retult;
}
Notar la forma en que se realizó la operación. En primer lugar se convirtió
cada una de las tres variables a un entero de mayor precisión, luego se
efectuó la suma de las tres y la división por 3. Finalmente se convirtió el
resultado en un entero de 8 bits y se lo copió en la variable result.
7 Ejercicios
1 - Defina un tipo de dato 'complejo', que almacene dos enteros llamados
Real e Imag.
2 - Defina dos funciones que tomen un par de datos de tipo complejo y
devuelvan su suma y diferencia, respectivamente.
3 - Escriba una función que devuelva la parte real de un número complejo,
convertida a punto flotante.
20 Archivos
Muy a menudo necesitamos almacenar cierta cantidad de datos de forma
más o menos permanente. La memoria del ordenador es volátil, y lo que es
peor, escaso y caro. De modo que cuando tenemos que guardar nuestros datos
durante cierto tiempo tenemos que recurrir a sistemas de almacenamiento más
económicos, aunque sea a costa de que sean más lentos.
Durante la historia de los ordenadores se han usado varios métodos
distintos para el almacenamiento de datos. Al principio se recurrió a
cintas de papel perforadas, después a tarjetas perforadas. A continuación
se pasó al soporte magnético, empezando por grandes rollos de cintas
magnéticas abiertas.
Hasta aquí, todos los sistemas de almacenamiento externo eran
secuenciales, es decir, no permitían acceder al punto exacto donde se
guardaba la información sin antes haber partido desde el principio y sin
haber leído toda la información, hasta el punto donde se encontraba la que
estábamos buscando.
Con las cintas magnéticas empezó lo que con el tiempo sería el acceso
aleatorio a los datos. Se podía reservar parte de la cinta para guardar
cierta información sobre la situación de los datos, y añadir ciertas marcas
que hicieran más sencillo localizarla.
Pero no fue hasta la aparición de los discos magnéticos cuando ésta
técnica llegó a su sentido más amplio. En los discos es más sencillo
acceder a cualquier punto de la superficie en poco tiempo, ya que se accede
al punto de lectura y escritura usando dos coordenadas físicas. Por una
parte la cabeza de lectura/escritura se puede mover en el sentido del radio
del disco, y por otra el disco gira permanentemente, con lo que cualquier
punto del disco pasa por la cabeza en un tiempo relativamente corto. Esto
no pasa con las cintas, donde sólo hay una coordenada física.
Con la invención y proliferación de los discos se desarrollaron los
ficheros de acceso aleatorio, que permiten acceder a cualquier dato
almacenado en un fichero en relativamente poco tiempo.
Actualmente, los discos duros tienen una enorme capacidad y son muy
rápidos, aunque aún siguen siendo lentos, en comparación con las memorias
RAM. El caso de los CD es algo intermedio. En realidad son secuenciales en
cuanto al modo de guardar los datos, cada disco sólo tiene una pista de
datos grabada en espiral. Sin embargo, este sistema, combinado con algo de
memoria RAM, proporciona un acceso muy próximo al de los discos duros.
1 Clasificación de Archivos
En cuanto al tipo de acceso, en C y C++ podemos clasificar los archivos
según varias categorías:
Dependiendo de la dirección del flujo de datos:
De entrada: los datos se leen por el programa desde el archivo.
De salida: los datos se escriben por el programa hacia el archivo.
De entrada/salida: los datos pueden se escritos o leídos.
Dependiendo del tipo de valores permitidos a cada byte:
De texto: sólo están permitidos ciertos rangos de valores para cada
byte. Algunos bytes tienen un significado especial, por ejemplo, el
valor hexadecimal 0x1A marca el fin de fichero. Si abrimos un archivo
en modo texto, no será posible leer más allá de un byte con ese valor,
aunque el fichero sea más largo.
Binarios: están permitidos todos lo valores para cada byte. En estos
archivos el final del fichero se detecta de otro modo, dependiendo del
soporte y del sistema operativo. La mayoría de las veces se hace
guardando la longitud del fichero. Cuando queramos almacenar valores
enteros, o en coma flotante, o imágenes, etc, deberemos usar este tipo
de archivos.
Según el tipo de acceso:
Archivos secuenciales: imitan el modo de acceso de los antiguos
ficheros secuenciales almacenados en cintas magnéticas y
Archivos de acceso aleatorio: permiten acceder a cualquier punto de
ellos para realizar lecturas y/o escrituras.
Según la longitud de registro:
Longitud variable: en realidad, en este tipo de archivos no tiene
sentido hablar de longitud de registro, podemos considerar cada byte
como un registro. También puede suceder que nuestra aplicación conozca
el tipo y longitud de cada dato almacenado en el archivo, y lea o
escriba los bytes necesarios en cada ocasión. Otro caso es cuando se
usa una marca para el final de registro, por ejemplo, en ficheros de
texto se usa el carácter de retorno de línea para eso. En estos casos
cada registro es de longitud diferente.
Longitud constante: en estos archivos los datos se almacenan en forma
de registro de tamaño constante. En C usaremos estructuras para
definir los registros. C dispone de funciones de librería adecuadas
para manejar este tipo de ficheros.
Mixtos: en ocasiones pueden crearse archivos que combinen los dos
tipos de registros, por ejemplo, dBASE usa registros de longitud
constante, pero añade un registro especial de cabecera al principio
para definir, entre otras cosas, el tamaño y el tipo de los registros.
Es posible crear archivos combinando cada una de estas categorías, por
ejemplo: archivos secuenciales de texto de longitud de registro variable,
que son los típicos archivos de texto. Archivos de acceso aleatorio
binarios de longitud de registro constante, normalmente usados en bases de
datos. Y también cualquier combinación menos corriente, como archivos
secuenciales binarios de longitud de registro constante, etc.
"Modo de apertura de un archivo "
"r " Archivo para sólo lectura. El fichero debe existir. "
"w " Se abre para escritura, se crea un fichero nuevo o se "
" "sobrescribe si ya existe. "
"a " Añadir, se abre para escritura, el cursor se sitúa al "
" "final del fichero. Si el fichero no existe, se crea. "
"rb "Abre un archivo binario para sólo lectura "
"wb "Crea un archivo binario para escritura "
"ab "Añade información a un archivo binario "
"r+ " Lectura y escritura. El fichero debe existir. "
"w+ " Lectura y escritura, se crea un fichero nuevo o se "
" "sobrescribe si ya existe. "
"a+ "Añadir, lectura y escritura, el cursor se sitúa al final"
" "del fichero. Si el fichero no existe, se crea. "
"rb+ "Abre un archivo binario para operaciones de "
" "lectura/escritura "
"wb+ "Crea archivo binario para operaciones de "
" "lectura/escritura "
"ab+ "Añade información a un archivo binario para operaciones"
" "de lectura/escritura "
Cuando un archivo es abierto, se espera que exista, de no ser así se
intentara crearlo. En el modo w cualquier información previa en el archivo
es borrada. Como se puede apreciar, si no se especifica ninguna otra cosa,
el archivo abierto es de tipo texto.
La función que realiza la apertura de un archivo es fopen() y tiene
la siguiente función prototipo:
FILE *fopen(const char *nombre_archivo, const char *modo)
Donde:
"FILE "Es un tipo especificado en el archivo de cabecera "
" "stdio.h. Cuando la operación es exitosa se entrega un"
" "puntero al archivo, cuando la operación falla se "
" "entrega un NULL "
"nombre_archivo"Es el nombre del archivo que se debe abrir "
"modo "Es alguno de los modos descritos anteriormente. "
Ejemplo:
Para abrir un nuevo archivo llamado alumnos en el cual sólo se
realizara escritura de texto, se deben escribir las siguientes sentencias:
FILE *pfArch;
pfArch = fopen("alumnos", "w");
Para realizar una apertura en la cual se desee leer el contenido se
tendrá:
pfArch = fopen("alumnos", "r");
Y para continuar añadiendo información se tendrá:
pfArch = fopen("alumnos", "r");
Si la apertura del archivo no es exitosa por cualquier razón, la
función fopen() entrega un valor NULL. Una operación de apertura por
escritura puede fallar, por ejemplo si el medio está protegido para
escritura, si el archivo tiene la propiedad de solo lectura o porque el
medio esta dañado. Una operación de apertura para lectura puede fallar
porque el archivo no existe, o la ruta especificada es errónea.
Cuando un archivo ya no sea necesitado por un programa, es
recomendable que sea cerrado, es decir, se elimine la conexión entre el
programa y el archivo. La función fclose() cumple con dicha tarea. Su
función prototipo es la siguiente:
int fclose(FILE *puntero_archivo);
Donde:
"puntero_archiv"Es el puntero al archivo que se desea cerrar. "
"o " "
Por ejemplo, para cerrar el archivo del ejemplo anterior basta con
escribir:
fclose(pfArch);
2 Creación de un archivo de datos.
Existen diversas funciones que nos permiten escribir o leer un archivo
que ha sido abierto con anterioridad. Las funciones putc() y fputc() son
equivalentes, con ellas se puede escribir un carácter en un archivo. Su
función prototipo es:
Int fputc(int carácter, FILE *puntero_archivo)
Ejemplo de programa utilizando fopen, fputc, fclose.
/* Archivo: Leecadar.c
Uso de archivos */
#include
#include
#include
#include
#ifndef SALIDA
#define SALIDA '@'
#endif
void main(void)
{
FILE *pfArchivo;
char szLinea[80];
char szArchivo[12];
register int iContador;
/* Pide el nombre del szArchivo a crear */
printf("\n Digite el nombre del szArchivo: ");
gets(szArchivo);
if (strlen(szArchivo) == 0)
{
printf("\n Se omitio nombre del Archivo");
}
else
{
/* Creacion del szArchivo */
pfArchivo=fopen(szArchivo,"w");
if ( pfArchivo != NULL )
{
/* Si la creacion fue exitosa captura una szLinea de texto a la
vez */
/*clrscr();*/
szLinea[0]='\0';
printf("\n @ Termina captura\n Capturando...\n");
/* Mientras no se de caracter de salida */
while (szLinea[0] != SALIDA)
{
gets(szLinea);
iContador=0;
while (szLinea[iContador] != '\0'&& szLinea[0] != SALIDA)
{
/* Guarda un caracter a la vez */
fputc(szLinea[iContador],pfArchivo);
iContador++;
}
fputc('\n',pfArchivo);
}
/* Cierra el szArchivo */
fclose(pfArchivo);
}
else
{
printf("\n No fue posible realizar la apertura");
}
}
}
4 Archivos de Acceso Aleatorio
Los archivos de acceso aleatorio son más versátiles, permiten acceder
a cualquier parte del fichero en cualquier momento, como si fueran arrays
en memoria. Las operaciones de lectura y/o escritura pueden hacerse en
cualquier punto del archivo.
En general se suelen establecer ciertas normas para la creación, aunque
no todas son obligatorias:
1. Abrir el archivo en un modo que te permita leer y escribir. Esto no es
imprescindible, es posible usar archivos de acceso aleatorio sólo de
lectura o de escritura.
2. Abrirlo en modo binario, ya que algunos o todos los campos de la
estructura pueden no ser caracteres.
3. Usar funciones como fread y fwrite, que permiten leer y escribir
registros de longitud constante desde y hacia un fichero.
4. Usar la función fseek para situar el puntero de lectura/escritura en
el lugar apropiado de tu archivo.
Por ejemplo, supongamos que nuestros registros tienen la siguiente
estructura:
struct stRegistro {
char Nombre[34];
int dato;
int matriz[23];
}reg;
Teniendo en cuenta que los registros empiezan a contarse desde el cero,
para hacer una lectura del registro número 6 usaremos:
fseek(fichero, 5*sizeof(stRegistro), SEEK_SET);
fread(®, sizeof(stRegistro), 1, fichero);
Análogamente, para hacer una operación de escritura, usaremos:
fseek(fichero, 5*sizeof(stRegistro), SEEK_SET);
fwrite(®, sizeof(stRegistro), 1, fichero);
" " "
" "Después de cada operación de lectura o escritura, el "
" "cursor del fichero se actualiza automáticamente a la "
" "siguiente posición, así que es buena idea hacer siempre"
" "un fseek antes de un fread o un fwrite. "
"Información" "
6 Calcular la longitud de un fichero
Para calcular el tamaño de un fichero, ya sea en bytes o en registros
se suele usar el siguiente procedimiento:
long nRegistros;
long nBytes;
fseek(fichero, 0, SEEK_END); // Colocar el cursor al final del fichero
nBytes = ftell(fichero); // Tamaño en bytes
nRegistros = ftell(fich)/sizeof(stRegistro); // Tamaño en registros
7 Borrar registros
Borrar registros puede ser complicado, ya que no hay ninguna función
de librería estándar que lo haga.
Es su lugar se suele usar uno de estos dos métodos:
1. Marcar el registro como borrado o no válido, para ello hay que añadir
un campo extra en la estructura del registro:
struct stRegistro {
char Valido; // Campo que indica si el registro es válido
char Nombre[34];
int dato;
int matriz[23];
};
Si el campo Valido tiene un valor prefijado, por ejemplo 'S' o ' ', el
registro es válido. Si tiene un valor prefijado, por ejemplo 'N' o '*', el
registro será inválido o se considerará borrado.
De este modo, para borrar un registro sólo tienes que cambiar el valor
de ese campo.
Pero hay que tener en cuenta que será el programa el encargado de
tratar los registros del modo adecuado dependiendo del valor del campo
Valido, el hecho de marcar un registro no lo borra físicamente.
Si se quiere elaborar más, se puede mantener un fichero auxiliar con la
lista de los registros borrados. Esto tiene un doble propósito:
o Que se pueda diseñar una función para sustituir a fseek() de
modo que se tengan en cuenta los registros marcados.
o Que al insertar nuevos registros, se puedan sobrescribir los
anteriormente marcados como borrados, si existe alguno.
2. Hacer una copia del fichero en otro fichero, pero sin copiar el
registro que se quiere borrar. Este sistema es más tedioso y lento, y
requiere cerrar el fichero y borrarlo o renombrarlo, antes de poder
usar de nuevo la versión con el registro eliminado.
Lo normal es hacer una combinación de ambos, durante la ejecución normal
del programa se borran registros con el método de marcarlos, y cuando se
cierra la aplicación, o se detecta que el porcentaje de registros borrados
es alto o el usuario así lo decide, se "empaqueta" el fichero usando el
segundo método.
8 Ejemplo:
A continuación se incluye un ejemplo de un programa que trabaja con
registros de acceso aleatorio, es un poco largo, pero bastante completo:
// aleatory.cpp: Ejemplo de ficheros de acceso aleatorio.
#include
#include
struct stRegistro {
char valido; //Campo que indica si el registro es válido S-
>Válido, N->Inválido
char nombre[34];
int dato[4];
};
int Menu();
void Leer(struct stRegistro *reg);
void Mostrar(struct stRegistro *reg);
void Listar(long n, struct stRegistro *reg);
long LeeNumero();
void Empaquetar(FILE **fa);
int main()
{
struct stRegistro reg;
FILE *fa;
int opcion;
long numero;
fa = fopen("alea.dat", "r+b"); // Este modo permite leer y
escribir
if(!fa) fa = fopen("alea.dat", "w+b"); // si el fichero no existe, lo
crea.
do {
opcion = Menu();
switch(opcion) {
case '1': // Añadir registro
Leer(®);
// Insertar al final:
fseek(fa, 0, SEEK_END);
fwrite(®, sizeof(struct stRegistro), 1, fa);
break;
case '2': // Mostrar registro
system("cls");
printf("Mostrar registro: ");
numero = LeeNumero();
fseek(fa, numero*sizeof(struct stRegistro), SEEK_SET);
fread(®, sizeof(struct stRegistro), 1, fa);
Mostrar(®);
break;
case '3': // Eliminar registro
system("cls");
printf("Eliminar registro: ");
numero = LeeNumero();
fseek(fa, numero*sizeof(struct stRegistro), SEEK_SET);
fread(®, sizeof(struct stRegistro), 1, fa);
reg.valido = 'N';
fseek(fa, numero*sizeof(struct stRegistro), SEEK_SET);
fwrite(®, sizeof(struct stRegistro), 1, fa);
break;
case '4': // Mostrar todo
rewind(fa);
numero = 0;
system("cls");
printf("Nombre Datos\n");
while(fread(®, sizeof(struct stRegistro), 1, fa))
Listar(numero++, ®);
system("PAUSE");
break;
case '5': // Eliminar marcados
Empaquetar(&fa);
break;
}
} while(opcion != '0');
fclose(fa);
return 0;
}
// Muestra un menú con las opciones disponibles y captura una opción del
usuario
int Menu()
{
char resp[20];
do {
system("cls");
printf("MENU PRINCIPAL\n");
printf("--------------\n\n");
printf("1- Insertar registro\n");
printf("2- Mostrar registro\n");
printf("3- Eliminar registro\n");
printf("4- Mostrar todo\n");
printf("5- Eliminar registros marcados\n");
printf("0- Salir\n");
fgets(resp, 20, stdin);
} while(resp[0] < '0' && resp[0] > '5');
return resp[0];
}
// Permite que el usuario introduzca un registro por pantalla
void Leer(struct stRegistro *reg)
{
int i;
char numero[6];
system("cls");
printf("Leer registro:\n\n");
reg->valido = 'S';
printf("Nombre: ");
fgets(reg->nombre, 34, stdin);
// la función fgets captura el retorno de línea, hay que eliminarlo:
for(i = strlen(reg->nombre)-1; i && reg->nombre[i] < ' '; i--)
reg->nombre[i] = 0;
for(i = 0; i < 4; i++) {
printf("Dato[%1d]: ", i);
fgets(numero, 6, stdin);
reg->dato[i] = atoi(numero);
}
}
// Muestra un registro en pantalla, si no está marcado como borrado
void Mostrar(struct stRegistro *reg)
{
int i;
system("cls");
if(reg->valido == 'S') {
printf("Nombre: %s\n", reg->nombre);
for(i = 0; i < 4; i++) printf("Dato[%1d]: %d\n", i, reg->dato[i]);
}
system("PAUSE");
}
// Muestra un registro por pantalla en forma de listado,
// si no está marcado como borrado
void Listar(long n, struct stRegistro *reg)
{
int i;
if(reg->valido == 'S') {
printf("[%6ld] %-34s", n, reg->nombre);
for(i = 0; i < 4; i++) printf(", %4d", reg->dato[i]);
printf("\n");
}
}
// Lee un número suministrado por el usuario
long LeeNumero()
{
char numero[6];
fgets(numero, 6, stdin);
return atoi(numero);
}
// Elimina los registros marcados como borrados
void Empaquetar(FILE **fa)
{
FILE *ftemp;
struct stRegistro reg;
ftemp = fopen("alea.tmp", "wb");
rewind(*fa);
while(fread(®, sizeof(struct stRegistro), 1, *fa))
if(reg.valido == 'S')
fwrite(®, sizeof(struct stRegistro), 1, ftemp);
fclose(ftemp);
fclose(*fa);
remove("alea.bak");
rename("alea.dat", "alea.bak");
rename("alea.tmp", "alea.dat");
*fa = fopen("alea.dat", "r+b");
}
21 Apuntadores
Los punteros proporcionan la mayor parte de la potencia al C y C++, y
marcan la principal diferencia con otros lenguajes de programación. Una
buena comprensión y un buen dominio de los punteros pondrá en tus manos una
herramienta de gran potencia. Un conocimiento mediocre o incompleto te
impedirá desarrollar programas eficaces.
La memoria RAM de un ordenador está compuesta por unidades básicas
llamadas bits. Cada bit sólo puede tomar dos valores, normalmente
denominados alto y bajo, ó 1 y 0. Pero trabajar con bits no es práctico, y
por eso se agrupan. Cada grupo de 8 bits forma un byte u octeto. En
realidad el microprocesador, y por lo tanto nuestro programa, sólo puede
manejar directamente bytes o grupos de dos o cuatro bytes. Para acceder a
los bits hay que acceder antes a los bytes. Y aquí llegamos al quid, cada
byte tiene una dirección, llamada normalmente dirección de memoria.
La unidad de información básica es la palabra, dependiendo del tipo de
microprocesador una palabra puede estar compuesta por dos, cuatro, ocho o
dieciséis bytes. Hablaremos en estos casos de plataformas de 16, 32, 64 ó
128 bits. Se habla indistintamente de direcciones de memoria, aunque las
palabras sean de distinta longitud. Cada dirección de memoria contiene
siempre un byte. Lo que sucederá cuando las palabras sean de 32 bits es que
accederemos a posiciones de memoria que serán múltiplos de 4.
"Un puntero es un tipo especial de variable que contiene, ni más ni
menos que, una dirección de memoria".
Por supuesto, a partir de esa dirección de memoria puede haber
cualquier tipo de objeto: un char, un int, un float, un array, una
estructura, una función u otro puntero.
Intentemos ver con mayor claridad el funcionamiento de los punteros.
Podemos considerar la memoria del ordenador como un gran array, de modo que
podemos acceder a cada celda de memoria a través de un índice. Podemos
considerar que la primera posición del array es la 0 celda[0].
Si usamos una variable para almacenar el índice, por ejemplo,
índice=0, entonces celda[0] == celda[índice]. Prescindiendo de la notación
de los arrays, el índice se comporta exactamente igual que un puntero.
El puntero índice podría tener por ejemplo, el valor 3, en ese caso,
*índice tendría el valor 'valor3'.
Las celdas de memoria existirán independientemente del valor de
índice, o incluso de la existencia de índice, por lo tanto, la existencia
del puntero no implica nada más que eso, pero no que el valor de la
dirección que contiene sea un valor válido de memoria.
Dentro del array de celdas de memoria existirán zonas que contendrán
programas y datos, tanto del usuario como del propio sistema operativo o de
otros programas, el sistema operativo se encarga de gestionar esa memoria,
prohibiendo o protegiendo determinadas zonas.
El propio puntero, como variable que es, ocupará ciertas direcciones
de memoria.
En principio, debemos asignar a un puntero, o bien la dirección de un
objeto existente, o bien la de uno creado explícitamente durante la
ejecución del programa. El sistema operativo suele controlar la memoria, y
no tiene por costumbre permitir el acceso al resto de la memoria.
1 Declaración de punteros.
Una variable puntero se declara como todas las variables. Debe ser del
mismo tipo que la variable apuntada. Su identificador va precedido de un
asterisco (*):
int *punt;
*punt es una variable puntero que apunta a otra variable que contiene un
dato de tipo entero.
char *car:
Es un puntero a variable de tipo carácter.
Como pasa con todas las variables en C++, cuando se declaran sólo se
reserva espacio para almacenarlas, pero no se asigna ningún valor inicial,
el contenido de la variable permanecerá sin cambios, de modo que el valor
inicial del puntero será aleatorio e indeterminado. Debemos suponer que
contiene una dirección no válida.
Si "punt" apunta a una variable de tipo "int", "*punt" será el
contenido de esa variable, pero no olvides que "*punt" es un operador
aplicado a una variable de tipo "puntero a int", es decir "*punt" es una
expresión, no una variable.
2 Obtener punteros a variables.
Para averiguar la dirección de memoria de cualquier variable usaremos
el operador de dirección (&), que leeremos como "dirección de".
Por supuesto, los tipos tienen que ser "compatibles", no podemos
almacenar la dirección de una variable de tipo "char" en un puntero de tipo
"int".
Por ejemplo:
int A;
int *pA;
pA = &A;
Según este ejemplo, pA es un puntero a int que apunta a la dirección
donde se almacena el valor del entero A.
3 Diferencia entre punteros y variables.
Declarar un puntero no creará un objeto. Por ejemplo: int *entero; no
crea un objeto de tipo "int" en memoria, sólo crea una variable que puede
contener una dirección de memoria. Se puede decir que existe físicamente la
variable "entero", y también que esta variable puede contener la dirección
de un objeto de tipo "int". Lo veremos mejor con otro ejemplo:
int A, B;
int *entero;
...
B = 213; /* B vale 213 */
entero = &A; /* entero apunta a la dirección de la variable A */
*entero = 103; /* equivale a la línea A = 103; */
B = *entero; /* equivale a B = A; */
...
En este ejemplo vemos que "entero" puede apuntar a cualquier variable
de tipo "int", y que podemos hacer referencia al contenido de dichas
variables usando el operador de indirección (*).
Como todas las variables, los punteros también contienen "basura"
cuando son declaradas. Es costumbre dar valores iniciales nulos a los
punteros que no apuntan a ningún sitio concreto:
entero = NULL;
caracter = NULL;
NULL es una constante, que está definida como cero en varios ficheros de
cabecera, como "stdio" o "iostream", y normalmente vale 0L.
4 Correspondencia entre arrays y punteros
Existe una equivalencia casi total entre arrays y punteros. Cuando
declaramos un array estamos haciendo varias cosas a la vez:
Declaramos un puntero del mismo tipo que los elementos del array, y
que apunta al primer elemento del array.
Reservamos memoria para todos los elementos del array. Los elementos
de un array se almacenan internamente en el ordenador en posiciones
consecutivas de la memoria.
La principal diferencia entre un array y un puntero es que el nombre
de un array es un puntero constante, no podemos hacer que apunte a otra
dirección de memoria. Además, el compilador asocia una zona de memoria para
los elementos del array, cosa que no hace para los elementos apuntados por
un puntero auténtico.
Ejemplo:
int vector[10];
int *puntero;
puntero = vector; /* Equivale a puntero = &vector[0];
*puntero++; /* Equivale a vector[0]++; */
puntero++; /* puntero equivale a &vector[1] */
¿Qué hace cada una de estas instrucciones?:
La primera incrementa el contenido de la memoria apuntada por
"puntero", que es vector[0].
La segunda incrementa el puntero, esto significa que apuntará a la
posición de memoria del siguiente "int", pero no a la siguiente posición de
memoria. El puntero no se incrementará en una unidad, como tal vez sería
lógico esperar, sino en la longitud de un "int".
Análogamente la operación:
puntero = puntero + 7;
No incrementará la dirección de memoria almacenada en "puntero" en
siete posiciones, sino en 7*sizeof(int).
Otro ejemplo:
struct stComplejo {
float real, imaginario;
} Complejo[10];
stComplejo *p;
p = Complejo; /* Equivale a p = &Complejo[0]; */
p++; /* p == &Complejo[1] */
En este caso, al incrementar p avanzaremos las posiciones de memoria
necesarias para apuntar al siguiente complejo del array "Complejo". Es
decir avanzaremos sizeof(stComplejo) bytes.
5 Operaciones con punteros.
Aunque no son muchas las operaciones que se pueden hacer con los punteros,
cada una tiene sus peculiaridades.
6 Asignación.
Ya hemos visto cómo asignar a un puntero la dirección de una variable.
También podemos asignar un puntero a otro, esto hará que los dos apunten a
la misma posición:
int *q, *p;
int a;
q = &a; /* q apunta al contenido de a */
p = q; /* p apunta al mismo sitio, es decir, al contenido de a */
7 Operaciones aritméticas.
También hemos visto como afectan a los punteros las operaciones de
suma con enteros. Las restas con enteros operan de modo análogo. Pero, ¿qué
significan las operaciones de suma y resta entre punteros?, por ejemplo:
int vector[10];
int *p, *q;
p = vector; /* Equivale a p = &vector[0]; */
q = &vector[4]; /* apuntamos al 5º elemento */
printf("%u", q-p);
El resultado será 4, que es la "distancia" entre ambos punteros.
Normalmente este tipo de operaciones sólo tendrá sentido entre punteros que
apunten a elementos del mismo array. La suma de punteros no está permitida.
Guía de programas elaborados
#include
#include
char cad[20]="Hola Buenos Dias\n";
char *punt; //declaracion de puntero.
void main(void)
{
//inicialización de variable
punt=cad; //asignacion de direccion al puntero.
clrscr();
printf("a = %s", cad); //imprimo valor de variable
printf("\n&a = %x", cad); //imprimo dirección variable apuntada (en
hexadecimal)
printf("\npunt = %x ", punt); //imprimo la dirección guardada (la
variable apuntada)
printf("\n*punt = %s", punt); //imprimo el contenido de la dirección
apuntada.
printf("\nTamaño puntero %d Bytes", sizeof(punt));
getch();
}
#include
#include
void main()
{
char carac='A', *r;
int a=12,b=45;
int *p, *q;
clrscr();
printf("Direcci¢n de a=%p\n", &a); printf("Direcci¢n de b=%p\n",
&b);
printf("a = %d b = %d\n", a, b); printf("Direcci¢n de caract =
%p\n", &carac);
printf("carac = %c\n", carac); printf("Valor ASCII de carac =
%d\n", carac);
printf("Direcci¢n de p = %p\n", &p);
printf("Direcci¢n de q = %p\n", &q);
printf("Direcci¢n de r = %p\n", &r);
p = &a;
q = &b;
r = &carac;
printf("El puntero p apunta a la direcci¢n: %p\n", p);
printf("Y su contenido es: %d\n", *p);
printf("El puntero q apunta a la direcci¢n: %p\n", q);
printf("Y su contenido es: %d\n", *q);
printf("El puntero r apunta a la direcci¢n: %p\n", r);
printf("Y el contenido de *r es: %d\n", *r);
printf("Como caracter ASCII *r contiene: %c\n", *r);
getch();
}
//Programa que suma un valor a una variable a través de un puntero
#include
#include
void main()
{
int a, *p;
a=5;
p=&a;
*p+=7; //*p=*p+7
printf("\nEl valor final de a es: %d", a);
getch();
}
#include
#include
void main()
{
int mat[] = {1,2,3,4}, *pmat, i;
clrscr();
printf("\Empleamos notaci¢n decimal\n");
printf("Hacemos que el puntero se¤ale al primer elemento del array:
pmat = mat");
pmat = mat;
//pmat=&mat[0]; //ES LO MISMO QUE pmat =mat
printf("\nComprobamos los valores: ");
printf("\nDirecci¢n de inicio del array: mat = %u", &mat[0]);
getch();
printf("\nDirecci¢n de pmat: &pmat = %u", &pmat);
getch();
printf("\nImprimimos las direcciones de memoria:\n");
for(i=0; i<4; i++)
{
printf("\n&mat[%u] = %u (pmat+%u) = %u ", i, &mat[i], i,
(pmat+i));
printf(" mat[%u] = %u *(pat+%u) = %u" , i, mat[i], i,
*(pmat+i));
}
getch();
}
BIBLIOGRAFIA
SCHILDT, H.: "C: Manual de referencia" Osborne-Mc Graw-Hill, 1990
SEDGEWICK, R.: "Algoritmos en C++". Addison-Wesley Iberoameriacana /
Díaz de Santos.
KERNIGHAN, B. Y RITCHIE, D.: "El lenguaje de programación C". Prentice
Hall.
KERNIGHAN, B. Y PIKE, R.: "La práctica de la programación". Prentice
Hall.
Metodología de la Programación. Luis Joyanes Aguilar
Lenguajes de Diagramas de Flujo Forsythe, Keenan, Organick, Stenberg.
Introducción a la computación jose vasconcelos Santillán,
publicaciones culturales, séptimo impresión, México2001.
-----------------------
Prototipo de Funcion
Llamada a Funcion y paso de argumentos
Encabezado de funcion
Sentencia return para regreso de resultado
Sentencia return para regreso de resultado
CECYTEV
Colegio de Estudios Científicos y Tecnológicos del Estado de Veracruz