PROGRAMACION ESTRUCTURADA - unpa.edu.mxblopez/ProgramacionEstructurada/Estructura... · M. C....

29
M. C. Bertha López Azamar Modulo 4 pag. 1 PROGRAMACION ESTRUCTURADA 1.1 Esquema General de un programa en Lenguaje C El lenguaje C, tiene una sintaxis que permite al programador estructurar sus programas de una manera fácil, siempre y cuando se respeten las reglas y se tenga un buen estilo de programación. Todo programa tiene una estructura general, dependiendo muchas veces del tipo de programación que se este tratando, va a ser la forma de incluir los elementos que un programa permite. En el caso de la programación en el lenguaje C, todo programa consta de uno o más módulos llamados funciones. Una de las funciones se llama main(), y todo programa escrito en C comenzará con ésta función, desde la cual se puede acceder a las demás funciones. Recordemos que un programa puede considerarse como una secuencia de acciones (instrucciones) que manipulan un conjunto de objetos (datos). Contendrá, por lo tanto, dos bloques para la descripción de los aspectos citados: Bloque de declaraciones: En él que se especifican todos los objetos que utiliza el programa (variables, constantes, tablas, registros, archivos, etc.) El lenguaje C es sensible a mayusculas y minusculas. Bloque de instrucciones: Constituido por el conjunto de operaciones que se han de realizar para la obtención de los resultados deseados. 1.1.1 Descomposición de un problema #include <stdio.h> #define PI 3.1416 #define CUADRADO(x) ((x)*(x)) /* Programa: Area de un círculo Autor: A.G. programador Descripción: Este programa calculará el área de un círculo. El programador sólo debe introducir el radio. El valor devuelto está en unidades cuadradas Variables: valor_de entrada = Valor introducido por el usuario. radio = Radio del círculo. area = área del círculo. Constantes: PI = 3.1416 Prototipos de funciones: */ void explica_el programa(void); float obtener_valor(void); float area_del_circulo(float radio); void mostrar_la_respuesta(void);

Transcript of PROGRAMACION ESTRUCTURADA - unpa.edu.mxblopez/ProgramacionEstructurada/Estructura... · M. C....

M. C. Bertha López Azamar Modulo 4 pag. 1

PROGRAMACION ESTRUCTURADA

1.1 Esquema General de un programa en Lenguaje C

El lenguaje C, tiene una sintaxis que permite al programador estructurar sus programas de

una manera fácil, siempre y cuando se respeten las reglas y se tenga un buen estilo de programación.

Todo programa tiene una estructura general, dependiendo muchas veces del tipo de

programación que se este tratando, va a ser la forma de incluir los elementos que un programa

permite. En el caso de la programación en el lenguaje C, todo programa consta de uno o más

módulos llamados funciones. Una de las funciones se llama main(), y todo programa escrito en C

comenzará con ésta función, desde la cual se puede acceder a las demás funciones.

Recordemos que un programa puede considerarse como una secuencia de acciones

(instrucciones) que manipulan un conjunto de objetos (datos). Contendrá, por lo tanto, dos bloques

para la descripción de los aspectos citados:

Bloque de declaraciones: En él que se especifican todos los objetos que utiliza el

programa (variables, constantes, tablas, registros, archivos, etc.) El lenguaje C es

sensible a mayusculas y minusculas.

Bloque de instrucciones: Constituido por el conjunto de operaciones que se han de

realizar para la obtención de los resultados deseados.

1.1.1 Descomposición de un problema

#include <stdio.h>

#define PI 3.1416

#define CUADRADO(x) ((x)*(x))

/* Programa: Area de un círculo

Autor: A.G. programador

Descripción: Este programa calculará el área de un círculo. El programador sólo

debe introducir el radio. El valor devuelto está en unidades cuadradas

Variables:

valor_de entrada = Valor introducido por el usuario.

radio = Radio del círculo.

area = área del círculo.

Constantes: PI = 3.1416

Prototipos de funciones:

*/

void explica_el programa(void);

float obtener_valor(void);

float area_del_circulo(float radio);

void mostrar_la_respuesta(void);

M. C. Bertha López Azamar Modulo 4 pag. 2

float area; //area del circulo

void main()

{

float radio;

explica_el_programa(); /*explica el programa*/

radio = obtener_valor(); //pide el radio al usuario

area = area_del circulo(radio); //calcula el área del circulo

mostrar la respuesta(); //muestra la respuesta al usuario

}

void explica_el_programa(void) /*explica el programa al usuario*/

{

printf("Este programa calcula el area de un circulo.\n");

printf("Introduzca el valor del radio y pulse -INTRO- \n");

printf("\n"); /*imprime una línea en blanco*/

}

float obtener_valor(void) //Pide el radio al usuario

{

float valor_de _entrada; //valor introducido por el usuario

printf("Valor del radio --> ");

scanf("%f", &valor_de_entrada);

return(valor_de_entrada);

}

float area_del_circulo(float radio) //Calcula el área del círculo

{

float area; //area del circulo

area = PI * CUADRADO(radio);

return(area);

}

void mostrar_la_respuesta(void)

{

printf("\n\n"); /*imprime dos lineas en blanco*/

printf("El area del circulo es %f unidades.", area);

}

M. C. Bertha López Azamar Modulo 4 pag. 3

1.2 Concepto de función

Las funciones son bloques constructores de C y el lugar donde se produce toda la actividad

del programa.

Una función es una colección independiente de declaraciones1 y

sentencias(instrucciones).

Una función es un segmento independiente de código fuente diseñado para realizar

una tarea específica.

Cualquier programador puede hacer sus propias funciones en C, la única función obligatoria

es main(), y a partir de ella, se llaman todas las demás. Debe tenerse cuidado de escribir los

prototipos de funciones, las llamadas deben hacerse correctamente y mandando los argumentos

correctos, en el orden correcto, con los tipos de datos correctos; y devolviendo(retornando) los

valores de acuerdo al tipo de dato de la variable que recibirá el valor en la llamada a la función;

igualmente, las cabeceras de las funciones y los prototipos de las mismas, deben tener los tipos de

datos de las variables en el orden correcto e iguales, ya que sino, se incurre en errores de sintaxis,

los cuales deben de ser detectados por el programador.

No es un trabajo complicado el manejo de funciones, pero si es delicado en el aspecto de

manejo de variables, ya que deben considerarse los tipos de datos, para que no se tengan

inconsistencias y el programa mantenga la integridad.

Si queremos que nuestro programa tenga mayor legibilidad y podamos mantener bloques de

código consistentes, podemos estar seguros de que el manejo de las funciones en la programación en

C proporciona las mayores ventajas. Recordemos que tratamos en este curso la programación

estructurada.

Cada función es un bloque de código discreto. Por lo que una función define un ámbito de

bloque. Lo que significa que el código de una función es privado a esa función, y no se puede

acceder a él mediante una instrucción de otra función, a menos que se haga a través de una llamada

a esa función.

La declaración de una función se realiza mediante lo que se denomina prototipo de una

función. Un prototipo de función consta del nombre de la función y de información importante

