Curso: "Como programar en C en muchos pasos" by El_Max_5 (Parte 1)

Publicado el 16/08/2023 12:08:00 en Programación General.
Author: [x][x] l0ve | Total de votos: 5   Vote



Gente les dejo este espectacular curso de C, que no es de mi autoría .. con la cual reconosco yo aprendí C hace algúnos años .. Y se llama "Como programar en C en muchos pasos".



Aclaración: Este curso tiene como objetivo aprender C, pero si quieren realmente darle mano dura al C y C++ entonces recomiendo "Pensando en C++" pero advertencia para tomar este último se requiere experiencia previa o entendimiento de como es al menos el lenguaje C, pero lo dejo acá como segundo paso ya que es el que más explica C++ de una forma eficiente, eficaz y completa. De todos los cursos de C++ que pude tener oportunidad de leer este sería el que YO recomiendo.


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

08:. - [ Como programar en C en muchos pasos ] [El_Max_5] :.
[[email protected]]:.


Ante todo soy El_Max_5 y quiero agradecer al editor de esta Revista por
publicar este curso de programacion; Este curso lo escribi hace tiempo
como guia de usuario para estudiantes Universitarios y ha funcionado a
las mil maravillas. Si tienes alguna duda o sugerencia puedes escribirme
a mi email (a veces trardo un poco para constestar porque me la paso
OFF-LINE la mayoria de mi tiempo y muy poco reviso mi correo, Ok).


NOTA: Los Ejemplos que tomaremos son para Borland o Turbo C y puede ser
que muchas de las funciones y librerias no sean compatibles con Linux.


Introduccion
------------

C es un lenguaje de programacion de empleo general, caracterizado por ser
conciso y poseer un moderno flujo de control y estructuras de datos, asi
como un rico conjunto de operadores.

El C es un lenguaje de nivel medio, lo que implica la combinacion de
caracteristicas de lenguaje de bajo nivel - ensambladores - con elementos
propios de lenguaje de alto nivel como BASIC o PASCAL.

------------------------------------------
| Alto Nivel | Medio Nivel | Bajo Nivel |
------------------------------------------
| ADA | C | EMSAMBLADOR |
| BASIC | FORTH | |
| COBOL | | |
| FORTRAN | | |
| PASCAL | | |
------------------------------------------


Un lenguaje de medio nivel proporciona a los programadores un conjunto minimo
de sentencias de control y de manipulacion de datos, que pueden usarse para
definir construcciones de alto nivel. Esto significa que el programador
debera definir todas las funciones (rutinas), ya que ninguna va previamente
incorporada.

Se dise~an, asi, librerias de funciones C que se adaptan a las tareas
especificas de un programa.

Entre las ventajas del C se destacan:

- El C posee solamente <b>33 palabras clave</b>;
- El C es <b>portable</b>.


El C se usa para escribir:

- Sistemas Operativos
- Compiladores de lenguajes
- Ensambladores
- Procesadores de palabras
- Integracion E/S
- Controladores de red
- Programas de modem
- Bases de datos
- Interpretes de lenguajes
- Utilidades
- Etc...


El C es un lenguaje estructurado al igual que Pascal y Ada mientras que el
BASIC, COBOL, FORTRAN no lo son.

La caracteristica basica de un lenguaje estructurado es el uso de bloques. Un
bloque es un conjunto de sentencias que estan relacionadas logicamente.

Un lenguaje estructurado se caracteriza por:

- Soportar el concepto de subrutinas con variables locales y construcciones
de bucles.
- No se recomienda el uso de goto (aun cuando no esta prohibido).
- Permitir compilar por separado a las subrutinas.
- Indentar las sentencias.


Para finalizar esta Introduccion, deben introducir las nociones de
interpretes y compiladores.

Estos terminos se refieren a la forma en que un programa es ejecutado. Los
interpretes y compiladores son programas que operan sobre el codigo fuente
de su programa. El codigo fuente es el texto del programa que uno escribe.

Un interprete lee el codigo fuente del programa linea a linea, llevando a
cabo cada vez las instrucciones especificas contenidas en la linea.

Un compilador lee el programa entero y lo convierte en codigo objeto (o
codigo binario, o codigo maquina) directamente ejecutable por la computadora.

BASIC normalmente es interpretado, el C casi siempre es compilado.

El proceso de compilacion lleva tiempo pero produce un programa directamente
ejecutable que se ejecuta mucho mas rapidamente que un programa interpretado.

Con los programas compilados se introducen, entonces, dos conceptos nuevos,
a saber "tiempo de compilacion" y "tiempo de ejecucion" que, por desgracia,
se encuentran muy a menudo asociado a la palabra error: "compile-time
errors" y "run-time errors".


Empecemos....



I.- Manejo de Datos en C
--------------------

1.- Anatomia de un programa
-----------------------

Sea el programa siguiente:

Ejemplo I.1:

1: #include <stdio.h>
2:
3: main()
4: {
5: printf("Bienvenidos al C!/n");
6: return 0;
7: }


La linea 1 es una sentencia include que indica al compilador leer otro
archivo fuente. Las llaves "<>" indican al compilador buscar stdio.h en un
directorio-subdirectorio preconfigurado en el autoexec.bat. Si se usa las
comillas, por ejemplo "otrofichero.h" se indica al compilador buscar en el
subdirectorio de trabajo. El archivo stdio.h se denomina cabecera
("header"), razon por la cual su extension es .h, es porque contiene varias
declaraciones de entrada-salida que usa el programa. Otros archivos de
cabecera contienen otros tipos de declaraciones que veremos en el transcurso
de esta revista.

Si el compilador no encuentra el archivo mencionado entre comillas, te
tratara como si estuviera delimitado por <> (buscara en los subdirectorios
Include).

Los nombres de archivos pueden escribirse en minusculas o mayusculas; la
sentencia # include debe escribirse en minusculas. Los nombres de archivos
pueden incluir el camino para llegarles, por ejemplo:

# include "C:/midir/miarchivo.inc"

La linea 2 es una linea vacia. Este tipo de linea es importante para
delimitar las diferentes secciones que constituyen el programa.

Las llaves de la linea 4 y 7 abren y cierran un bloque constituido por
sentencias y permiten indicar al compilador que queremos que considere a
este grupo de sentencias como una entidad. Este tipo de bloque se denomina
tambien sentencia compuesta. En el ejemplo tenemos dos sentencias. La
primera llama a una funcion de salida formateada printf() que escribe en la
pantalla el texto entre comillas. Esta linea termina con /n, lo que provoca
un salto de linea al terminarse de escribirse la frase.

La linea 6 es una sentencia return. Termina el programa y regresa al DOS.
Usualmente un valor diferente de cero significa que se han producido uno o
varios errores.

Hasta el momento no hemos hablado de la linea 3. Esta linea es una de las mas
importantes de cualquier programa escrito en C y se explicara en el parrafo
siguiente.


a.- La funcion main()
-----------------

En C los programas se ejecutan de manera lineal. Empiezan en una sentencia,
ejecutan las que siguen y, solo si cae un rayo y/o ocurre un corto de
energia, terminan con la sentencia y en el tiempo planificados.

Todos los programas empiezan en el mismo sitio: la primera sentencia de una
funcion que se ha denominado main() al escribir el programa (si no exista
tal funcion el compilador generara un mensaje de error).

Uno de las formas que puede tomar el mas peque~o programa escrito en C es la
siguiente:

Ejemplo I.2

1: main()
2: {
3: return 0;
4: }


La funcion empieza por su nombre, seguido de un par de parentesis. Si la
funcion no tiene parametros estos parentesis no contendran nada.

A continuacion viene el bloque que define la funcion. En este figuran las
sentencias (una en este ejemplo), todas terminadas por un ";" (para que el
compilador sepa donde terminan).


b.- Mensajes de errores
-------------------

El compilador da dos tipos de mensajes:

- De errores: en este caso el compilador no puede llevar a cabo su trabajo y
Deben corregirse obligatoriamente los errores;
- De advertencias: son llamadas de atencion sobre problemas que podrian
eventualmente impedir que el programa se ejecute o que conducirian a
resultados imprevisibles. Al recibir tales mensajes, es preferible
corregir la causa lo mas rapidamente posible.