relativa a la misma. Los prototipos de las funciones aparecen al comienzo del programa y antes de la

función main().

Cuando se define una función, se indica de nuevo el nombre de la función y la información

relativa a la misma (al igual que en el prototipo de la función) y el cuerpo de la función, que

contiene todo el código fuente utilizado por la función.

El cuerpo de la función está oculto al resto del programa. Las variables de la función son

locales al código, a menos que la variable se defina como global.

Los parámetros formales de una función también tienen la función como ámbito. Esto

significa que los parámetros se conocen en toda la función.

Todas las funciones tienen un ámbito de archivo, es decir son locales al archivo.

No se puede definir una función dentro de otra función.

1 Una declaración establece la relación entre el nombre y el tipo de una variable u otra función.

M. C. Bertha López Azamar Modulo 4 pag. 4

Todas las funciones deben estar declaradas antes de ser utilizadas. Esto normalmente se

lleva a cabo mediante los prototipos de funciones. Aunque los prototipos no son técnicamente

obligatorios, su uso se recomienda insistentemente. (en C++, su uso si es obligatorio)

Los prototipos permiten que el compilador lleve a cabo una fuerte comprobación de tipos.

Cuando se usan, el compilador puede encontrar e informar sobre conversiones de tipo ilegales entre

el tipo de los argumentos usados en la llamada a la función y las definiciones de tipos de sus

parámetros.

La forma general de un prototipo de función es:

Tipo nombre_de_la_funcion(tipo de parámetro1, tipo de parámetro2,…, tipo de parámetroN, );

En el prototipo, colocar los parámetros es opcional, pero esto permite que el compilador

identifique la discordancia de tipo por su nombre, cuando se dé un error, por lo que es una buena

idea incluirlos.

La forma general de una función es:

Tipo_dev nombre_de_la_funcion(lista de parámetros)

{

cuerpo de la función

}

donde:

tipo_dev especifica el tipo de dato que devuelve la función. Una función puede

devolver cualquier tipo de dato, excepto un array.

Nombre_de_funcion, cuando se crea una función, es mejor darle un nombre

descriptivo. Recordemos que el nombre es un identificador, por lo que debe

cumplir las reglas de los identificadores.

Lista de parámetros es una lista de nombres de variables separados por comas con

sus tipos asociados. Los parámetros reciben los valores de los argumentos

cuando se llama a la función. Puede no tener parámetros una función, y se

especifica colocando la palabra clave void entre los paréntesis. Si los

parámetros son del mismos tipo, deben declararse individualmente, la forma

general es la siguiente:

f(tipo var1, tipo var2 …,, tipo varN)

Ejemplos:

Correcto: f(int i, int k, int j);

Incorrecto: f(int i, k, float j); /*a k le falta el tipo de dato*/

Si una función va a aceptar argumentos, debe declarar variables que reciban los valores de

los argumentos.

M. C. Bertha López Azamar Modulo 4 pag. 5

Ejemplo:

#include <stdio.h>

int cuadrado(int Y); /*prototipo de la función*/

int main(void)

{

int X=10;

printf(“El cuadrado de %d, es: %d”, t, cuadrado(t)); /*llamada de la función*/

return 0;

}

int cuadrado(int Y) /*declaración de la función*/

{

y = y * y;

return(Y);

}

La instrucción return se usa para devolver de una función. Se trata de una instrucción de

salto porque hace que la ejecución vuelva al punto en que se hizo la llamada a la función. Puede

tener o no uno y solo un valor asociado, en una función con valor de vuelta de tipo distinto de void.

En este caso, el valor asociado con return es el valor de vuelta de la función. El return sin valor de

vuelta se utiliza para volver de las funciones de tipo void. La sintaxis general es:

return expresión;

donde:

expresión estará presente sólo si se ha declarado la función como devolviendo un valor.

Pueden usarse tantas instrucciones return como se quiera en una función, pero, la función

terminará al encontrarse la primera línea con la función return. Si no se va a devolver un valor

(porque la función se declaro como void), y se encuentra primero la llave de cierre de la función

(“}“), esto provoca que se termine la función y se vuelva de ella, equivale a un return sin valor

especifico.

Todas las funciónes, excepto aquellas de tipo void, devuelven un valor. Este valor se

especifica explícitamente en la instrucción return. Una función que no sea de tipo void debe

devolver un valor por medio de una instrucción return. Si la ejecución de una función que no sea

void llega al final de la función (esto es, si encuentra la llave de cierre al final de la función), se

devuelve basura como valor de vuelta. Pero esto es una mala práctica y se debe evitar.

Ahora bien, cualquier función de biblioteca estandar que se utilice en el programa, debe

estar prototipada. Para ello se debe incluir la cabecera (archivo de cabecera) apropiada para cada

función de biblioteca. En C, las bibliotecas de funciones generalmente tienen archivos de extensión

.h.

M. C. Bertha López Azamar Modulo 4 pag. 6

1.3 Concepto de procedimiento

El caso de las subrutinas o procedimientos, estos, son subprogramas que ejecutan un proceso

específico.

Ningún valor esta asociado con el nombre del procedimiento, por lo que, no puede ocurrir en

una expresión.

Un procedimiento puede devolver muchos valores al salir de el, un valor o ningún valor y en

forma de lista de parámetros.

En realidad, los procedimientos ya se conservan sólo en algunos lenguajes procedimentales

como Pascal. En otros lenguajes, sólo se implementan funciones y los procedimientos son

equivalentes a funciones que no devuelven valor (En C, se denomina función que devuelve un valor

de tipo void).

M. C. Bertha López Azamar Modulo 4 pag. 7

1.4 Variables globales y locales

Existen tres sitios básicos donde se declaran variables:

dentro de las funciones, y son llamadas variables locales.

en la definición de parámetros de funciones y son llamados parámetros formales; y

fuera de todas las funciones y se les llama variables globales.

Las variables locales pueden ser utilizadas sólo en las instrucciones que estén dentro del

bloque en el que han sido declaradas. No se conocen fuera de su propio bloque de código. Es más,

otra variable en otro bloque de código puede llamarse igual, y no tiene nada que ver con ella. El

bloque más normal en el que se declaran las variables locales es la función.

Ejemplo:

void funciónX(void)

{

int X;

X = 30;

}

void funciónYvoid)

{

int X;

X = 10;

}

Las variables locales, sólo existen mientras se está ejecutando el bloque en el que son

declaradas. Se crean al entrar en el bloque y se destruye al salir de él, es decir, su contenido se

pierde al salir de él. Esto significa que no pueden retener sus valores entre llamadas. Por esto, al

darse un valor inicial a una variable local estática, este valor se asignará cada vez que vuelva a

llamarse a la función. Aunque se puede indicar al compilador que retenga sus valores mediante el

uso del modificador static.

A menos que se especifique lo contrario, las variables locales se guardan en la pila. Y por lo

tanto, terminar la llamada a la función, los valores no se pueden mantener, y se borran de la pila.

La ventaja de las variables locales es que hace a los subprogramas independientes,

permitiendo la comunicación, entre el programa principal y los subprogramas manipulados

estructuralmente, a través de la lista de parámetros ( lo cual se conoce como paso de parámetros)

NOTA: el ámbito de un identificador (variable, constante, procedimiento) es la parte del

programa donde se conoce el identificador.

M. C. Bertha López Azamar Modulo 4 pag. 8

Figura 1 Ejemplificación del ámbito de las variables.

En la figura superior, puede observarse claramente el ámbito de las variables; los bloques de

código marcados por los círculos internos, determinan claramente el lugar donde son vistas cada una

de las variables locales, por ejemplo: las variables VAR5 y VAR6, son empleadas únicamente por la

FUNCION C, y ninguna de las otras funciones puede ver a tales variables. Lo contrario pasa con la

funcion VAR0, la cual se encuentra en el bloque del código principal del programa, y puede ser

vista tanto por FUNCION A, FUNCION B y FUNCION C, ya que es una variable global.

Las variables globales, se conocen a lo largo de todo el programa y se pueden usar en

cualquier parte del código. Además de que mantienen sus valores durante toda la ejecución del

programa. Se crean al declararlas en cualquier parte fuera de una función.(recordemos que main() es

una función igualmente de C).

Pueden ser accedidas por cualquier expresión, independientemente de la función en la que se

encuentre la expresión. Si una variable global y una variable local tienen el mismo nombre, todas las

referencias a ese nombre de variable dentro de la función donde se ha declarado la variable local se

refieren a esa variable local y no tienen efecto sobre la variable global.

Las variables globales, tienen la ventaja de compartir información de diferentes

subprogramas, sin una correspondiente entrada en la lista de parámetros.

El almacenamiento de las variables globales se hace en una región de memoria fija

establecida para este propósito por el compilador. Son muy útiles cuando se usan los mismos datos

FUNCION

A

var1, var2

FUNCION C

VAR5,VAR6

FUNCION

B

var3,var4 PROGRAMA

PRINCIPAL

VAR0

M. C. Bertha López Azamar Modulo 4 pag. 9

en varias funciones del programa, sin embargo el uso de variables globales innecesarias, provoca

que se utilice memoria durante todo el tiempo de la ejecución del programa.

C define tres categorías de enlace: externo, interno y nulo.

En general las funciones y las variables globales se ven afectadas por un enlace

externo. Esto significa que están disponibles para todos los archivos que constituyen

el programa.

Los objetos con ámbito de archivo declarados como static2 tienen un enlace interno.

Sólo son conocidos dentro del archivo en el que están declarados. Las variables

locales no se ven afectadas por el enlace y por lo tanto son conocidas sólo dentro de

su propio bloque.

C contempla cuatro especificadores de clase de almacenamiento para las variables: extern,

static, register, auto.

El uso de extern es el de especificar que un objeto está declarado con enlace externo en

alguna otra parte del programa. En la mayoria de los casos, al declarar una variable, también se dice

que se esta definiendo (es decir, que se esta disponiendo de almacenamiento para la variable), sin

embargo, al usar el especificador extern, se puede declarar una variable sin definirla. Pero, si se da

un valor a la variable, la declaración extern se convierte en una definición.

Por ejemplo, cuando se declara una variable, dentro del bloque de la función main(), puede

ser declarada como extern, para que si en algun bloque de codigo externo al main(), se llega a

definir, se pueda hacer, y se pueda usar, incluso antes de la línea de código donde se inicializan los

valores.

Ejemplo:

int main(void)

{

extern int primero, ultimo; /*uso de variables globales*/

printf(“%d, %d”), primero, ultimo);

return 0;

}

/*definición global de primero y ultimo en otra linea fuera del main()*/

int primero = 10, ultimo = 20;

Aquí lo que el printf mostrará será los valores 10, 20.

El mayor uso de extern es el que se le da con los programas multiarchivo, los cuales son

compilador por separado y enlazados luego juntos.

Las variables static son variables permanentes. Difieren de las variables globales en que no

2 Las variables static son variables permanentes. Difieren de las variables globales en que no son conocidas

fuera de su función o archivo, aunque mantienen sus valores entre llamadas.

M. C. Bertha López Azamar Modulo 4 pag. 10

son conocidas fuera de su función o archivo, aunque mantienen sus valores entre llamadas.

Las variables locales static sólo se conocen en la función o bloque de código en el que son

declaradas; los nombres de las variables globales static sólo se conocen en el archivo en que residen.

Las variables static permiten al programador ocultar partes de un programa a otras partes del mismo.

Al aplicarse el especificador static a una variable local, el compilador crea un

almacenamiento permanente para ella de forma que sólo sea conocida por el bloque de código donde

es declarada. Pero que al realizar llamadas sucesivas a la función, el valor de la variable en la

llamada anterior a la función, pueda ser recuperado en la llamada actual. Puede darse un valor inicial

a una variable local estática, pero este valor se asignará sólo una vez; y no cada vez que vuelva a

llamarse a la función.

Al aplicarse el especificador static a una variable global, lo que se hace es indicarle al

compilador que cree una variable global conocida solo en el archivo en el que se declara la variable

global estática. Lo que ocasiona que las rutinas de otros archivos no la reconocerán y no podrán

alterar sus valores. Así una variable global estática tiene enlace interno.

El especificador de almacenamiento register, originalmente pedía al compilador que

mantuviera el valor de una variable declarada con ese especificador en un registro de la CPU en

lugar de en memoria, que es donde se almacenan las variables. Actualmente, la definición de

register se ha ampliado, pudiendo ser aplicado a cualquier tipo de variable. Simplemente establece

que el acceso al objeto sea lo más rápido posible. Dependiendo de la implementación del

compilador de C y del entorno de operación, las variables register pueden ser manejadas de algún

modo establecido por el creador del compilador. Solo puede aplicarse el especificador register a

variables locales y a parámetros formales de una función. No se permiten variables globales

register.

El especificador auto, se usa para declarar variables globales. Sin embargo, como todas las

variables que no son globales son, por defecto asumidas como auto, este especificador casi ni se usa.

M. C. Bertha López Azamar Modulo 4 pag. 11

1.5 Paso de parámetros

Muchas funciones usan argumentos, por lo cual debe declarar las variables que van a aceptar

esos valores de los argumentos, a estas variables se les llama propiamente parámetros formales de la

función. Estos, se comportan como cualquier otra variable local de la función.

Figura 2 Llamadas a funciones

Como puede verse en la figura 6, las funciones pueden ser llamadas desde el programa

principal, en la parte de la función main(), así como también pueden llamarse desde otra función. El

objetivo del uso de funciones es poder modularizar el código, permitiendo con ello simplificar la

implementación de la solución del problema. Para permitir que las funciones sean generales, y

puedan manipular valores o referencias de valores externos, se emplea el paso de parámetros.

Los parámetros de una manera simple, permiten mantener aislado código y funcionalidad,

permitiendo que el código pueda reutilizarse.

El uso del paso de parámetros permite que el programador afecte o no los valores de las

variables originales.

Veamos el siguiente diagrama, que nos muestra claramente el uso de los parámetros y los

argumentos.

FUNCION A()

var1, var2

invoca a

FUNCION B()