El programa siguiente (ejemplo I.3) tiene varios errores de tipeo. Al
compilarlo (con CTRL + F9, o con F8 en el modo Trace) se generan varios
mensajes.


1: include <stdio.h)
2:
3: main{}
4: (
5: printf("Problemas, problemas/n");
6: printf("Mas problemas!/n);
7: printf("___Errores Voluntarios")
8: )


Al corregir los errores, la compilacion no generara mensajes y se podra ver
el resultado en la pantalla de salida (con ALT + F5).


2.- Comentarios
-----------

Los comentarios son ignorados por el compilador. Nos permiten aclarar algunos
aspectos del programa.

Los comentarios se encierran entre los simbolos /* y */; por ejemplo:

/* Esto es un comentario */

/* Esto es un comentario
que ocupa
varias lineas
*/


3.- Tipos de variables y datos
--------------------------

Una variable es un nombre asociado a un espacio de memoria que se reserva
para almacenar un valor.

Antes de usar la variable, esta debe declararse. Esta regla ayuda al
compilador a terminar su tarea rapidamente y obliga al programador a
planificar cuidadosamente su trabajo. En el listado siguiente (ejemplo I.4)
se muestra como declarar, inicializar y utilizar una variable entera. Un
entero se simboliza por int.


1: #include <stdio.h>
2:
3: main()
4: {
5: int cuanto;
6:
7: cuanto = 1234;
8: printf("Cuanto = %d/n", cuanto);
9: return 0;
10 }



La linea 5 declara la variable llamada cuanto: int es el tipo de dato,
cuanto es el identificador.

Por convencion, se separa por una linea blanca la seccion donde se declaran
las variables de las otras sentencias del programa.

La linea 7 asigna a la variable cuanto el valor de 1234. Al ejecutar esta
sentencia, el programa almacena una representacion binaria de 1234 en el
espacio de memoria asignada a la variable cuanto.

La linea 8 muestra cuanto. El argumento %d indica a la funcion printf()
donde colocar el valor de la variable cuyo nombre se ha colocado despues de
la coma. Al ejecutarse esta linea, printf() convierte el valor binario de
cuanto a caracteres, coloca estos caracteres en el sitio donde esta ubicado
el argumento %d y manda el resultado al terminal. Ya sabemos que /n provoca
una salto de linea en el terminal.

El listado siguiente (ejemplo I.5) declara y muestra un cierto numero de
variables de diferentes tipos.

1: #include <stdio.h>
2:
3: main()
4: {
5: char slash = '/';
6: short mes = 4;
7: int an = 2001;
8: long poblacion = 308700000L;
9: float pi = 3.14159;
10: double velocidad = 186281.7;
11: long double anioLuz = 5.88e12;
12:
13: printf("Fecha = %02d%c%d/n", mes, slash, an);
14: printf("Poblacion = %ld/n", poblacion);
15: printf("Pi = %f/n", pi);
16: printf("Velocidad de la luz =%12.2f miles/sec/n", velocidad);
17: printf("Un a~o luz = %.0Lf miles/n", anioLuz);
18: return 0;
19: }


A continuacion definiremos los diferentes tipos de variables. Para mostrar el
valor de estas ultimas en este programa, se usa en printf() varios argumentos
(%d, %c, %f, etc..) que se definiran a continuacion.

La linea 7 muestra otra manera de declarar y inicializar una variable.

Todas las variables deben inicializarse. De lo contrario, se obtendrian
resultados impredecibles ya que las variables podrian contener valores
dejados en la memoria por operaciones previas.


a.- Variables Globales
------------------

En el ejemplo anterior, las variables se declaran en el bloque de la funcion
main() y son locales. Sin embargo, las variables pueden declararse afuera de
main(). En este ultimo caso las variables seran globales.

Contrariamente a las variables locales, las variables globales se
preinicializan en cero. El listado siguiente (ejemplo I.6) es identico al
del ejemplo I.4 pero declara cuanto como una variable global.

1 #include <stdio.h>
2
3 int cuanto;
4
5 main()
6 {
7 cuanto = 1234;
8 printf("Valor = %d/n", cuanto);
9 return 0;
10 }



En este caso, el compilador almacena la variable en la misma area de memoria
que la del programa.

Al ejecutarse este ultimo, el codigo de arranque coloca cada byte de esta
area de memoria en 0.

Si se elimina la linea 7, cuanto tendria un valor de 0.


El ejemplo I.7 ilustra la diferencia entre variables globales y locales.

1 #include <stdio.h>
2
3 int global = 100;
4 int globalDefault;
5
6 main()
7 {
8 int local = 200;
9 int localDefault;
10
11 printf("global = %d/n", global);
12 printf("local = %d/n", local);
13
14 printf("globalDefault = %d/n", globalDefault);
15 printf("localDefault = %d/n", localDefault);
16 return 0;
17 }


b.- Palabras Clave
--------------

Son simbolos internos del C que cuando se incluyen en su sintaxis formal
forman el lenguaje de programacion. Son palabras reservadas que no pueden
utilizarse con un objetivo diferente al que se ha destinado. Deben escribirse
en minusculas.

ANSI C
------------------------------------------------------------
| Asm | default | for | short | union |
| auto | do | goto | signed | unsigned |
| break | double | if | sizeof | void |
| case | else | int | static | volatile |
| char | enum | long | struct | while |
| const | extern | register | switch | |
| continue | float | return | typedef | |
------------------------------------------------------------

Ademas, la Borland amplio este listado agregando 19 palabras clave
suplementarias:

-------------------------------------------------------
| _cdecl | _es | _fastcall | _near | _saveregs |
| cdecl | _export | huge | near | _seg |
| _cs | _far | interrupt | _pascal | _ss |
| _ds | far | _loadds | pascal | |
-------------------------------------------------------


c.- Identificadores
---------------

Son palabras que identifican las variables y funciones que dise~amos.
Deben empezar por un caracter y pueden contener letras, numeros y el caracter
subrayado.

Los identificadores deben tener los 32 primeros caracteres distintos.

Un mismo nombre escrito en mayusculas o en minusculas corresponde a dos
variables diferentes:

Mivar, MiVar y MIVAR corresponden a tres variables diferentes.

Generalmente, los identificadores de variables y las funciones de la libreria
estandar comienzan por una minuscula. Las palabras clave se escriben siempre
en minuscula.

Estos son convenciones y no reglas; pero si todo el mundo las utiliza se
obtiene una mejor organizacion de los programas y se entiende mejor el codigo
escrito por los demas programadores.

Para mejor claridad se separan las palabras por el caracter de subrayado:

velocidad_de_la_luz

o se utilizan mayusculas:

velocidadDelaLuz.



d.- Enteros
-------

Los datos tipo int ocupan 2 bytes y tienen valores entre - 32768 y 32767
(incluyendo el 0). Corresponden a numeros enteros.

Cuando se necesita un rango de valores mas grande se usa el tipo long int que
corresponde al rango - 2 147 483 648 a 2 147 483 647.

La declaracion de variable se hace segun:

long int valorElevado;

que puede abreviarse segun:

long valorElevado;

Tambien existe el tipo short int:

short int valorPeque~o;

que se abrevia:

short valorPeque~o



Sin embargo, en Borland C, un short int es identico al int (el ANSI C no
especifica nada y esto depende del compilador usado). Para los enteros muy
peque~os se usa el tipo char que tambien puede representar un caracter.

Una variable tipo char ocupa un byte y representa valores entre -128 y +127
(incluyendo el 0).



d.1 Variables signed y unsigned
---------------------------

Todos los enteros char, short, int y long tienen un signo por default. Es
decir que pueden ser positivos o negativos. Podria usarse la palabra clave
signed para estos valores. Sin embargo, resulta inutil ya que int significa
lo mismo que signed int.


Si no se necesita los enteros negativos se puede usar unsigned en la
declaracion:

unsigned char uc;
unsigned short si;
unsigned int ui;
unsigned long li;

Ya que los enteros unsigned no pueden representar numeros negativos, ellos
representan el doble del anterior en los numeros positivos (incluyendo el 0)
como indicado en la tabla siguiente:

------------------------------------------------------------------------------
| Tipo | tam. en bytes | tam. en bits | valor minimo | valor maximo |
------------------------------------------------------------------------------
| signed char | 1 | 8 | -128 | 127 |
| unsigned char | 1 | 8 | 0 | 255 |
| signed short | 2 | 16 | -32768 | 32767 |
| unsigned short| 2 | 16 | 0 | 65535 |
| signed int | 2 | 16 | - 32768 | 32767 |
| unsigned int | 2 | 16 | 0 | 65535 |
| signed long | 4 | 32 | -2147483648 | 2147483647 |
| unsigned long | 4 | 32 | 0 | 4294967295 |
------------------------------------------------------------------------------


La libreria estandar limits.h indica el rango exacto de los datos de tipos
enteros.

En la expresion:

j = i + 60000;

si i es del tipo unsigned int y es igual a 15000, entonces:

j = 9464



d.2 Declaracion multiple de variables
---------------------------------

Para declarar varias variables del mismo tipo puede usarse:

int v1; int v2; int v3; int v4;

o

int v1, v2, v3, v4;



d.3 Valores Verdadero y Falso
-------------------------

En C, falso es equivalente a 0 y verdadero es equivalente a cualquier valor
entero diferente de 0.



d.4 Constantes
----------

Son valores fijos que el programa no altera. Generalmente el compilador
utiliza el tipo de datos que permiten almacenar el valor en el espacio mas
peque~o posible.

Cuando es necesario, se puede obligar al compilador a usar un tipo de datos
especifico. Por ejemplo, la constante 1234 normalmente se almacena como int.
Sin embargo, si queremos que se almacene como long se usa L despues del
ultimo numero:

long valor = 1234L;

si queremos un valor unsigned, se usa U:

1234U se almacena como unsigned int, 1234UL como unsigned long.

Ademas se puede usar el sistema octal o hexadecimal. Una constante que
empieza por 0 se considera como expresada en base 8, una que empieza por 0x
como expresada en base 16.

Ejemplo 1.8

1 #include <stdio.h>
2
3 main()
4 {
5 int hexValor = 0xf9ac;
6 int octalValor = 0724;
7 int decimalValor = 255;
8
9 printf("Como enteros en base 10:/n");
10 printf(" hexValor = %d/n", hexValor);
11 printf(" octalValor = %d/n", octalValor);
12 printf(" decimalValor = %d/n", decimalValor);
13
14 printf("/nComo enteros formateados:/n");
15 printf(" hexValor = %x/n", hexValor);
16 printf(" octalValor = %o/n", octalValor);
17 printf(" decimalValor = %#x/n", decimalValor);
18 return 0;
19 }

Formateando los valores hexadecimales con %d en printf() (lineas 10-12) se
obtiene sus valores equivalentes en base 10.

Para obtener los valores hexadecimales debe usarse %x (o %X para obtener A..
...F). Con #x o #X se obtiene los valores hexadecimales de las constantes
decimales. Para obtener los valores en el sistema octal se usa %o.


d.5 La palabra clave const
----------------------

Para almacenar un valor en una variable de tipo int se usa:


int valor = 1234;

Al escribir:

valor = 4321;

cambiamos el valor de la variable. Si la variable nuevoValor es tambien del
tipo int:

valor = nuevoValor;

asigna a valor el contenido de nuevoValor.

Para evitarlo se usa la palabra clave const:

const int calor = 1234;

Al escribir valor = nuevoValor se genera un mensaje de error.


e.- Reales
------

Existen tres tipos: float, double y long double


----------------------------------------------------------------------------
| Tipo | tam. en bits | tam. en bytes | valor minimo | valor maximo |
----------------------------------------------------------------------------
| float | 4 | 32 | 34 10-38 | 34 10+38 |
| double | 8 | 64 | 1.7 10-308 | 1.7 10 +308 |
| long double | 10 | 80 | 3.4 10-4932 | 3.4 10+4932 |
----------------------------------------------------------------------------

(consultar float.h) or man ... jejeje


Se declaran de la misma manera que las variables int:

float saldo;
double saldo = 525.49;

Por default, las constantes, tal como 525.49 o 99.99 son del tipo double,
para que sea del tipo float hay que agregarle f o F:

3.14159F

o para el tipo long double:

3.14159L

En el ejemplo I.9 se calcula el impuesto a la venta y el precio de venta de
un item a partir del precio de compra y del % de impuesto.

1 #include <stdlib.h>
2 #include <stdio.h>
3
4 main()
5 {
6 double precioLista, pagado, porcien, impuesto;
7 char s[20];
8 printf("Cuanto pago ? ");
9 scanf("%20s", s);
10 pagado = atof(s);
11 printf("Porcentaje de Impuesto (ej: .06)? ");
12 scanf("%20s", s);
13 porcien = atof(s);
14 precioLista = pagado * (1 + porcien);
15 impuesto = precioLista- pagado;
16 printf("Precio de venta = Bs.%8.2f/n", precioLista);
17 printf("IVA = Bs.%8.2f/n", impuesto);
18 return 0;
19 }


La linea 6 declara cuatro variables del tipo float; la linea 7 declara una
cadena de caracteres (string); la 9 pregunta por el precio de compra que la
linea 10 lea en la cadena s. La sentencia

scanf("%20s",s);

espera que el usuario tipee hasta 20 caracteres. Esta funcion se declara en
stdio.h

char s(20) reserva espacio para 19 caracteres mas un byte para un cero
terminal el cual indica la terminacion de la cadena.

La linea 11 llama a la funcion atof(), declarada en stdlib.h para convertir
el string a un valor real de tipo double.



f.- Caracteres (char)
-----------------

Se almacenan en 1 byte. Ya vimos como se usa el tipo char para almacenar
enteros entre -128 y + 127, pero una declaracion del tipo:

char c= 'A';

asigna a la variable c el valor ASCII de la letra A (ojo: comilla sencilla).

El ejemplo 1.10 muestra el valor ASCII de los caracteres en base 10 y 16.

1 #include <stdio.h>
2
3 main()
4 {
5 char c;
6
7 printf("Tipear el caracter: ");
8 c = getchar();
9 printf(" Caracter = %c/n", c);
10 printf(" ASCII (dec) = %d/n", c);
11 printf(" ASCII (hex) = %#x/n", c);
12 return 0;
13 }


En la linea 5 declara la variable, tipo char, c
En la linea 8 llama a la funcion getchar() que lee un caracter introducido
por teclado. La funcion espera hasta que se presione Enter.

printf() muestra c, como caracter, decimal y hexadecimal.
Ya que los caracteres se almacenan como enteros, este programa funciona muy
bien con int c en la linea 5.


f.1 Cadenas de caracteres como constantes.
--------------------------------------

Estas constantes corresponden a una o mas palabras entre comillas. "Que dia
tan bonito" se almacena como una serie de valores tipo char que termina por
un byte en cero. Este byte en cero permite a las funciones detectar la
terminacion de la cadena.

Generalmente, se escribe estas constantes en una sola linea. Sin embargo,
Borland C permite escribir las frases muy largas en varias lineas:

"Esto parece constituir"
"tres cadenas,"
"pero en realidad es una sola"

Ademas de caracteres, las cadenas pueden contener secuencias de escape que
son simbolos especiales que no pueden tipearse directamente.

------------------------------------------------------------------------------
| Codigo | Significado | Valor ASCII Decimal | Hexadecimal | Simbolo|
------------------------------------------------------------------------------
| '/a' | alerta | 7 | 0x07 | BEL |
| '/b' | retroceso | 8 | 0x08 | BS |
| '/f' | salto de pagina | 12 | 0x0C | FF |
| '/n' | salto de linea | 10 | 0x0a | LF |
| '/r' | retorno de carro | 13 | 0x0d | CR |
| '/t' | tabulacion horizontal| 9 | 0x09 | HT |
| '/v' | tabulacion vertical | 11 | 0x0b | VT |
| '//' | slash | 92 | 0x5c | / |
| '/'' | comilla simple | 39 | 0x27 | ' |
| '/"' | comilla doble | 34 | 0x22 | " |
| '/?' | | 63 | 0x3f | ? |
------------------------------------------------------------------------------


Para crear una cadena de caracteres se declara:
char cadena[80];

Se lee la cadena introducida por teclado con:
scanf("%80s",cadena);

El problema de scanf() es que termina la captacion al primer espacio en
blanco o al presionar Enter.

Para leer cadenas con espacio en blanco se usa:
gets(cadena);

Esta funcion puede aceptar mas caracteres que los previstos en la declaracion
de la variable, sobreescribiendose zonas de memorias no asociadas a dicha
variables. Para evitarlo, se declara:

char cadena[128];

ya que el buffer estandar del DOS es de 128 caracteres, los usuarios no
pueden entrar cadenas mas largas.

Las cadenas de caracteres son muy utiles para recibir los datos introducidos
a traves del teclado. Pero cuando el dato es numerico, el programa debera
convertir la cadena a entero o real. Existen para ello tres funciones:

atof() (¡se obtiene un valor double!)
atoi() (se obtiene un entero int)
atol() (se obtiene un entero long)


Ejemplos 1.11 y 1.12


#include <stdio.h>
#include <stdlib.h>

main()
{
int valor;
char cadena[128];

printf("Tipear el valor: ");
gets(cadena);
valor = atoi(cadena);
printf("Valor en base 10 = %d/n", valor);
printf("Valor en hex = %#x/n", valor);
printf("Valor en base 8 = %o/n", valor);
return 0;
}

#include <stdio.h>
#include <stdlib.h>

main()
{
double millas;
char cadena[128];

printf("Convierte millas a kilometros/n");
printf("šCuantas millas? ");
gets(cadena);
millas = atof(cadena);
printf("Kilometros = %f/n", millas * 1.609344);
return 0;
}



g.- Espacio ocupado por las variables
---------------------------------

Los compiladores de ANSI C pueden almacenar los valores de los enteros y
reales de manera diferente. En vez de suponer que una cierta variable ocupa
un cierto numero de bytes puede usarse el operador sizeof para calcular los
requerimientos necesarios para almacenar una variable.



Ejemplo_I_13

#include <stdio.h>

main()
{
char c;
short s;
int i;
long l;
float f;
double d;
long double ld;

printf("Espacio Ocupado por char ...... = %2d byte(s)/n", sizeof(c));
printf("Espacio Ocupado por short ..... = %2d byte(s)/n", sizeof(s));
printf("Espacio Ocupado por int ....... = %2d byte(s)/n", sizeof(i));
printf("Espacio Ocupado por long ...... = %2d byte(s)/n", sizeof(l));
printf("Espacio Ocupado por float ..... = %2d byte(s)/n", sizeof(f));
printf("Espacio Ocupado por double .... = %2d byte(s)/n", sizeof(d));
printf("Espacio Ocupado por long double = %2d byte(s)/n", sizeof(ld));
return 0;
}



4- Constantes simbolicas
---------------------

Son identificadores que se usan para asociar un simbolo a una cadena:

#define FALSO 0
#define CIERTO 1

El compilador sustituye el identificador por la cadena asociada. Al proceso
de reemplazamiento se le denomina sustitucion de macro.

(Nota: No termina con ";")

La ventaja de usarlas es que cuando deben cambiarse los valores resulta mas
facil hacerlo sobre todo con los programas muy largos.

Tarea para la Casa: Editar math.h para ver las constantes simbolicas alli
definidas (jejeje).



5.- Enumeraciones
-------------

Podriamos definir los colores con:

#define ROJO 0
#define ANARANJADO 1
#define AMARILLO 2
#define VERDE 3
#define AZUL 4
#define INDIGO 5
#define MORADO 6

y en el programa usar:

int color;
color = VERDE;


sin embargo, con una lista mas larga esto resultaria bastante tedioso. Para
evitarlo, se usa la palabra clave enum:

enum {ROJO,ANARANJADO,AMARILLO,VERDE,AZUL,INDIGO,MORADO};

lo que da por resultado un simbolo asociado a un valor entero empezando por
0 (0,1,2,etc...).

en el programa se usa entonces:

int color = AZUL;


Con la palabra clave typedef se crea un nuevo tipo de datos:

typedef enum

{

ROJO,ANARANJADO,AMARILLO,VERDE,AZUL,INDIGO,MORADO

} Colores;


obteniendose asi un nombre de un nuevo tipo de datos. En este ejemplo, el
nombre Colores se asocia a la lista de los nombres de colores. El programa
puede entonces declarar una variable de este tipo.

Colores unColor;

y se asigna una constante simbolica a unColor segun:

unColor = INDIGO;

Tambien puede usarse un entero:

unColor = 5;

Si tenemos:

enum {ENE,FEB,MAR,ABR,MAY,JUN,JUL,AGO,SEP,OCT,NOV,DIC};

como resulta inhabitual asignar el 0 al mes de enero, puede forzarse empezar
por 1 con:

enum { ENE = 1,FEB,MAR,ABR,MAY,JUN,JUL,AGO,SEP,OCT,NOV,DIC};

Tambien podria usarse:

enum { ENE =10,FEB=20,...,DIC=120};



6- Conversion de Tipos
-------------------

Para terminar con esta parte de la revista es importante ver lo que pasa al
mezclar diferentes tipos de datos en una expresion.

Si tenemos:

long valor = 12345678;
int otroValor;

podemos preguntarnos que pasa al escribir:

otroValor = valor;

otroValor puede almacenar valores entre -32768 y 32767 pero no puede
almacenar la variable valor que es un positivo mas grande. Sin embargo, el
compilador permite esta asignacion y almacena 24910 en otroValor (resto de la
division de valor por 65536, entero mas grande que puede almacenarse en
otroValor).

Se genera asi una perdida de informacion.
Al contrario si otroValor = 4321, la asignacion:

valor = otroValor;

coloca 4321 en valor. En este caso no existe ningun problema. El compilador
promueve la transformacion del dato tipo int a tipo long.

Si fp es del tipo float y db del tipo double, la asignacion:

db = fp;

promueve fp a double. Sin embargo:

fp = db;

puede causar problemas.


Otros tipos de promocion tambien se permiten:

int i;
float f =2.8;
i =f;

en este caso, i = 2 y se perdieron las cifras decimales.

Int i= 4;
float f;
f = i;

promueve i a float y f = 4

int i= 4;
float f =2,8;
i = f * i;

i primero se promueve a float y se multiplica obteniendose 11.2. sin embargo,
al asignarse el valor del producto a i, se transforma de nuevo i a int y se
obtiene 11.



Moldes (Typecasts)
------------------

puede controlarse las reglas de promocion que usa el compilador:

int i = 4;
float f = 2.8;

al escribir:

i = (int) f * i;

se obtiene i = 8

Es decir, se indica al compilador considerar f como int.

/*
Bueno Se~ores, esto fue todo por ahora y nos vemos en la proxima edicion
de esta Revista Electronica.
*/


=< El_Max_5 >=


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

-[ 2x08 ]--------------------------------------------------------------------
-[ Como programar en C en muchos pasos (II) ]-------------------[ El_Max_5 ]-
---------------------------------------------------------[ [email protected] ]-


Saludos, nos volvemos a ver en esta segunda entrega, bueno, en esta segunda
parte de este curso de programacion en C vamos a trabajar con los que es
Operadores, Sentencias de Control y Funciones, Cualquier Pregunta, Opinion
Referencia, Correccion enviarmela por correo, Ok ... Bueno Empecemos ...

II.- Operadores y Sentencias de Control
----------------------------------

1.- Operadores
----------

Existen siete tipos de operadores: aritmeticos, relacionales, logicos, de
negacion, de incremento, de decremento y de bits.

a.- Aritmeticos
-----------

Son:

* multiplicacion
/ division
+ suma
- resta
% division en modulo


Los cuatro primeros actuan de acuerdo a lo se~alado. El quinto da el resto
de la division de dos enteros.

Ejemplo:
-------

#include <stdio.h>
#include <stdlib.h>

main()
{
double fgrados, cgrados;
char respuesta[128];

printf("Conversion de grados Fahrenheit a grados Celsius/n/n");
printf("Grados Fahrenheit? ");
gets(respuesta);
fgrados = atof(respuesta);
cgrados = ((fgrados - 32.0) * 5.0) / 9.0;
printf("Grados Celsius = %f/n", cgrados);
return 0;
}

Para su uso en expresiones debera tomarse en cuenta su precedencia:

(a + b * c) = (a + ( b * c ))
(a + b * c) ? ((a + b ) * c))