FUNCION B()

var3,var4

PROGRAMA PRINCIPAL

VAR0

FUNCION main()

VAR10,VAR11

Invoca a

FUNCION A()

FUNCION C() FUNCION C()

VAR5,VAR6

invoca a

FUNCION A()

M. C. Bertha López Azamar Modulo 4 pag. 12

Figura 3 Ejemplificación gráfica del paso de parámetros.

Como podemos ver en la figura, existe un programa principal, una variable global llamada

VAR0, la cual, es pasada como argumento a la función A, por referencia, es decir, se pasa su

dirección de memoria; esto, quizá pueda parecer innecesario, ya que sabemos que una variable

global puede ser vista por cualquier parte del programa, es decir, por cualquier función. El objetivo

de que la función A reciba la dirección de memoria de la variable VAR0, en el parámetro formal “x”

(el cual es un puntero), es permitir que de una u otra forma, la función pueda modificar el valor de la

variable(VAR0) que se encuentra fuera del bloque de código, es decir, fuera del ámbito de la

función, ya que puede llamarse a la función desde cualquier otra parte de código y pasarse por

referencia cualquier otra variable no global; podemos ver el ejemplo de esto con la llamada a la

función A, desde la función C, la cual le pasa como argumento en la llamada la dirección de

memoria de la variable VAR6.

Las declaraciones de los parámetros formales se dan tras el nombre de la función y entre los

paréntesis.

Ejemplo:

FUNCION main()

VAR10,VAR11

Invoca a

FUNCION A(&VAR0)

FUNCION C(VAR10, VAR11)

FUNCION A(*x)

var1, var2

invoca a

FUNCION B(&var1, var2)

FUNCION C(m, l)

VAR5,VAR6

invoca a

FUNCION A(&VAR6)

FUNCION B(*a, b)

var3,var4

PROGRAMA PRINCIPAL

VAR0

M. C. Bertha López Azamar Modulo 4 pag. 13

void funcionX(int X) //aquí se muestra un parámetro X en la función funcionX.

{

int Y;

X = 10;

Y +=X;

return (Y);

}

Los parámetros formales reciben los valores de los argumentos que se pasan a la función,

pero en sí, se comportan como cualquier otra variable local, por lo que igualmente tienen la misma

característica de destruirse al salir de de la función.

Existe el paso de argumentos a la función por valor y por referencia.

El paso por valor, copia el valor de un argumento en el parámetro formal de la subrutina. En

este caso, los cambios realizados sobre el parámetro no afectan al argumento.

Ejemplo:

#include <stdio.h>

int cuadrado(int Y);

int main(void)

{

int X=10;

printf(“El cuadrado de %d, es: %d”, t, cuadrado(t));

return 0;

}

int cuadrado(int Y)

{

y = y * y;

return(Y);

}

El resultado mostrado sería: El cuadrado de 10 es 100.

La llamada por referencia, copia la dirección del argumento en el parámetro. Dentro de la

subrutina se usa la dirección para acceder realmente al argumento usado en la llamada, con lo que

los cambios sufridos por el parámetro se reflejan en el argumento.

Se puede crear una llamada por referencia pasando un puntero al argumento, en lugar de

pasar el propio argumento. Como lo que se pasa a la función es la dirección del argumento, el

código de la función puede cambiar el valor del argumento externo a la función.

M. C. Bertha López Azamar Modulo 4 pag. 14

Los punteros se pasan a las funciones como cualquier otro argumento. Por supuesto, es

necesario declarar los parámetros como de tipo puntero.

Ejemplo:

#include <stdio.h>

void interambio(int *x, int *y);

int main(void)

{

int I, j;

i=10;

j=20;

printf(“ i y j antes del intercambio: %d, %d \n”, i, j);

inter(&i, &j); /*se pasan las direcciones de i y j */

/* se usa el operador monario & para obtener la dirección de las

variables.*/

printf(“ i y j después del intercambio: %d, %d \n”, i, j);

return 0;

}

void intercambio(int *x, int *y);

{

int temp;

temp = *x; /*guarda el valor de la dirección de X */

*x = *y; /*poner y en x */

*y = temp; /*poner x en y*/

}

La salida del programa es:

i y j antes del intercambio: 10, 20

i y j después del intercambio: 20,10

M. C. Bertha López Azamar Modulo 4 pag. 15

2 Primitivas de entrada y salida

C dispone de una biblioteca de funciones de E/S (entrada/salida) muy completa. Estas rutinas

de E/S permiten leer y escribir datos en archivos y dispositivos. Sin embargo, el propio lenguaje C

no incluye estructuras de archivos predefinidas. C trata todos los datos como secuencias de bytes.

Existen tres tipos básicos de funciones de E/S:

De flujo

Por consola y puerto, y

De bajo nivel.

Todas las funciones de E/S de flujo tratan los archivos de datos o los elementos de datos

como un flujo de caracteres individuales. Seleccionando la función de flujo apropiada, la aplicación

puede procesar los datos en cualquier orden o formato requerido, desde caracteres simples a

estructuras de datos grandes y complicadas.

Técnicamente, cuando un programa abre un archivo para utilizar las funciones de flujo de

E/S, el archivo abierto se asocia con una estructura de tipo FILE (predefinido en stdio.h) que

contiene información básica sobre el archivo. Una vez que se abre el flujo, se devuelve un puntero a

la estructura del archivo. El puntero del archivo, a veces llamado puntero de flujo o flujo, se utiliza

para hacer referencia al archivo en todas las sucesivas operaciones de E/S. Todas las funciones de

E/S por flujo proporcionan E/S por buffer, con o sin formato. Un formato por buffer proporciona

una posición de almacenamiento intermedia para toda la información que se introduce en el flujo y

que se extrae del mismo.

Las rutinas de E/S por consola o puerto, tienen un funcionamiento similar a las de flujo,

incluso pueden considerarse como una extensión de estas. Las funciones para la consola y los

puertos de E/S, ejecutan las operaciones de lectura y escritura sobre la consola(teclado/pantalla) o

sobre un puerto específico (como puerto de impresoras). Las funciones de E/S por puerto

simplemente leen o escriben los datos por bytes. Las funciones de E/S por consola ofrecen varias

opciones adicionales, por ejemplo, se puede detectar si se ha escrito un carácter en la consola y si los

caracteres introducidos se muestran o no en la pantalla mientras se leen.

De las funciones de entrada y salida de bajo nivel, ninguna realiza su operación a través de

un buffer o con formato; en su lugar, llaman directamente a las capacidades de entrada y salida del

sistema operativo. Estas rutinas permiten acceder a los archivos y los dispositivos periféricos a un

nivel mas básico que las funciones de flujo. Los archivos abiertos de esta forma devuelven un

controlador de archivo. Este controlador es un valor entero que se utiliza para hacer referencia al

archivo en las sucesivas operaciones.

FUNCIONES DE FLUJO

Para utilizar las funciones de flujo, la aplicación debe incluir el archivo “stdio.h”. Este

archivo contiene la definición de constantes, tipos y estructuras utilizados en las funciones de flujo,

y contiene los prototipos de funcion y las definiciones de macro para las rutinas de flujo.

Cuando comienza a ejecutarse una aplicación, automáticamente se abren cinco flujos. Estos

son el estándar de entrada (stdin), el estándar de salida (stdout), el estándar de error (stderr), el

estándar de impresión (stdprn) y el estandar auxiliar (stdaux).

M. C. Bertha López Azamar Modulo 4 pag. 16

Por omisión, el estándar de entrada, el estándar de salida y el estándar de error hacen

referencia al terminal del usuario. Esto significa que siempre que un programador espere una entrada

desde el estándar de entrada, entiende que la entrada es desde el terminal. De la misma forma, un

programa que escribe en el estándar de salida muestra sus datos en el terminal. Cualquier mensaje de

error que generen las rutinas de las bibliotecas se envía al flujo estándar de error, indicando que el

mensaje de error aparece en el terminal de usuario.

Los sistemas operativos actuales consideran el teclado y el visualizador de video como

archivos. Esto, porque el sistema puede leer desde el teclado de la misma forma que lo hace desde

un archivo de disco o de cinta. De forma similar, el sistema puede escribir en un visualizador de

video tal y como lo hace en un archivo de disco o de cinta.

Todos los archivos abiertos que utilizan las funciones de flujo (stdin(), stdout() y stdprn())

por omisión utilizan un buffer excepto para los flujos abiertos previamente stderr y stdaux, estos

flujos, por omisión no utilizan un buffer a menos que se utilicen en la familia de funciones printf() o

scanf(). En este caso, se les asigna un buffer temporal.

Existen ciertas funciones de entrada y salida de caracteres definidas en el estandar ANSI de

C que son suministradas con todos los compiladores de C. Estas funciones ofrecen la entrada y

salida estándar, y se consideran rutinas de alto nivel (en contraste con las rutinas de bajo nivel, que

acceden al hardware de la máquina mas directamente) La E/S en C está implementada a traves de

funciones suministradas por el fabricante mas que como palabras reservadas definidas como parte

del lenguaje.

Las más básicas de todas las funciones de E/S son aquellas de entrada y salida de un único

carácter.

getc() y putc():

La funcion getc() (que realmente es una macro) introduce un carácter desde un flujo de

archivo especificado, de la siguiente forma :

int ic;

ic=getc(stdin);

El carácter de entrada se devuelve en el nombre de la funcion getc() y, a continuación, asigna

el valor devuelto a ic.

La función getc() convierte el entero en un carácter sin signo. Este uso de un carácter sin

signo almacenado como entero garantiza que los valores ASCII superiores a 127 no se representan

como valores negativos. Por tanto, se pueden utilizar los valores negativos para representar

situaciones inusuales como errores y el final de un archivo de entrada.

Aunque pueda parecer extraño un entero en una función de caracteres, el lenguaje C

realmente considera una distinción mínima entre caracteres y enteros. Si se proporciona un carácter

cuando se necesita un entero, el carácter se convertira en un entero.

El complemento de la función getc() es putc(). Ésta, extrae un carácter del flujo de archivo

representado por el puntero de archivo especificado. Para enviar el mismo carácter que se había

leido anteriormente al estándar de salida, se utiliza la instrucción:

putc(ic,stdout);

M. C. Bertha López Azamar Modulo 4 pag. 17

Ejemplo de un programa:

#include <stdio.h>

void main()

{

int ic;

ic=getc(stdin);

putc(ic,stdout);

}

Una observación, la función getc() normalmente utiliza un buffer, lo que significa que

cuando la aplicación solicita un carácter, no se devuelve el control al programa hasta que se haya

introducido un retorno de carro (ENTER) en el flujo del archivo estándar de entrada.

Las dos macros getchar() y putchar() realmente son implementaciones especificas de las

macros getc() y putc(), respectivamente. Siempre estan asociadas con el estándar de entrada (stdin) y

con el estándar de salida (stdout).

getchar() y putchar():

getchar() .- Mediante esta función se puede conseguir la entrada de caracteres uno a uno. Esta

función es parte de la biblioteca de C de entrada/salida standard. Devuelve un carácter leído

del dispositivo de entrada standard (típicamente el teclado).

En general una referencia a esta función se escribe: variable_de_carácter = getchar();

putchar().- Se puede visualizar un carácter utilizando esta función de biblioteca. Transmite un

carácter al dispositivo de salida standard (típicamente el monitor).

En general una referencia a esta función se escribe: putchar(variable de carácter);

Ejemplo de un programa:

#include <stdio.h>

void main()

{

int ic;

ic=getchar();

putchar(ic);

}

La funcion putchar() se ha escrito para que devuelva un valor EOF (fin de archivo) siempre

que tenga lugar una condicion de error. Se puede utilizar el siguiente codigo para comprobar una

condicion de error de salida. Debido a que EOF se comprueba en la salida, puede parecer un poco

confuso, aunque es técnicamente correcto.

If(putchar(ic) == EOF)

printf(“Ha ocurrido un error escribiendo en stdout”);

M. C. Bertha López Azamar Modulo 4 pag. 18

scanf().- Esta función se puede utilizar para la introducción de cualquier combinación de valores

numéricos o caracteres.

En términos generales la función scanf se escribe: scanf(cadena de control, arg1,arg2,...,argn);

Donde cadena de control hace referencia a una cadena de caracteres que contiene cierta

información sobre el formato de los datos y arg1,arg2,...,argn son argumentos que representan los

datos. (En realidad los argumentos representan punteros que indican las direcciones de memoria en

donde se encuentran los datos. ).

En la cadena de control se incluyen grupos de caracteres, uno por cada dato de entrada. Cada

grupo debe comenzar con el signo de porcentaje, que irá seguido, en su forma más sencilla, de un

carácter de conversión que indica el tipo de dato correspondiente.

Carácter de

conversión

Significado

c El dato es un carácter.

d El dato es un entero decimal.

e El dato es un valor en coma flotante.

f El dato es un valor en coma flotante.

g El dato es un valor en coma flotante.

h El dato es un entero corto.

i El dato es un entero decimal, octal o hexadecimal.

o El dato es un entero octal.

s El dato es una cadena de caracteres.

u El dato es un entero decimal sin signo.

x El dato es un entero hexadecimal.

[...] El dato es una cadena de caracteres que puede incluir caracteres de espaciado.

Cada nombre de variable debe ir precedido por un ampersand (&).

Los datos que se introducen deben corresponderse en tipo y orden con los argumentos de la función

scanf.

Ejemplo de utilización de scanf:

int i;

float j;

scanf("%d %f ",&i,&j);

Al introducir dos o más datos, éstos deben ir separados por caracteres de espaciado (el

carácter de nueva línea se considera como un carácter de espaciado).

Los caracteres consecutivos que no sean de espaciado componen un dato. Es posible limitar el

número de los caracteres especificando una longitud máxima para ese dato. El dato puede estar

compuesto por menos caracteres de los que especifique la longitud y no se leerán los caracteres que

estén más allá de dicha longitud. Hay que tener en cuenta que en este caso los caracteres sobrantes

pueden ser interpretados de manera errónea para el siguiente dato. (ya que caen en el buffer).

Ejemplos: scanf("%3d %3d %3d",&a,&b,&c);

Si los datos se introducen: 1 2 3 las asignaciones que se llevan a cabo son: a=1, b=2, c=3

Si se introducen 123 456 789 las asignaciones son: a=123, b=456, c=789

M. C. Bertha López Azamar Modulo 4 pag. 19

Si se introduce 123456789 las asignaciones son: a=123, b=456, c=789

Si se introduce 1234 5678 9 las asignaciones son: a=123, b=4, c=567 y los otros dos

dígitos restantes (8 y 9) se ignoran, a no ser que se lean a continuación en otra sentencia scanf.

La mayoría de las versiones de C permiten que ciertos caracteres de conversión en la cadena

de control sean precedidos por un prefijo de una sola letra. Así, una l se utiliza para indicar un

argumento entero largo con o sin signo. Una h se utiliza para indicar un argumento entero corto con

o sin signo

printf().- Se pueden escribir datos en el dispositivo de salida standard utilizando la función de

biblioteca printf. Es análoga a la función scanf, con la diferencia que su propósito es

visualizar datos en vez de introducirlos.

En general la función printf se escribe: printf(cadena de control, arg1,arg2,...,argn);

En contraste con la función scanf() los argumentos en la función printf() no representan

direcciones de memoria y por tanto no van precedidos de ampersands.

Se puede especificar una longitud mínima anteponiendo al carácter de conversión un entero

sin signo. Si el número de caracteres del dato correspondiente es menor que la longitud especificada,

entonces el dato será precedido por los espacios en blanco necesarios para que se consiga la longitud

especificada. Si el número de caracteres del dato excede la longitud especificada, se visualizará el

dato completo. Esto es justo lo contrario al comportamiento del indicador de longitud en la función

scanf(), que especifica una longitud máxima. También es posible especificar el máximo número de

cifras decimales para un valor de coma flotante. Esta especificación se denomina precisión. La

precisión es un entero sin signo que siempre es precedido por un punto decimal. Un número en

coma flotante se redondeará si se debe recortar para ajustarse a la precisión especificada.

Ejemplo:

#include <stdio.h>

void main()

{

int entero;

double real;

printf(“Introducir un número entero y un numero real: \n”);

scanf(“%d %f”, &entero, &real);

printf(“ %d + %f = %f \n \n”, entero, real, entero + real);

}

gets().- lee una línea de la entrada estándar, stdin, y la almacena en la variable especificada. Esta

variable es un puntero a la cadena de caracteres leída. Su sintaxis es:

char *gets(char *var);

La variable var, contiene todos los caracteres tecleados, excepto el carácter nueva línea (\n),

que es automáticamente reemplazado por el carácter nulo (\0), con el cual C finaliza toda cadena de

caracteres. La función gets() devuelve un puntero al valor leído. Un valor nulo para este puntero,

indica un error o una condición de fin de fichero(eof).

A diferencia de scanf(), gets() permite la entrada de una cadena de caracteres formada por

M. C. Bertha López Azamar Modulo 4 pag. 20

varias palabras separadas por espacios en blanco, sin ningún tipo de formato. Para scanf(), el espacio

en blanco actuá como separador de datos en la entrada.

puts().- escribe una cadena de caracteres en la salida estándar stdout, y reemplaza el carácter nulo

de terminación de la cadena (\0) por el carácter nueva línea (\n). Su sintaxis:

int puts(const char *cadena);

La función puts() retorna un valor positivo si se ejecuta satisfactoriamente; en caso contrario,

retorna el valor EOF.

Ejemplo:

#include <stdio.h>

#include <conio.h>

char linea[81];

char *pc;

void main()

{

printf(“introduce una cadena de caracteres: “);

pc=gets(linea);

printf(“\nLa linea introducida es: \n”);

printf(%s \n”, linea);

puts(“Pulse una tecla para continuar”);

getch();

puts(“\nLa escribo por segunda vez:”);

puts(pc);

}

NOTA: Las funciones scanf(), getchar(), y gets() tienen una característica común: leen los datos

requeridos de la entrada estándar referenciada por stdin. Es necesario tener presente que los

datos, cuando son tecleados, no son leídos directamente del dispositivo, sino que éstos son

depositados en una memoria intermedia (buffer), asociada con el dispositivo de entrada. Los

datos son leídos de la memoria intermedia cuando son validados; esto ocurre cada vez que

pulsamos Enter.

Las funciones scanf() y getchar() interpretan el carácter „\n‟(nueva línea) como un separador.

En modo texto y bajo DOS, pulsar la tecla Enter equivale a trasmitir dos caracteres: CR+LF

(estos en Unís equivalen a un solo carácter:‟\n‟). Lo que significa que el carácter que actua

como separador es CR; quedando en el buffer asociado con stdin, el carácter LF. Este es un

carácter válido para la función gets(), entre otras; lo que ocasiona problemas de lecturas no

deseadas, cuando estas funciones son combinadas en un mismo programa.

La solución es limpiar el buffer asociado con stdin después de una lectura con las funciones

scanf() o con getchar(), y antes de una lectura con la función gets().

Ejemplo:

M. C. Bertha López Azamar Modulo 4 pag. 21

#include <stdio.h>

void main()

{

int entero;

double real;

printf(“Introducir un número entero y un numero real: \n”);

scanf(“%d %f”, &entero, &real);

printf(“ %d + %f = %f \n \n”, entero, real, entero + real);

/*leer 4 cadenas de caracteres*/

for(entero=0; entero<4; entero++)

{

scanf(“%s”, cadena);

printf(“%s \n”, cadena);

}

/*Limpiar el buffer de entrada y leer una cadena con gets*/

fflush(stdin);

printf(“Introducir cadenas para gets(). \n”);

while (respuesta == „s‟ && gets(cadena) != NULL)

{

printf(“%s \n”, cadena);

do

{

printf(“¿Desea continuar (s/n) “);

respuesta=getchar();

}while((respuesta !=‟s‟) && (respuesta !=‟n‟));

fflush(stdin);

}

FUNCIONES PARA LA CONSOLA Y PUERTOS DE E/S

La salida y la entrada de estas funciones pueden ser redireccionadas. La redirección se

efectúa a nivel de sistema operativo.

Las funciones para la consola (específicamente), son propias de MS-DOS y OS/2. Las

funciones utilizadas para la entrada de datos, leen los mismos de la entrada estándar (stdin) y las

funciones utilizadas para la salida de datos, escriben los mismos sobre la salida estándar (stdout).

Las declaraciones para todas estas funciones, están contenidas en el fichero conio.h.

getch() y putch():

Estas son funciones auténticas, pero no entran en el estandar ANSI de C, puesto que son

funciones de bajo nivel estrechamente relacionadas con el hardware. Para PC(computadoras

personales), estas funciones no utilizan buffer, lo que significa que la entrada de un carácter se

realiza en el mismo momento que se escribe en el teclado.

Se puede utilizar getch() y putch() exactamente de la misma forma que getchar() y putchar().

getch().- Lee un carácter del teclado sin que se produzca eco (el caráter no se visualiza en pantalla).

M. C. Bertha López Azamar Modulo 4 pag. 22

No es necesario pulsar ENTER. Normalmente se utiliza para leer las teclas que ignora la

función getchar(), por ejemplo REPAG, AVPAG, INICIO y FIN, ya que no hay conversión

de caracteres: CR+LF no es convertido a „\n‟.

La función getch() devuelve el carácter leído. Si este valor es cero, llamar otra vez a la

función para recuperar el segundo códigoSu sintaxis es: int getch(void);

putch(c).- Escribe directamente en la consola, el carácter c. No hay conversión de caracteres: „\n‟ no

es convertido a CR+LF. La función retorna el carácter c. Si se ejecuta satisfactoriamente y

un valor EOF si ocurre un error. Su sintaxis es: int putch(int c);

Ejemplo: putch(„\n‟) /* avanza de línea sin retorno al principio*/

Existes otras más para consola y otras más para puertos, se queda para investigación personal

según el interés, ya que por el momento quedan fuera del alcance de este curso.

2.1 Manejo de cadenas de caracteres

Cadenas de caracteres

Primero, una “cadena de caracteres" es un array unidimensional, en el cual todos sus

elementos son de tipo char.

En C, una cadena consta de un array de caracteres que termina en un nulo. („\0‟), por lo que

debe siempre declararse arrays de caracteres con un carácter más que el tamaño de la cadena que se

vaya a almacenar.

A diferencia de otros lenguajes de programación que emplean un tipo denominado cadena

string para manipular un conjunto de simbolos, en C, se debe simular mediante un arreglo de

caracteres, en donde la terminación de la cadena se debe indicar con nulo. Un nulo se especifica

como '\0'. Por lo anterior, cuando se declare un arreglo de caracteres se debe considerar un

carácter adicional a la cadena más larga que se vaya a guardar. Por ejemplo, si se quiere declarar un

arreglo cadena que guarde una cadena de diez caracteres, se hará como:

char cadena[11];

Se pueden hacer también inicializaciones de arreglos de caracteres en donde

automáticamente C asigna el caracter nulo al final de la cadena, de la siguiente forma:

char nombre_arr[ ]="cadena";

Por ejemplo, el siguiente fragmento inicializa cadena con ``hola'':

char cadena[5]="hola";

El código anterior es equivalente a:

char cadena[5]={'h','o','l','a','\0'};

Para asignar la entrada estándar a una cadena se puede usar la función scanf con la opción

%s (observar que no se requiere usar el operador &, pero, esto debe considerarse

M. C. Bertha López Azamar Modulo 4 pag. 23

con el compilador ), de igual forma para mostrarlo en la salida estándar.

Por ejemplo:

#include <stdio.h>

void main()

{

char nombre[15], apellidos[30];

printf("Introduce tu nombre: ");

scanf("%s",nombre);

printf("Introduce tus apellidos: ");

scanf("%s",apellidos);

printf("Usted es %s %s\n",nombre,apellidos);

}

El lenguaje C no maneja cadenas de caracteres, como se hace con enteros o flotantes, por lo

que lo siguiente no es válido:

void main()

{

char nombre[40], apellidos[40], completo[80];

nombre="José María"; /* Ilegal */

apellidos="Morelos y Pavón"; /* Ilegal */

completo="Gral."+nombre+appellidos; /* Ilegal */

}

Funciones para manipular cadenas de caracteres

Las declaraciones de las funciones, para manipular cadenas de caracteres, que a continuación

se describen, están en el fichero a incluir string.h

strcat(cadena1, cadena2).- esta función añade la cadena2 a la cadena1, termina la cadena resultante

con el carácter nulo y devuelve un puntero a cadena1. Su sintaxis:

char *strcat(char *cadena1, const char *cadena2);

Ejemplo:

char cadena1[80];

char *r;

r = strcat(cadena, “def”);

printf(“%s \n”, r); // escribe: def

strchr(cadena1, cadena2).- esta función devuelve un puntero a la primera ocurrencia de c en

cadena o un valor NULL si el carácter no es encontrado. El carácter c puede ser un carácter

nulo(„\n‟). Su sintaxis es:

char *strchr(const char *cadena, int c);

Ejemplo:

char cadena1[80];

M. C. Bertha López Azamar Modulo 4 pag. 24

char *r;

r=strchr(cadena1,‟e‟);

printf(“%s \n”, r); // escribe: ef

strcmp(cadena1, cadena2).- Esta función compara la cadena1 con la cadena2 y devuelve un valor.

<0 si la cadena1 es menor que la cadena2,

=0 si la cadena1 es igual a la cadena2, y

>0 si la cadena1 es mayor a la cadena2.

Su sintaxis es: int strcmp(const char *cadena, const char *cadena2);

Ejemplo:

char cadena1[80]=”abcdef”;

static char cadena2[80]=”xyz”;

int n;

n=strcmp(cadena1,cadena2);

printf(“ \” %s \” es %s \” %s”\” \n”, cadena1, n?(n>0? “mayor que” : “menor que”) :

“igual a”, cadena 2); // observación todo va en una sola línea

/*lo que escribe es “abcdef” es menor que “xyz” */

strcpy(cadena1, cadena2).- Esta función copia la cadena2, incluyendo el carácter de terminación

nulo, en la cadena1 y devuelve un puntero a cadena1. Su sintaxis es:

char *strcpy(char *cadena1, const char *cadena2);

Ejemplo:

char cadena1[80];

strcpy(cadena1, “abc”);

strlen(cadena).- Esta función devuelve la longitud en bytes de cadena, no incluyendo el carácter de

terminación nulo. Su sintaxis es:

size_t strlen(char *cadena);

Ejemplo:

char cadena1[80]=”abcdef”;

printf(“El tamaño de cadena 1 es %d \n”, strlen(cadena1));

/* escribe: El tamaño de cadena2 es 6

Estas son solo algunas funciones para manipular cadenas de caracteres.

Algunas otras de uso común para conversión de datos, pertenecientes a stdlib.h son:

atof(cadena).- Convierte una cadena de caracteres a un valor en doble precisión.

Sintaxis : double atof(const char *cadena);

atoi(cadena).- Convierte una cadena de caracteres a un valor entero.

M. C. Bertha López Azamar Modulo 4 pag. 25

Sintaxis: int atoi(const char *cadena);

atol(cadena).- convierte una cadena de caracteres a un valor entero largo.

Sintaxis: long atol(const char *cadena);

fcvt(valor, decs, pdec, signo).- convierte un número real a una cadena de caracteres. La cadena de

caracteres será finalizada con el carácter nulo. Devuelve un puntero a la cadena de

caracteres. Los argumentos significan:

valor.- es el número real a convertir

decs.- número de digitos después del punto decimal. Si es necesario, se añaden ceros.

pdec.- devuelve un puntero a un valor entero que especifica la posición del punto decimal.

signo.- devuelve un puntero a un valor 0, si el número es positivo, o un valor distinto de 0, si

el número es negativo.

Sintaxis: char fcvt(doble valor, int decs, int *pdec, int ¨signo);

itoa(valor, cadena, base) .- (solamente en MSDOS) convierte un valor entero a una cadena

de caracteres. La cadena de caracteres será finalizada con el carácter nulo. Esta función

devuelve un puntero a la cadena de caracteres.

valor.- es el número entero a convertir.

cadena.- es la cadena de caracteres resultante.

base.- es la base(2 – 36) en la que viene especificado el valor. Si la base es 10 y el valor

negativo, el primer carácter almacenado en cadena es el signo menos.

Sintaxis: int itoa(int valor, char *cadena, int base);

ltoa(valor, cadena, base) .- (solamente en MSDOS) convierte un valor entero en formato

largo a una cadena de caracteres. La cadena de caracteres será finalizada con el carácter nulo.

Esta función devuelve un puntero a la cadena de caracteres.

valor.- es el número entero a convertir.

cadena.- es la cadena de caracteres resultante.

base.- es la base(2 – 36) en la que viene especificado el valor. Si la base es 10 y el valor

negativo, el primer carácter almacenado en cadena es el signo menos.

Sintaxis: int ltoa(int valor, char *cadena, int base);

Algunas otras de uso común para clasificación y conversión de caracteres, pertenecientes a

ctype.h son:

isalnum(c).- comprueba si c es un carácter perteneciente a: „A‟ – „Z‟, „a‟ – „z‟ o „0‟ – „9‟.

Sintaxis: int isalnum(int c);

M. C. Bertha López Azamar Modulo 4 pag. 26

isalpha(c).- comprueba si c es un carácter perteneciente a: „A‟ – „Z‟, „a‟ – „z‟

Sintaxis: int isapha(int c);

isascii(c).- comprueba si c es un carácter ASCII (0 a 127).

Sintaxis: int isaccii(int c);

isdigit(c).- comprueba si c es un digito („0‟ – „9‟).

Sintaxis: int isdigit(int c);

islower(c).- comprueba si c es una letra minuscule („a‟ – „z‟).

Sintaxis: int islowe(int c);

isspace(c).- comprueba si c es un carácter espacio en blanco (9 – 13 o 32)

Sintaxis: int isspace(int c);

isupper(c).- comprueba si c es una letra mayúscula(„A‟ – „Z‟).

Sintaxis: int isupper(int c);

tolower(c).- convierte c a una letra minúscula, si procede.

Sintaxis: int tolower(int c);

toupper(c).- convierte c a una letra mayúscula, si procede.

Sintaxis: int toupper (int c);

M. C. Bertha López Azamar Modulo 4 pag. 27

PUNTEROS

Un tema adicional y muy ligado al uso de arreglos y paso de parámetros a funciones, es el

uso de punteros.

En C existe una relación tal entre punteros y arreglos, que cualquier operación que se pueda

realizar mediante la indexación de un array, se puede realizar también con punteros.

Veamos que son los punteros.

Un puntero, es una variable que contiene la dirección de memoria, de un dato o de otra

variable que contiene al dato. (El puntero apunta al espacio físico donde está el dato o la variable).

Un puntero puede apuntar a un objeto de cualquier tipo, incluyendo estructuras, funciones,

etc.

Los punteros se pueden utilizar para:

Crear y manipular estructuras de datos,

asignar memoria dinámicamente, y

proveer el paso de argumentos por referencia en las llamadas a funciones.

Un puntero se declara precediendo el identificador que referencia al puntero, por el operador

de indirección ( * ), el cual significa “puntero a”.

Un puntero no inicializado tiene un valor desconocido, y se convierte en un elemento muy

peligroso, porque puede contener basura y apuntar a un lugar donde hay datos de algun programa o

del mismo sistema operativo, y causar errores e inclusive volver inestable el sistema.

La declaración de un puntero tiene la siguiente sintaxis:

tipo *var_puntero;

donde:

tipo.- especifica el tipo de objeto apuntador, puede ser cualquier tipo, incluyendo tipos

agregados.

var_puntero.- nombre de la variable.

Al hablar de puntero, se distinguen dos operadores:

- operador dirección de: & .- el cual devuelve como resultado la dirección de su operador.

- operador de indirección: *.- toma su operando como una dirección y nos da como

resultado su contenido.

Operaciones con punteros:

Operación de asignación.- Un puntero puede asignarse a otro puntero.

Ejemplo:

int a= 10, *p, *q;

p=&a;

M. C. Bertha López Azamar Modulo 4 pag. 28

q=p;

Operaciones aritméticas.- A un puntero se le puede sumar o restar un entero.

Ejemplo:

int *p; /*declara p como puntero a un entero*/

p++; /*hace que p apunte al siguiente entero*/

p--; /*hace que p apunte al entero anterior*/

p = p + 3; /*avanza tres enteros*/

p = p – 3; /*retrocede tres enteros*/

Si p y q son variables tipo puntero y apuntan a elementos del mismo array, la operación p-q

es válida. No se permite sumar, multiplicar, dividir o rotar punteros y tampoco sumarles un real.

(porque estamos manejando direcciones de memoria, y no valores cualquiera).

PUNTEROS y ARREGLOS

En C existe una relación tal entre punteros y arreglos, que cualquier operación que se pueda

realizar mediante la indexación de un array, se puede realizar también con punteros.

El siguiente ejemplo muestra la comparativa con el uso de array y del uso de punteros:

/*con arreglos*/

#include <stdio.h>

void main()

{

static int lista[ ]={24, 30, 15, 45, 34];

int ind;

for(ind=0; ind <5; ind++)

printf(“%d”, lista[ind]);

}

/*con punteros

#include <stdio.h>

void main()

{

static int lista[ ]={24, 30, 15, 45, 34];

int ind;

for(ind=0; ind <5; ind++)

printf(“%d”, *(lista[ +ind));

}

La diferencia radica en la expresión para acceder a los elementos del arreglo:

lista[ind] ó *(lista + ind)

lo que es:

arreglo[indice] ó *(arreglo + indice)

Debemos considerar que en si, el nombre de la lista, es la dirección de

comienzo del arreglo, (lo que es lo mismo, la dirección de comienzo del arreglo es la misma

que la dirección del primer elemento del arreglo), entonces, si a la dirección del arreglo le sumamos

un valor, lo que hacemos es un desplazamiento, a una posición determinada dentro del arreglo(esto,

mientras que se vigile el no rebasar los limites del arreglo).

Algo que debe observarse y tener presente es que, el nombre de un array es una constante y

un puntero es una variable. Esto quiere decie que las operaciones:

lista = p ó lista= lista++ no son correctas, y las operaciones:

Programación Estructurada

M. C. Bertha López Azamar

p=lista ó p=p++ si son correctas

En el caso de las cadenas de caracteres, se manejan indiferentemente como arrays, y se puede

aplicar lo expuesto para los arrays. Particularmente, puede mencionarse que para hacer referencia a

una cadena de caracteres, podemos utilizar un puntero o un array:

char *nombre = “Benito Juárez”; /*utilizando un puntero*/

char nombre[ ] = “Benito Juárez”; /*utilizando un arreglo*/