b.- Operadores Relacionales
-----------------------
Producen cierto (?????o falso (0) como resultado. Son:

< > ==
<= >= !=

Tienen un nivel de precedencia menor que los operadores aritmeticos. Es asi
que 10 > 1+ 12 se evalua como 10 > (1+12) y da por resultado un falso.

c.- Operadores Logicos
------------------

Son && y || y combinan las expresiones relacionales de acuerdo al "y" logico
y "o" logico:

(A < B) && (B < C) cierto si A < B y B < C
(A < B) || (B < C) cierto si A < B o B < C


d.- Operador Negacion
-----------------

Invierte el resultado de cualquier expresion logica. Es un operador unitario

!( A< B) es equivalente a A >= B


e.- Operadores de Incremento ++ y Decremento --
-------------------------------------------

En vez de usar:

i = i + 1;

puede usarse:

i = i++;

De la misma manera:

i = i - 1;

puede sustituirse por:

i = i--;

Ademas:

j = i++;

asigna el valor no modificado de "i" a "j" e incrementa el valor de "i" de 1.
Si el valor de "i" es 7, al ejecutarse esta sentencia "j" vale 7 e i 8. Si se
coloca el operador antes de la variable:

j = ++i;

i y j tendran el mismo valor. Es decir que:

j = i ++; equivale a: j = i; i++;

mientras que:

j = ++i; a: ++i; j = i;

Ejemplo:

1 #include <stdio.h>
2
3 main()
4 {
5 int a, b, c, v, k;
6
7 printf("Teclear un valor entero: ");
8 scanf("%d", &v);
9 k = v;
10 printf("/n Antes Durante Despues/n");
11 v = k; a = v; b = v++; c = v; printf("v++%8d%8d%8d/n", a, b, c);
12 v = k; a = v; b = v--; c = v; printf("v--%8d%8d%8d/n", a, b, c);
13 v = k; a = v; b = ++v; c = v; printf("++v%8d%8d%8d/n", a, b, c);
14 v = k; a = v; b = --v; c = v; printf("--v%8d%8d%8d/n", a, b, c);
15 return 0;
16 }

En este programa la linea 8 permite leer el valor decimal del entero
tecleado y almacenarlo en v. La expresion &v pasa la direccion de v a
scanf(). A excepcion de las cadenas, los argumentos tienen siempre que
pasarse a scanf() por su direccion.


f.- Operadores de Asignacion
------------------------

Se usan en vez de los operadores aritmeticos para ayudar al compilador en la
generacion de un codigo mas eficiente:

cuenta += 10 /* cuenta = cuenta + 10 */
cuenta *= 2 /* cuenta = cuenta * 2 */
cuenta /= 3 /* cuenta = cuenta / 3 */
cuenta %= 16 /* cuenta = cuenta % 16 */


g.- Precedencias
------------

La tabla siguiente resume las precedencias entre los operadores del C Nivel
Operadores:

---------------------------------------------------------------------------
| Nivel | Operadores | Orden de Evaluacion |
---------------------------------------------------------------------------
| 1 | () . [] | izquierdo a derecha |
| 2 | * & ! - ++ -- + - (molde) sizeof | derecha a izquierda |
| 3 | * / % | izquierdo a derecha |
| 4 | + - | izquierdo a derecha |
| 5 | << >> | izquierdo a derecha |
| 6 | < <= > >= | izquierdo a derecha |
| 7 | == != | izquierdo a derecha |
| 8 | & | izquierdo a derecha |
| 9 | ^ | izquierdo a derecha |
| 10 | | | izquierdo a derecha |
| 11 | && | izquierdo a derecha |
| 12 | || | izquierdo a derecha |
| 13 | ? : | derecha a izquierda |
| 14 | = *= /= += -= %= <<= >>= &= ^= |= | derecha a izquierda |
| 15 | , | izquierdo a derecha |
---------------------------------------------------------------------------



2.- Sentencias de Control
---------------------

a.- Sentencia if
------------

Su forma general es: if (expresion) sentencia;

Si la expresion es cierta (??????entonces se ejecuta la sentencia?

Puede tambien usarse:

if (expresion) {
sentencia1;
sentencia2;
}

if (expresion == valor) {
sentencia1;
sentencia2;
}

Ejemplo:

1 #include <stdio.h>
2
3 main()
4 {
5 int numero;
6 int okay;
7
8 printf("Teclear un numero entre 1 y 10: ");
9 scanf("%d", &numero);
10 okay = (1 <= numero) && (numero <= 10);
11 if (!okay)
12 printf("Respuesta incorrecta!/n");
13 return okay;
14 }

La linea 10 asigna a okay el resultado, cierto o falso de (1<= numero) &&
(numero <= 10). Okay contiene entonces 0 o un valor diferente de 0.

La linea 11 examina la expresion !okay. Si okay es igual a falso, !okay es
igual a cierto y se produce el mensaje de error de la linea 12.

Se podria haber utilizado:

if ((1 <= numero) && (numero<=10))
printf("Este numero no puede aceptarse !/n");

b.- La sentencia else
-----------------

Puede usarse para ejecutar otra alternativa:

if (expresion)
sentencia1;
else
sentencia2;


Podemos tener tambien:

if (expresion) {
sentencia1;
sentencia2;
} else {
sentencia3;
sentencia4;
}

Podemos anidar los ifs:

if (expresion1) {
sentencia1;
sentencia2;
else if (expresion2) {
sentencia3;
sentencia4;
} else {
sentencia5;
sentencia6;
}

Sin embargo, para mayor claridad no es recomendable abusar de los if's
anidados.

Ejemplo:


#include <stdio.h>
main()
{
int bisiesto;
int an;

printf("Detector de a~o bisiesto/n");
printf("A~o? ");
scanf("%d", &an);
if (an > 0) {
if ((an % 100) == 0)
bisiesto = ((an % 400) == 0);
else
bisiesto = ((an % 4) == 0);
if (bisiesto)
printf("%d es un a~o bisiesto/n", an);
else
printf("%d no es un a~o bisiesto/n", an);
}
return 0;
}


JUEGO: El ejemplo presenta un error. Trate de corregirlo.


#include <stdio.h>

main()
{
int numero;

printf("Numero (1 ... 10)? ");
scanf("%d", &numero);
if (numero >= 1)
if (numero > 10)
printf("Error: numero > 10/n");
else
printf("Error: numero < 1/n");
return 0;
}


En C existe una regla muy sencilla. La sentencia else se refiere al if
precedente mas proximo que no tenga asociada una sentencia else.

Otra regla importante es que nunca debe confiarse en la sangria para
controlar la logica de un programa.


c.- Expresion Condicional
---------------------

En vez de usar:
if (expresion1)
expresion2;
else
expresion3;

puede utilizarse:

expresion1 ? expresion2 : expresion3;

Por ejemplo:

if (selecMenu == 'S')
valor = 100;
else
valor = 0;

se sustituye por:

valor = (selecMenu == 'S') ? 100 : 0;

La ventaja de usar esta expresion es que se evita tener dos referencias de
la misma variable.


d.- Sentencia switch
----------------

Esta representa una alternativa al uso de los ifs anidados. Su forma general
es:

switch (expresion) {
case val1:
sentencia1;
break;
case val2:
sentencia2;
break;
case val3:
sentencia3;
break;
default:
sentenciaPorDefecto;
}

La sentencia break permite salir de inmediato del switch. Si no se pusiera
despues de sentencia2, se ejecutarian todas las sentencias que siguen a case
val2:, es decir las sentencias sentencia2 , sentencia3 y sentenciaPorDefecto.
Algunos programadores se aprovechan de esto y omiten colocar un break a
proposito para que se ejecuten varias sentencias del switch. Sin embargo, se
desaconseja esta practica.

Ejemplo:

1 #include <stdio.h>
2 #include <ctype.h>
3
4 main()
5 {
6 int selec;
7
8 printf("Menu: A(gregar B(orrar S(ortear Sa(lir: ");
9 selec = toupper(getchar());
10 switch (selec) {
11 case 'A':
12 printf("Selecciono Agregar/n");
13 break;
14 case 'B':
15 printf("Selecciono Borrar/n");
16 break;
17 case 'S':
18 printf("Selecciono Sortear/n");
19 break;
20 case 'L':
21 printf("Selecciono Salir/n");
22 break;
23 default:
24 printf("/nSeleccion Ilegal !!!/n");
25 }
26 return selec;
27 }

La linea 9 llama a una funcion getchar() que lee un caracter tecleado. La
funcion toupper() convierte el caracter a mayuscula.

e.- Sentencia while
---------------

Constituye una de las tres sentencias que permiten crear un bucle para
repetir una accion. Su forma general es:

while (expresion) {
sentencia1;
sentencia2;
........
}

Cada sentencia se ejecuta repitidas veces mientras la expresion es cierta.
Debe, por consiguiente, existir una sentencia que afecte el valor de la
expresion para que se termine de ejecutar el bucle.

Ejemplo:
1 #include <stdio.h>
2
3 main()
4 {
5 int contador;
6
7 printf("contador con while/n");
8 contador = 1;
9 while (contador <= 10) {
10 printf("%d/n", contador);
11 contador++;
12 }
13 return 0;
14 }

La linea 8 inicializa el valor de la variable de control contador mientras
que la linea 11 la incrementa, asegurando asi que la sentencia termine.

Agregar una linea para chequear el valor de contador cuando termina la
sentencia while.

Verificar lo que pasa si la expresion es (contador < 10) asi como si se
inicializa el contador con el valor 11.

Esta ultima prueba ilustra una propiedad importante de la sentencia while:
no se ejecuta si la expresion de control es inicialmente falsa.

Ejemplo:


#include <stdio.h>

main()
{
int c;

printf("alfabeto con while/n");
c = 'A';
while (c <= 'Z') {
printf("%c", c);
c++;
}
return 0;
}



f.- Sentencia do - while
--------------------

Su forma general es:
do {
sentencia1;
sentencia2;
.......
} while (expresion);

Esta sentencia se ejecuta completamente por lo menos una vez ya que la
expresion de control se chequea a final del bucle.

Ejemplo:


#include <stdio.h>
main()
{
int contador;

printf("contador con do-while/n");
contador = 0;
do
{
contador++;
printf("%d/n", contador);
} while (contador < 10);
return 0;
}


Agregar una linea para chequear el valor del contador en posiciones
estrategicas. Observar que en este caso la expresion (contador < 10) termina
correctamente el bucle despues de mostrar el decimo valor. Es importante
entender el porque en este caso la expresion (contador <= 10) no funcionaria.

Ejemplo:


#include <stdio.h>
main()
{
int c;

printf("alfabeto con do-while/n");
c = 'A' - 1;
do {
c++;
printf("%c", c);
} while (c < 'Z');
return 0;
}


g.- Sentencia for
-------------

Cuando se conoce o cuando el programa puede calcular el numero de veces que
debe ejecutarse la sentencia es preferible usar una sentencia for. Su forma
general es:

for (expresion1; expresion2 ; expresion3) {
sentencia;
}

Ejemplo:

for (i = 1; i <= 10; i++)
printf("i == %d/n",i);

lo que es equivalente a:

i = 1;
while ( i <= 10) {
printf("i == %d/n",i);
i++;
}

Ejemplo:

1 #include <stdio.h>
2
3 main()
4 {
5 unsigned char c;
6
7 for (c = 32; (c < 254); c++) {
8 if ((c % 32) == 0) printf("/n");
9 printf("%c", c);
10 }
11 printf("/n");
12 return 0;
13 }

En la linea 7 se inicializa la variable de control c al valor ASCII 32, es
decir un blanco. Cuando el resto de la division de c por 32 es 0, la
sentencia if provoca un cambio de linea.

La sentencia for (;;) ; permite crear un bucle infinito que se terminara
solamente cuando un evento externo, tal como, una se~al de interrupcion,
obliga al procesador a ejecutar algun otro codigo.


h.- Sentencia break
---------------

Permite interrumpir un bucle while, do - while o for en ejecucion.

Ejemplo:


#include <stdio.h>

main()
{
int contador;

printf("/n/nbucle for:/n");
for (contador = 1; contador <= 10; contador++) {
if (contador > 5) break;
printf("%d/n", contador);
}

printf("/n/nbucle while:/n");
contador = 1;
while (contador <= 10) {
if (contador > 5) break;
printf("%d/n", contador);
contador++;
}

printf("/n/nbucle do/while:/n");
contador = 1;
do {
if (contador > 5) break;
printf("%d/n", contador);
contador++;
} while (contador <= 10);

return 0;
}

La sentencia break puede tambien usarse para terminar una sentencia for
infinita:

for (;;) {
if (expresion)
break;
}


i.- Sentencia continue
------------------

Obliga a un bucle a iniciar su proxima iteracion al principio del bucle.

Ejemplo:


#include <stdio.h>
main()
{
int contador;

printf("/n Empezando el bucle for que contiene continue.../n");
for (contador = 1; contador <= 10; contador++) {
if (contador > 5) continue;
printf("%d/n", contador);
}
printf("Despues del bucle for, contador = %d/n", contador);

printf("/n/nEmpezando el bucle for que contiene break.../n");
for (contador = 1; contador <= 10; contador++) {
if (contador > 5) break;
printf("%d/n", contador);
}
printf("Despues del bucle for, contador = %d/n", contador);
return 0;
}


j.- Sentencia goto
--------------

Se usa asociada a una etiqueta constituida por un identificador seguido de
":". Cuando se ejecuta el programa, esta provoca un salto a la sentencia que
sigue inmediatamente a la etiqueta.

Resulta dificil depurar un programa que utilice un exceso de gotos. Ademas
de esto, el compilador tiene problemas para optimizar los gotos. Se
recomienda, en consecuencia, limitarse en su uso o no usarla.

Ejemplo:


#include <stdio.h>

main()
{
int contador = 1;

TOP:
printf("%d/n", contador);
contador++;
if (contador <= 10) goto TOP;
return 0;
}



k.- exit()
------

Normalmente la ejecucion de un programa se termina despues de ejecutarse la
ultima sentencia que se encuentra en main(). Generalmente esta sentencia es:

return valor;

Otra manera de terminar la ejecucion es llamando a la funcion exit(),
definida en stdlib.h

Ejemplo:


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
main()
{
char respuesta;

printf("/nTeclear S para Si, N para No: ");
respuesta = toupper(getchar());
if (respuesta == 'Y')
exit(1);
else
exit(0);
printf("esta sentencia no se ejecuta nunca!/n");
}



Este programa puede usarse en cualquier archivo batch que necesite de una
respuesta si/no del usuario. Por ejemplo, crear el archivo batch prueba.bat
para probarlo

prueba.bat
echo off
goto PRINCIPIO
:SI
echo Contesta si !
:PRINCIPIO
echo.
echo Quiere seguir?

Ejm 2:

if errorlevel == 1 goto SI
echo Contesta no !



III.- Funciones
---------

Un programa C esta constituido por una funcion main() y otras funciones mas
cuya tarea es muy especifica. Generalmente, se asigna a cada funcion una
tarea muy bien definida. Al escribir las funciones se recomienda la mayor
simplicidad.

1.- Funciones que no devuelven valores
-----------------------------------

El ejemplo siguiente muestra la manera correcta de escribir y usar las
funciones.

Ejemplo:

1 #include <stdio.h>
2
3 void Cuenta(void);
4 void CuentaAlReves(void);
5
6 main()
7 {
8 Cuenta();
9 CuentaAlReves();
10 return 0;
11 }
12
13 void Cuenta(void)
14 {
15 int i;
16
17 printf("/n/nContando hasta 10/n");
18 for (i = 1; i <= 10; i++)
19 printf("%4d", i);
20 }
21
22 void CuentaAlReves(void)
23 {
24 int i;
25
26 printf("/n/nContando al reves desde 10/n");
27 for (i = 10; i >= 1; i--)
28 printf("%4d", i);
29 }

Las lineas 3 - 4 declaran los prototipos de las funciones, o sea, el esquema
que indica al compilador el nombre y la forma de cada funcion del programa.
Por ejemplo, en la linea 3 cuenta es el nombre de la funcion. Puesto que la
funcion no devuelve un valor, se usa antes del nombre la palabra void. Ademas
dentro de los parentesis tambien se coloca void para indicar al compilador
que la funcion no requiere de parametros. Cuenta es la forma mas sencilla de
funcion: no devuelve nada y no necesita parametros de entrada.

La linea 8 llama a la funcion. Con esta simple sentencia se ejecuta la
funcion.

En alguna parte del programa, despues de la declaracion de la funcion, pero
no dentro de otra funcion, debe definirse la funcion. Para eso, se coloca el
encabezado rigurosamente identico a la declaracion de la funcion pero sin el
";" , y a continuacion el bloque de definicion que puede contener
declaraciones de variables.

Cualquier variable declarada dentro de una funcion es local a la funcion. Su
tiempo de vida es igual al tiempo de actividad de la funcion.

En el ejemplo, las variables i definidas en las lineas 15 y 24 son dos
variables diferentes.


2.- Funciones que devuelven un valor
--------------------------------

En el ejemplo anterior se utiliza una funcion que devuelve un valor entero
para indicar la respuesta del usuario cuando se le pregunta si quiere salir
de la aplicacion. Esta funcion se declara como una funcion que devuelve un
valor entero y no requiere argumentos.


#include <stdio.h>
#include <ctype.h>
#define FALSO 0
#define CIERTO 1

int Saliendo(void);

main()
{
int i = 0;
int salir = FALSO;

printf("Salir/n");
while (!salir)
{
i++;
printf("i == %d/n", i);
salir = Saliendo();
}
return 0;
}

int Saliendo(void)
{
int c;

printf("Otro valor? (s/n) ");
do
{
c = toupper(getchar());
} while ((c != 'S') && (c != 'N'));
return (c == 'N');
}



El ejemplo anterior usa una funcion que devuelve un valor real. Este ejemplo
muestra ademas como colocar las directivas #include y #define en un archivo
de cabecera unidades.h. Todas estas declaraciones podrian haberse colocado
al principio del programa, sin embargo, al colocarse en un archivo aparte,
pueden utilizarse en cualquier otro programa.



Unidades.h
#include <conio.h>

#define FALSO 0
#define CIERTO 1
#define CENT_POR_PULG 2.54;

/* Prototipos de Funciones */

void MuestraMenu(void);
int SelecMenu(void);
double PideValor(void);
void PulgadasACentimetros(void);

Ejemplo:

1 #include <stdio.h>
2 #include "c:/conversi.h"
3
4 main()
5 {
6 int saliendo = FALSO;
7
8 printf("Bienvenido al Programa de Conversion de Unidades/n");
9 while (!saliendo) {
10 MuestraMenu();
11 switch(SelecMenu()) {
12 case 1:
13 PulgadasACentimetros();
14 break;
15 case 9:
16 saliendo = CIERTO;
17 break;
18 default:
19 printf("/nError en la Seleccion!/a/n");
20 }
21 }
22 return 0;
23 }
24
25 /* Definicion de las funciones */
26
27 void MuestraMenu(void)
28 {
29 printf("/nMenu/n");
30 printf("----/n");
31 printf("1 -- Pulgadas a centimetros/n");
32 printf("2 -- Centimetros a pulgadas/n");
33 printf("3 -- Pies a metros/n");
34 printf("4 -- Metros a pies/n");
35 printf("5 -- Millas a kilometros/n");
36 printf("6 -- Kilometros a millas/n");
37 printf("9 -- Salir/n");
38 }
39
40 int SelecMenu(void)
41 {
42 printf("/nSeleccion? (No presione ENTER!): ");
43 return (getche() - '0');
44 }
45
46 double PideValor(void)
47 {
48 double valor;
49
50 printf("/nIndique el valor que desea convertir? ");
51 scanf("%lf", &valor);
52 return valor;
53 }
54
54 void PulgadasACentimetros(void)
55 {
56 double valor;
57 double resultado;
58
59 printf("/nPulgadas a Centimetros/n");
60 valor = PideValor();
61 resultado = valor * CENT_POR_PULG;
62 printf("%.3f pulgadas == %.3f centimetros/n", valor, resultado);
63 }


La linea 43 llama a la funcion getche() que capta la presion de una tecla
sin necesidad de presionar Enter por parte del usuario. Sustrayendo el valor
ASCII del caracter 0 convierte el caracter a su valor decimal entre 0 y 9.

El valor devuelto por una funcion puede tener cualquiera de los tipos
definidos anteriormente.

3.- Errores mas comunes cometidas con las funciones
-----------------------------------------------

* Ausencia de return: si la funcion no esta declarada con void y no posee un
return en su definicion, el compilador avisa del error.
* Falta de un return: este error es comun cuando se usa la sentencia if.
* Ausencia de prototipo: en este caso se considera que la funcion devuelve
un valor entero; sin embargo, es preferible no
contar con esta regla.
* efectos laterales: el caso mas frecuente es cuando la funcion modifica el
valor de una o varias variables globales.

El tercer tipo de error es comun en los programas viejos. main(), por
ejemplo, devuelve un valor entero y podria declararse int main(). En
realidad es por eso que se ha se~alado anteriormente que main() deberia
siempre contener una sentencia return.

El ejemplo Anterior ilustra el cuarto error que probablemente es el mas
dificil de detectar.


#include <stdio.h>

int Par(void);
int Impar(void);

int i = 1;

main()
{
while (i < 20) {
printf("%2d", i);
if (Par())
printf(" : is par");
printf("/n");
i++;
}
return 0;
}

int Par(void)
{
i++;
return Impar();
}

int Impar(void)
{
return (i % 2);
}



4.- Variables Locales
-----------------

Las variables declaradas dentro de las funciones son locales a estas
funciones. Esto significa que no existen afuera de ellas.

Cuando se llama a una funcion, esta reserva espacio en la pila para
almacenar sus variables locales. Cuando se termina la ejecucion de la
funcion, esta borra el espacio de la pila que se ha reservado, borrando al
mismo tiempo cualquier valor almacenado.

Si dos funciones independientes declaran una variable local, mientras una
funcion no llame a la otra, las dos variables pueden muy bien usar el mismo
espacio de memoria, claro, no al mismo tiempo. Se asegura asi un uso
racional de la memoria.

El siguiente ejemplo ilustra estos conceptos. Aun cuando las dos funciones
utilizan el mismo nombre de variable no se produce ningun conflicto. La
funcion1 llama a la funcion2 pero esta ultima no cambia el contenido de la
variable de la funcion1 ya que para la funcion2 la variable declarada en la
funcion1 no existe.

1 #include <stdio.h>
2 #include <conio.h>
3
4 void Pause(void);
5 void Funcion1(void);
6 void Funcion2(void);
7
8 main()
9 {
10 Funcion1();
11 return 0;
12 }
13
14 void Pause(void)
15 {
16 printf("Presionar la <barra espaciadora> para continuar...");
17 while (getch() != ' ') ;
18 }
19
20 void Funcion1(void)
21 {
22 char s[15] = "Puerto La Cruz/n";
23
24 printf("/nLa funcion # 1 empieza. s = %s", s);
25 Pause();
26 Funcion2();
27 printf("/nDe regreso a la funcion #1. s = %s", s);
28 }
29
30 void Funcion2(void)
31 {
32 char s[15] = "Valencia/n";
33
33 printf("/nLa funcion # 2 empieza. s = %s", s);
34 Pause();
35 }


La funcion getch() de la linea 17 es muy parecida a la funcion getche()
utilizada en otro ejemplo, pero no muestra la respuesta del usuario en la
pantalla.


Variable registro
-----------------
Las variables register se almacenan directamente en un registro del CPU. Ya
que no es obligatorio que un registro este disponible, se sugiere que la
variable se almacene en un registro. Esto se hace para aumentar la velocidad
de las operaciones. Estas variables son ideales para el control de bucles.
Solo las variables locales pueden declararse con register. Nunca una
variable global puede ser especificada como register.

Variable Volatil
----------------
Puede usarse el modificador volatile en la declaracion de la variable para
se~alar que la variable no puede almacenarse en un registro del CPU. Se
indica tambien al compilador que se abstenga de proceder a cualquier tipo de
optimizacion al manipular esta variable.

Tipicamente, se utilizan estas variables cuando se usan valores que deben
ser modificados por una rutina de interrupcion que opera independientemente
del programa.

Variable Externa
----------------
Al declarar una variable se describe su tipo. Al definirla, se reserva un
espacio para almacenar su valor. Un programa puede declarar un numero
ilimitado de veces una variable, pero solo puede definirla una vez.

Cuando un programa esta constituido por varios archivos, las funciones de un
modulo pueden hacer referencia a las variables definidas en otro modulo. Las
variables definidas en un modulo diferente de donde se usan se llaman
externas. Por ejemplo, si se define en el modulo A una variable:

int afuera;

y si una funcion de un modulo B necesita usar esta variable, debera
declararse en B:

extern int afuera;

El ejemplo siguiente contiene dos archivos, EXTERNA1.C y EXTERNA2.C. Antes
de compilarlos, se crea un proyecto EXTERNA1.PRJ, se agrega los dos archivos
y se compila y ejecuta con Ctrl + F9.


#include <stdio.h>

void GetFloat(void);
float f;

main()
{
GetFloat();
printf("Valor del numero real = %f/n", f);
return 0;
}


#include <stdio.h>

void GetFloat(void)
{
extern float f;
printf("Teclear un numero real: ");
scanf("%f", &f);
}



Variable Estatica
-----------------
Como ya se ha mencionado, las variables locales no mantienen su valor entre
dos llamadas a la funcion. Sin embargo, a veces, es necesario definir
variables que mantienen su valor entre dos llamadas a las funciones. En este
caso se declaran como static.

Ejemplo:

#include <stdio.h>

int Proximo1(void);
int Proximo2(void);

main()
{
int i;

printf("/nLlamando a Proximo1():/n");
for (i = 1; i <= 10; i++)
printf(" %d", Proximo1());
printf("/nLlamando a Proximo2():/n");
for (i = 1; i <= 10; i++)
printf(" %d", Proximo2());
return 0;
}

int Proximo1(void)
{
static int valor = 1;

return valor++;
}

int Proximo2(void)
{
int valor = 1;

return valor++;
}



5.- Parametros y Argumentos
-----------------------

double cubo(double r); /* r es un parametro. */
x = cubo(y); /* y es un argumento */

double cubo(double r)
{
return r * r * r;
}

Los parametros se inicializan con el valor de los argumentos que se pasan a
la funcion. Estos parametros reciben una copia de los valores pasados como
argumentos y el valor de estos ultimos no se cambia. Es decir que la
sentencia:

Q = Costo(x,y,z)

garantiza no cambiar los valores de x, y, z

Ejemplo:

#include <stdio.h>
#include <ctype.h>
#include <conio.h>

#define MAXLIN 12
#define MAXCOL 8

void Inicializa(void);
double Costo(double tiempo, double potencia, double costoUnitario);
void ImprimaTabla(void);
int Terminado(void);
double iniciaHoras;
double incrementoHorario;
double iniciaWatts;
double incrementoWatts;
double costoPorKwh;

main()
{
do {
Inicializa();
ImprimaTabla();
} while (!Terminado());
return 0;
}

void Inicializa(void)
{
puts("Costo del consumo electrico/n");
printf("Numero Inicial de Horas .. ? ");
scanf("%lf", &iniciaHoras);
printf("Incremento Horario.......... ? ");
scanf("%lf", &incrementoHorario);
printf("Numero inicial de Watts .. ? ");
scanf("%lf", &iniciaWatts);
printf("incremento para Watts........... ? ");
scanf("%lf", &incrementoWatts);
printf("Costo del kilowatt hora (KWH)? ");
scanf("%lf", &costoPorKwh);
}

double Costo(double tiempo, double potencia, double costoUnitario)
{
return costoUnitario * (potencia*0.001 * tiempo);
}

void ImprimaTabla(void)
{
int linea, col;
double horas, watts;

printf("/nHrs/Watts");
watts = iniciaWatts;
for (col = 1; col <= MAXCOL; col++) {
printf("%8.0f", watts);
watts += incrementoWatts;
}

horas = iniciaHoras;
for (linea = 1; linea <= MAXLIN; linea++) {
printf("/n%6.1f - ", horas);
watts = iniciaWatts;
for (col = 1; col <= MAXCOL; col++) {
printf("%8.2f", Costo(horas, watts, costoPorKwh));
watts += incrementoWatts;
}
horas += incrementoHorario;
}
printf("/n/nCosto del consumo electrico @ Bs.%.4f por KWH/n", costoPorKwh);
}

int Terminado(void)
{
int respuesta;

printf("/nOtra tabla (s/n) ? ");
respuesta = getch();
putchar(respuesta);
putchar('/n');
return (toupper(respuesta) != 'S');
}


En este ejemplo, la funcion putchar() se usa para mostrar un solo caracter.

El prototipo de una funcion puede omitir los nombres de los parametros:

double Costo (double, double, double);

Sin embargo, la definicion de la funcion debe incluir los nombres de los
parametros:

double Costo(double tiempo, double potencia, double costoUnitario)
{
return costoUnitario* (potencia * 0.001 * tiempo);
}

Puesto que los parametros se tratan como variables locales preinicializadas,
estos pueden declararse con los modificadores register, volatile y const.



6.- Recursividad
------------

Cuando una funcion se llama a si mismo, se habla de una funcion recursiva:

double Factorial (int numero)
{
if (numero > 1)
return numero * Factorial(numero - 1);
return 1;
}


Ejemplo:


#include <stdio.h>

void Recuenta(int max);

main()
{
Recuenta(10);
return 0;
}

void Recuenta(int max)
{
if (max > 1)
Recuenta(max - 1);
printf("%4d", max);
}


Un caso menos comun de recursividad es cuando una funcion llama a otra que a
su vez llama a la primera funcion.

Ejemplo:

#include <stdio.h>

void A(int c);
void B(int c);

main()
{
A('Z');
puts("");
return 0;
}

void A(int c)
{
if (c > 'A')
B(c);
putchar(c);
}

void B(int c)
{
A(--c);
}


Es importante resaltar que, a cada llamada de la funcion, los parametros se
almacenan en la pila, es decir que la recursividad consume el espacio de la
pila y podria hasta gotarlo.

Tambien las llamadas a una funcion consumen tiempo. Un programa que no
utiliza funciones recursivas puede ejecutarse mas rapidamente que un
programa equivalente utilizando funciones recursivas.



7.- Modificadores de Funcion
------------------------

Por defecto, una funcion es externa. Para limitar el uso de la funcion al
modulo donde se define, debe declararse como static. Pueden utilizarse
tambien los modificadores siguientes:

pascal

Con este modificador las funciones son responsables de eliminar de la pila
los valores de los argumentos alli colocados por la llamada a la funcion.

_fastcall
---------
Pasa los argumentos en registros en vez de la pila.

interrupt
---------
Declara a una funcion como rutina de interrupcion.

near
----
La funcion puede llamarse solamente a partir del mismo segmento de memoria.

far
---
La funcion puede llamarse a partir de cualquier segemento de memoria.

huge
----
Lo mismo que far.

8.- Compilacion condicional
-----------------------
Se utilisa #ifdef para chequear si una constante simbolica existe:

# ifdef DEPURANDO
sentencia1;
# elif PRUEBABETA
sentencia2;
# else
sentencia3;
#endif
#ifndef BUFFER
#define BUFFER 255
#endif

para evitar incluir varias veces el mismo archivo de cabecera:

#if !defined(___DEFS_H)
#include <_defs.h>
#endif

Continua en la segunda parte

Comments: