You are on page 1of 89

UNIVERSIDAD CATOLICA ANDRES BELLO

GUAYANA

Introducción al
Lenguaje de Programación C

Jesús J. Lárez M.
jlarez@ucab.edu.ve

Octubre 2010
Introducción al
Lenguaje de Programación C

"Si tú tienes una manzana y yo tengo una


manzana e intercambiamos las manzanas, entonces tanto tú como
yo seguiremos teniendo una manzana. Pero si tú tienes una idea y
yo tengo una idea e intercambiamos ideas, entonces ambos
tendremos dos ideas.“
George Bernard Shaw (1856 - 1950)
Dramaturgo y periodista irlandés.

Octubre 2010 Página 2 de 89


Introducción al
Lenguaje de Programación C

Contenido
 Presentación
 Historia y características del lenguaje C
 Pasos para la creación de una aplicación en Lenguaje C
 Entorno de Programación Integrado
 Estructura de un programa en C
 Identificadores
 Palabras reservadas
 Tipos de datos
 Variables, alcance y tiempo de vida
 Tipos de variables
 Declaración de variables
 Constantes
 Operadores y expresiones
 Instrucciones (sentencias ejecutables)
 Funciones y parámetros
 Arreglos, Estructuras, Uniones y Enumeraciones
 Apuntadores
 La biblioteca estándar
 Entrada y salida
 Memoria dinámica
 Funciones con un número de parámetros variables
 Pase de parámetros a la función main
 El preprocesador
 Llamadas a otros lenguajes de programación
 Caso de estudio: implementar un comando del sistema
 Algoritmos y aplicaciones
 Bibliografía recomendada
 Anexos

Octubre 2010 Página 3 de 89


Introducción al
Lenguaje de Programación C

Presentación
La presente guía nace como una necesidad de tener una herramienta concisa
para la enseñanza del lenguaje de programación C, en la Universidad Católica
Andrés Bello – Guayana. Está orientada como un instrumento de aprendizaje del
lenguaje y/o de consulta para los estudiantes de la carrera de Ingeniería en
Informática, con la finalidad de apoyar el uso el lenguaje tanto en cursos básicos
como el de Algoritmos y Programación y cursos avanzados tales como Sistemas
Operativos y Sistemas Distribuidos. Sin embargo la misma puede ser utilizada por
cualquier lector interesado en el aprendizaje o uso del lenguaje,
Se asume que el lector tiene nociones básicas en algoritmos y programación, es
por esta razón que se que desde un principio se muestran ejemplos de programas
completos para que el lector se inicie lo más pronto posible en el lenguaje. Sin
embargo los nuevos programadores, con nociones básica en ingles, podrán leer y
asimilar los concepto del lenguajes.
El contenido trata desde los usos más elementales hasta los más avanzados del
lenguaje. Mientras se abarca cada uno de los temas se muestran ejemplos de
programas completos y funcionales que ejemplifican los conceptos expuestos, la
idea es que el lector utilice estos ejemplos en el entorno de programación de su
preferencia, los compile y ejecute, observando y analizando tanto el código
como los resultados, para posteriormente modificarlos y experimentar. Estos
ejemplos intentan mostrar principios básicos de la programación tales como la
simplicidad, la claridad y la generalidad, con la finalidad que el lector pueda
desarrollar un buen estilo de programación, ya que un programa bien escrito es
más sencillo de entender y modificar; y tal vez lo más importante que tiene una
mayor probabilidad de ser correcto.

Octubre 2010 Página 4 de 89


Introducción al
Lenguaje de Programación C

Historia y características del lenguaje C


Este lenguaje fue diseñado originalmente para el Sistema Operativo UNIX en la
DEC pdp-11, en implantado en ella por Dennis Ritchie en los Laboratorios Bell,
aunque siempre ha estado estrechamente relacionado con UNIX no está ligado a
un Hardware o Sistema de Operación en particular. Aunque es un lenguaje para
la programación de sistemas debido a su uso en el desarrollo de Sistemas de
Operación, sin embargo C es un lenguaje de programación de propósito general,
es decir no está especializado en ninguna área de aplicación en particular es
decir se puede utilizar para varios propósitos.

El lenguaje C es caracterizado por ser conciso y poseer estructuras de control y


datos de alto nivel. Muchas de las características de C provienen del lenguaje
BCPL, inventado por Martin Richards, llegando indirectamente a través del
Lenguaje B, escrito por Ken Thompson en 1970 para el primer sistema UNIX en una
DEC pdp-7.

Durante años el estándar de C fue la versión proporcionada por la versión V de


UNIX. El cual aparece descrito en el libro ―The C. Programming Language‖ de
Brian Kernigham y Dennis Ritchie. En 1989 se define el primer estándar por ANSI,
que también fue adoptado por la ISO, siendo habitual que se le refiera como
estándar ANSI/ISO y se le conozca habitualmente como C89. Para 1995 aparece
la primera enmienda, que, entre otras cosas incorpora varias funciones de
bibliotecas nuevas. Siendo C89 y la enmienda 1 la base para C++. Para 1999
aparece una nueva versión de C (ANSI/ISO C99) donde se hace énfasis en la
incorporación de bibliotecas numéricas y arreglos de tamaño variable.

Al lenguaje C se le denomina como un lenguaje de nivel intermedio, es no


significa que sea menos potente, más difícil de utilizar o menos evolucionados que
otros lenguajes de alto nivel, sino más bien su significado es que combina
elementos de lenguajes de alto nivel con la flexibilidad y poder de los
ensambladores, permitiendo la manipulación de bits, bytes y direcciones, a pesar
de ello el código en C es muy portable. Esto le hace particularmente adecuado

Octubre 2010 Página 5 de 89


Introducción al
Lenguaje de Programación C

para la programación de sistemas.

Entre sus principales características, se puede mencionar:

 Lenguaje programación de sistemas (UNIX), es decir es utilizado en para


programar partes del sistema de operación y/o utilidades, aunque es un
Lenguaje de propósito general.
 Lenguaje de nivel intermedio, combina características de lenguaje de alto
nivel y la flexibilidad de los lenguajes ensambladores.
 No es un lenguaje fuertemente estructurado, aunque posee estructuras de
control propias de lenguajes estructurado (while, do o for) tiene
instrucciones como goto, return, etc.
 No es un lenguaje fuertemente tipeado permite que tipos diferentes lates
como caracteres y enteros se mezclen en una expresión así mismo permite
el uso incongruente de argumentos y parámetros en las llamadas a
funciones.
 No comprueba errores durante la ejecución, tales como que se
sobrepasen los límites de los arreglos.
 Código muy portable, es posible llevar o fácil de adaptar los programas
escritos a otro sistema de operación o hardware.
 Un conjunto reducido de palabras reservadas.
 Funcionalidades adicionales (I/O, matemáticas, memoria dinámica, etc.)
están disponible a través de bibliotecas estándar.
 Lenguaje conciso con sobrecarga de operadores lo que puede dar a lugar
Programas Crípticos que pueden atentar contra la claridad.
 Lenguaje simple, con funcionalidades añadidas por bibliotecas estándar.
 Usa un lenguaje de preprocesado, el preprocesador de C se usa para
tareas como definir macros e incluir archivos al código fuente.
 Soporta llamado recursivo de funciones.
 Ofrece solamente el paso parámetros por valor.
 Imposibilidad de anidar funciones.
 Incluye apuntadores y aritmética de direcciones.

Octubre 2010 Página 6 de 89


Introducción al
Lenguaje de Programación C

 Soporta compilación por separado.


 Flujo de control sencillo, aunque no soporta multiprogramación,
paralelismo, sincronización o corrutinas.

En definitiva C, por sus características y eficiencia en un lenguaje para


programadores.

El lenguaje C ha influido a muchos lenguajes de programación entre los que se


puede mencionar: C++, java, JavaScript, C#, Objective-C, Perl, PHP, Python entre
otros.

Con la aparición de C++, se pensó que C dejaría de existir como lenguaje de


programación, pero no ha sido así en parte inmensa cantidad de sistemas y
aplicaciones en funcionamiento que desarrollada en C necesita mantenimiento,
no siempre se requiere la orientación a objeto de C++ por ejemplo aplicaciones
empotradas, a tiempo real o que requieran de mucha eficiencia.

Octubre 2010 Página 7 de 89


Introducción al
Lenguaje de Programación C

Pasos para la creación de una aplicación en lenguaje C


Los pasos para la creación de una aplicación en C son las siguientes:

 Crear el programa fuente para lo cual se utiliza un editor.

 Compilación del programa, C proporciona ciertas facilidades del lenguaje


por medio de un preprocesador, que conceptualmente es el primer paso
separado en la compilación, entre sus funcionalidades se pueden nombrar:
inclusión de archivos, substitución de macros, compilación condicional. Así
mismo C tiene soporte de compilación por separado, es decir se un
programa se puede escribir en múltiples archivos y cada uno de ellos
compilado por separado, lo cual tiene como ventaja en que un cambio en
uno de los archivos no requiere la compilación del programa completo.

 Enlazar los diferentes módulos objetos, las bibliotecas requeridas y crear


archivo ejecutable

 Opcionalmente depurar la aplicación.

A continuación se muestra un grafico con un ejemplo:

Encabezado Encabezado Encabezado Encabezado Encabezado

stdio.h colas.h graficos.h colas.h graficos.h

Editor Fuente Preprocesador Fuente Fuente

Principal.c colas.c graficos.c

Compilador
Objeto Objeto Objeto Librerias

Principal.obj colas.obj graficos.obj .lib

Enlazador

Ejecutable

[Depurador] Aplicacion.exe

Octubre 2010 Página 8 de 89


Introducción al
Lenguaje de Programación C

Cuando se escriben programas, se pueden cometer errores (bugs). De hecho, los


programadores comenten errores y la probabilidad de que el programa funcione
a la primera vez es prácticamente cero. Por lo tanto, el desarrollo de un programa
siempre incorpora una etapa de depuración (debugging), que consiste en buscar
y resolver los errores cometidos durante la programación. Para facilitar la etapa
de depuración es conveniente usar herramientas especializadas para estos
efectos. La más común es el depurador o también llamado debugger.

Un depurador es una herramienta que permite intervenir durante la ejecución de


un programa, para saber cómo se está ejecutando. Un depurador permite, entre
otras funciones:

 Ejecutar paso a paso un programa (stepping).

 Establecer puntos de detención (breakpoints).

 Examinar el contenido de las variables y objetos.

 Conocer el encadenamiento de llamadas de procedimientos.

 Retomar la ejecución hasta un nuevo punto de detención.

El proceso de compilación se puede automatizar utilizando la herramienta make,


la cual lee las instrucciones y las dependencias de un archivo (makefile) y ejecuta
los comandos que permiten generar el código ejecutable.

En nuestro caso en particular se ha utilizado el compilador gcc


(http://gcc.gnu.org/) y las herramientas gdb y make

Octubre 2010 Página 9 de 89


Introducción al
Lenguaje de Programación C

Entorno de Programación Integrado


Un entorno de programación integrado o en inglés Integrated Development
Environment (IDE) es una aplicación compuesta por un conjunto de herramientas
para programar, la cual puede estar dedicada de forma exclusiva a un sólo
lenguaje de programación o bien a poder utilizarse para varios.

Un IDE es un entorno de programación que ha integrado diferentes aplicaciones,


tales como: editores de código, compiladores, depurador, entre otras.

En nuestro caso en particular se ha utilizado el IDE eclipse (http://www.eclipse.org)

Octubre 2010 Página 10 de 89


Introducción al
Lenguaje de Programación C

Estructura de un programa en C
Los programas en C pueden estar formados por una o más funciones, aunque la
única función que debe estar presente en la función main(), siendo la función que
se invoca de primero cuando el programa se ejecuta, aunque main no es una
palabra clave se trata como si lo fuera, los programas en C tienen extensión .c
para los programas y .h para los encabezados (ya sean del programador o de las
librerías estándares).

Aunque un programa en C puede estar formado solamente por construcciones


propias del lenguaje, los compiladores de C incorporan bibliotecas estándar que
proporcionan funciones para algunas de las tareas más usuales, por ejemplo
entrada y salida ya sea por consola o por archivo, manejo de caracteres y
cadenas de caracteres, funciones matemáticas, manejo de memoria dinámica,
etc.

La estructura general de un programa en C, junto con nuestro primer programa


(en el archivo hola.c), se muestra a continuación

Declaraciones globales /*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
tipo función(parámetros){
#include <stdio.h>
Declaraciones locales
secuencia de instrucciones int main(void){
printf("Hello, world\n");
} return 0;
}

/* --- Fin ejemplo --- */


int main(int argc, char **argv){
Declaraciones locales
secuencia de instrucciones
}

La forma de compilar este programa, depende del sistema, en nuestro caso es


desde la línea de comando con gcc hola.c y se ejecuta con la orden a.out y

Octubre 2010 Página 11 de 89


Introducción al
Lenguaje de Programación C

siendo su salida

Hello, world

Analizando el programa se puede observar que:


 Se considera comentario todo lo incluido entre /* y */. Siguiendo el
siguiente formato: /* comentario */ , un comentario puede ocupar varias
líneas y no pueden haber comentarios anidados, adicionalmente C99
permite comentarios en una sola línea comenzando con // y
extendiéndose hasta el final de la línea por ejemplo // comentario.
 #include <nombre_archivo.h> es una directiva para el preprocesador que
incluye un archivo de cabecera (header) en este caso la biblioteca
estándar de entrada y salida.
 Se define la función main() es la función principal del programa, es decir la
primera función a la que se llama dentro de la ejecución del programa y
que las instrucciones están entre llaves ("{" y "}") .
 Se llama a la función printf() de la biblioteca estándar de entrada y salida,
la cual muestra la cadena de caracteres "Hello, world\n" en la salida
estándar.

Octubre 2010 Página 12 de 89


Introducción al
Lenguaje de Programación C

Identificadores
Un identificador, sirven para nombrar variables, etiquetas, y otros objetos definidos
por el programador, puede estar compuesto de cualquier combinación de letras
(minúsculas y mayúsculas), dígitos y el símbolo underscore '_'. La única restricción
es que el primer carácter debe ser una letra o un underscore. Un identificador no
puede coincidir con una palabra clave de C y con identificadores previamente
definidos en el mismo ámbito.

Ejemplos de identificadores:

 Válidos:
x
y2
suma_1
_t
TABLA
 No válidos:
4num primer carácter no es letra
―x‖ carácter ilegal “
orden-no carácter ilegal -
ind lis espacio ilegal

Observaciones
 Se diferencia entre mayúsculas y minúsculas.
 No se limita la longitud de los identificadores. Pero algunas
implementaciones sólo reconocen (son significativos) los 8 primeros y otras
(ANSI) los 31 primeros caracteres.

Octubre 2010 Página 13 de 89


Introducción al
Lenguaje de Programación C

Palabras reservadas (C89)


Las palabras reservadas de C, que no pueden ser utilizadas en definiciones por el
usuario son las que se listan en la siguiente tabla:
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while

A las cuales C99 añade, entre otras:


inline Restrict

Octubre 2010 Página 14 de 89


Introducción al
Lenguaje de Programación C

Tipos de datos
C utiliza 5 palabras reservadas para definir los tipos de datos fundamentales. A
diferencia de otros lenguajes, un determinado tipo de datos puede ir cualificado
por un conjunto de modificadores.

Los tipos de datos fundamentales son:


char carácter
int entero
float real
double real doble precisión
void sin tipo
Todos los demás tipos se basan en los anteriores

Modificadores:

signed con signo


unsigned sin signo
short corto
long largo

A continuación se muestra una tabla con los tipos, su tamaño en bits y el rango
de valores que puede almacenar, esto puede variar por cada tipo de
procesador.

Tipo Tamaño Bits Rango de valores


char 8 -128 a 127
unsigned char 8 0 a 255
signed char 8 -128 a 127
int 16 (o 32) -32.768 a 32.767
unsigned int 16 (o 32) 0 a 65.535
signed int 16 (o 32) -32.768 a 32.767
short int 16 -32.768 a 32.767
unsigned short int 16 0 a 65.535
signed short int 16 -32.768 a 32.767

Octubre 2010 Página 15 de 89


Introducción al
Lenguaje de Programación C

long int 32 -2.147.483.648 a 2.147.483.647


unsigned long int 32 0 a 4.294.967.295
signed long int 32 -2.147.483.648 a 2.147.483.647
long long int 64 Añadido por C99
unsigned long long int 64 Añadido por C99
enum 16 0 a 65.535
float 32 3,4E-38 a 3,4E+38 (7 dígitos)
double 64 1,7E-308 a 1,7E+308 (15 dígitos)
long double 64 1,7E-308 a 1,7E+308 (15 dígitos)

El lenguaje C no incorpora el tipo de dato lógico (boolean) para lo cual se utiliza


un entero, interpretándose todo valor distinto de 0 como verdadero y el valor 0
como falso.

Se pueden definir nuevos nombre para los tipos de datos existentes utilizando la
palabra clave typedef, que realmente no crea un nuevo tipo de datos sino que
define un nuevo nombre para un tipo ya existente. La forma de utilizarlos es:

typedef tipo nombre;

Donde tipo es cualquier tipo de datos valido, ya sea un tipo base o una
estructura, por ejemplo:

typedef unsigned char byte;

El programa a continuación muestra el tamaño de las diferentes tipos, para lo


cual usaremos el operador sizeof que calcula el tamaño, en bytes, de cualquier
tipo o variables.

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

Octubre 2010 Página 16 de 89


Introducción al
Lenguaje de Programación C

int main(void){
int a;
float f;

printf("Size of char: %d\n",sizeof(char));


printf("Size of short: %d\n",sizeof(short));
printf("Size of int: %d\n",sizeof(int));
printf("Size of long int: %d\n",sizeof(long int));
printf("Size of float: %d\n",sizeof(float));
printf("Size of double: %d\n",sizeof(double));
printf("Size of a: %d\n",sizeof(a));
printf("Size of f: %d\n",sizeof(f));
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 17 de 89


Introducción al
Lenguaje de Programación C

Variables, alcance y tiempo de vida


La posición de memoria con un nombre que se utiliza para mantener un valor que
puede ser modificado durante la ejecución del programa se le conoce como
variable.

Asociado a una variable se pueden distinguir tres conceptos: el identificador, su


tipo y su valor. El identificador es el símbolo que la nombra y el tipo define lo que
puede almacenar la variable y el conjunto de operaciones que pueden aplicar,
por ejemplo para la siguiente declaración

int n = 123;
Se puede distinguir

Ahora veamos algunas definiciones relacionadas con las variables:

 Un binding es una asociación entre dos cosas, por ejemplo un nombre de


variable con su contenido (el elemento que denota).

 Tiempo de vida de una variable: El tiempo de vida se refiere al intervalo de


tiempo que trascurre desde que se crea la variable hasta que se destruye.

 Alcance de una variable: El alcance de una variable es el conjunto de


instrucciones en la que esa variable es visible por medio de su identificador.

Es importante entender la diferencia entre alcance y tiempo de vida de una


variable. Si una variable no es visible, no necesariamente ha sido destruida. En
cambio, si una variable fue destruida, esa variable no es visible.

Octubre 2010 Página 18 de 89


Introducción al
Lenguaje de Programación C

Tipos de variables
Un programa compilado en C tiene cuatro regiones lógicas de memoria
diferentes, las cuales se muestran a continuación:

Aunque la disposición física de cada una de ellas puede diferir entre los diferentes
tipos de procesadores y entre las diferentes implementación del lenguaje. La
figura anterior muestra de forma conceptual como aparece un programa en C
en la memoria. La primera región contiene el código del programa, en la
siguiente región se guardan las variables globales (datos estáticos) ambas
regiones se asignan en tiempo de compilación. Las dos regiones restante son
asignadas en tiempo de ejecución y son la pila (stack) y el montón (heap), la pila
mantiene entre otros las direcciones de retornos, los parámetros reales y las
variables locales (datos automáticos) y finalmente el montón es la región que
puede utilizarse, de forma explícita, mediante las funciones de administración de
memoria dinámica (datos dinámicos) y se utilizan a través de apuntadores
(pointers).

Octubre 2010 Página 19 de 89


Introducción al
Lenguaje de Programación C

Declaración de variables
La forma general de declaración es:
<tipo> lista_de_variables;

int numero1, numero2;


long int l;

Las variables globales y static solo son inicializadas al principio del


programa, las variable locales automáticas se inicializan cada vez que se
entra en el bloque. Las variables globales y static que no hayan sido
inicializadas toman automáticamente el valor inicial de cero y las locales
auto tendrán valores desconocidos.

Inicialización de variables en la declaración

El esquema para la inicialización de variables en la declaración es el siguiente:


<tipo> nombre_variable = valor;

int numero1=5,numero2;

Dónde se declaran las variables

Básicamente hay tres sitios donde se pueden declarar variables: dentro de las
funciones, en la definición de los parámetros de las funciones y fuera de todas las
funciones. Estas variables se denominan, respectivamente, variables locales, los
parámetros formales y variables globales.

Variables locales, parámetros formales y variables globales

El lugar donde se declara una variable define el ámbito que determina el


alcance o visibilidad de la variable por medio de su identificador. Hay que
recordar que dentro del mismo ámbito no se pueden realizar declaraciones con
el mismo identificador

Octubre 2010 Página 20 de 89


Introducción al
Lenguaje de Programación C

 Variable local: A las variables locales que se declaran dentro de una


función nos referimos a ellas como variables automáticas. Solo puede ser
utilizada dentro del bloque en el que han sido declaradas o expresado de
otra forma, las variables locales no son conocidas fuera del bloque de
código donde han sido declaradas. Un bloque de código comienza con
una llave que abre { y termina con una llave que cierra }, se pueden
declarar variables locales en cualquier bloque, el bloque definido por una
función es solo un caso especial. Las variables locales solo existen mientras
se está ejecutando el bloque de código donde está declarada, es decir se
crean al entrar y destruyen a salir de él y su valor se pierde, esto es
particularmente importante en las llamadas a funciones ya que no pueden
retener su valor entre llamadas, sin embargo se usando el calificador static
se crea un almacenamiento permanente con alcance en el bloque.
 Parámetros formales: Si una función utiliza argumentos, debe declarar las
variables que aceptarán los valores de los argumentos, esas variables son
los parámetros formales de la función y se comportan como cualquier otra
variables local de la función. Hay que tener presente que al igual de las
variables locales son automáticas y se destruyen al salir de la función.
 Variables globales: A diferencia de las variables locales, las variables
globales se conocen a lo ―largo del todo programa‖ y se puede utilizar en
cualquier parte del código, además mantienen sus valores durante toda la
ejecución del programa. Son variables que declaran fuera de todas las
funciones.

En la práctica ocurre que cada identificador solo tiene alcance (es visible) en
algunas regiones de su ámbito, que podrían ser discontinuas. La razón por la que
un identificador deja de tener alcance (o ser visible) dentro de su ámbito es que
sea ocultado por otra declaración explícita que utiliza el mismo identificador. La
nueva declaración puede ocurrir por ejemplo en un bloque de código anidado
(recordemos en el mismo no es posible una nueva declaración con el mismo
identificador) o dentro de una función en el caso de variables globales.

Octubre 2010 Página 21 de 89


Introducción al
Lenguaje de Programación C

Como se pudo observar dependiendo del sitio donde se declaren las misma
pueden tener distintos tiempo de vida, recordemos si una variable no es visible, no
necesariamente ha sido destruida. En cambio, si una variable fue destruida, esa
variable no es visible.

En el siguiente ejemplo se pueden observar todos los sitios donde se declaran las
variables, en ellas se puede observar conceptos como ámbito y alcance.

#include <stdio.h>
int numero1, numero2; Variable Global

int funcion1(int numero1,int numero2){ Parámetro Formal


}

int funcion2(void){
int numero1, numero2; Variable local

int funcion3(void){
int numero1, numero2;

if (numero1==numero2){
int numero1, numero2; Variable Local (Bloque)
}
}

A continuación se muestra un ejemplo donde se pueden observar los conceptos


de ámbito y alcance de las variables.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int a=0;

Octubre 2010 Página 22 de 89


Introducción al
Lenguaje de Programación C

void funcion1(int a){


printf("En funcion1 el valor de a:%d\n",a);
}

void funcion2(int p){


int a=p;

printf("En funcion2 el valor de a:%d\n",a);


funcion1(a+1);
}

int main(void){
int a=1;

printf("En main el valor de a:%d\n",a);


funcion2(a+1);
return 0;
}

/* --- Fin ejemplo --- */

Calificadores de acceso
Controlan las formas que se acceden o se modifican las variables.
const
No pueden ser modificadas por el programa, solo inicializar.

const int a=10;

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

void nocambiar(const char parametro){


printf("desde no cambiar p:%d\015",parametro);
}

int main(void){
const unsigned char b=0xFF;

nocambiar(b);
printf("%d\n",b);
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 23 de 89


Introducción al
Lenguaje de Programación C

volatile
El valor de una variable puede cambiar por medios no explícitamente
especificados por el programa.

Const volatile char *puerto = (const volatile char *) 0x30;

Calificadores de clase de almacenamiento


Indican al compilador como deben almacenar las variables.
extern
Específica (declara) que un objeto esta declarado con enlace externo en
otro módulo (archivo) del programa, es decir esta definido en otro archivo.

int x,y; extern int x,y;


char c; extern char c;

int main(){ void func23(void){


x=123; y=10;
} }

static
Cuando es variable local se crea un almacenamiento permanente, con
alcance en el bloque que es declarada y cuando la variable es global se
le indica que solo es conocida en el archivo que se declara.

int veces(void){ static int cuentas=0;


static cuentas=0;
int veces(void){
cuentas++; cuentas++;
return cuentas; return cuentas;
} }

A continuación se muestra un ejemplo de compilado por separado, note

Octubre 2010 Página 24 de 89


Introducción al
Lenguaje de Programación C

que se trata de dos archivos, revise la documentación del compilador


para compilar y enlazar ambos modulos.

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*
* archivo: modulo1.c
*/

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

extern int valor;


void funcion1(void);

int cuenta(void){
static int cuentas=0;

cuentas++;
return cuentas;
}

int main(void) {
funcion1();
printf("valor: %d\n",valor);
printf("cuentas: %d\n",cuenta());
printf("cuentas: %d\n",cuenta());
return EXIT_SUCCESS;
}

/* --- Fin archivo modulo1.c --- */

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*
* archivo: modulo2.c
*/

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

int valor=5;

void funcion1(void){
printf("desde funcion1 valor: %d\n",valor);
}

/* --- Fin archivo modulo2.c --- */

Octubre 2010 Página 25 de 89


Introducción al
Lenguaje de Programación C

register
Se le pide al compilador que mantenga el valor en un registro del CPU en
lugar de la memoria, donde normalmente se almacenan las variables, esto
con el fin de realizar operaciones mucho más rápido.

Register int temp;

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
register char c='a';

for(;c<='z';c++)
printf("%c",c);
return 0;
}

/* --- Fin ejemplo --- */

auto
Utilizado para declara variables locales, sin embargo todas las variables
que no son globales, se asumen auto.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

void funcion1(void){
auto int a=1;
printf("En funcion1 el valor de a:%d\n",a);
a=a+1;
}

void funcion2(void){
static int a=1;
printf("En funcion2 el valor de a:%d\n",a);
a=a+1;
}

Octubre 2010 Página 26 de 89


Introducción al
Lenguaje de Programación C

int main(void){
funcion1();
funcion1();
funcion1();
funcion2();
funcion2();
funcion2();
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 27 de 89


Introducción al
Lenguaje de Programación C

Constantes
Refieren a valores fijos que no pueden ser modificados por el programa, también
se le conocen como literales, pueden ser de cualquier tipo básico de datos. La
forma que se representa cada constate depende de su tipo.

Enteras:

123
-123
012 /* en octal 10 */
0xA /* en hexadecimal 10 */
3500L /* long */

Reales:

123.45 /* float */
1001.2L /* long 28nión28 */
47e-4
.25E7

Caracteres:

'Z'
'\n' /* salto de línea */
'\r' /* retorno carro */
'\f' /* salto de página */
'\t' /* tabulador */
'\b' /* espacio atrás */
'\v' /* tabulador vertical */
'\\' /* barra invertida */
'\'' /* comillas simples */
'\”' /* comillas dobles */
'\Const' /* constante octal */
'\xConst' /* constante hexadecimal*/

Octubre 2010 Página 28 de 89


Introducción al
Lenguaje de Programación C

Cadenas de caracteres (string)

"HOLA MUNDO"

Una cadena de caracteres (string) es un vector de caracteres que


termina con un carácter '\0', hay siempre que recordar que Las
funciones que manipulan strings esperan que los mismos terminen en
'\0'.

Enumeradas

enum dias{Domingo, Lunes, Martes, Miercoles, Jueves,


Viernes, Sabado};

Octubre 2010 Página 29 de 89


Introducción al
Lenguaje de Programación C

Operadores y expresiones
El lenguaje C es sumamente rico en operadores incorporados, entre los que se
tienen:

Asignación

A diferencia de mucho otros lenguajes en C la asignación es un operador y


no una instrucción por lo cual se puede utilizar en cualquier expresión.

Lvalue = expresión

num=5
num=temp=0

Aritméticos
Negación - -x
Suma + x+y
Substracción - x-y
Multiplicación * x*y
División / x/y
Modulo % x%y

Relaciónales
Igual == x==y
Diferente ¡= x!=y
Mayor que > x>y
Mayor o igual que >= x>=y
Menor que < x<y
Menor o igual que <= x<=y

Lógicos
Negación ¡ ¡x
Y && x&&y (Evaluación en cortocircuito)

Octubre 2010 Página 30 de 89


Introducción al
Lenguaje de Programación C

O || x||y (Evaluación en cortocircuito)

Bits
Complemento ~ ~x (negación)
And & x&y
Or | x|y
Xor ^ x^y
Corrimiento der. >> x>>y
Corrimiento izq. << x<<y

Asignación Compuesta
x=x+y += x+=y
-= x-=y
*= x*=y
/= x/=y
%= x%=y
&= x&=y
|= x|=y
^= x^=y
>>= x>>=y
<<= x<<=y

Incrementales y decrementales
x=x+1 ++ x++
++x
x=x-1 -- x—
--x

Misceláneos
Condicional ¿: x?y:z
Dirección & &x
Indirección * *x

Octubre 2010 Página 31 de 89


Introducción al
Lenguaje de Programación C

Coma , x,y
Tamaño sizeof() sizeof(X)
Selector campo . x.y
Selector campo -> x->y equivale a (*x).y
Elemento arreglo [] x[y]
Conversión tipo (tipo) (tipo)x

Precedencia y Asociación de operadores

La siguiente tabla resume las reglas de precedencia y asociatividad de todos los


operadores, están colocados por orden de precedencia decreciente.

Precedencia Operador Asociatividad Nombre

MAYOR () [] -> . Izq. A Der.

¡ ~ ++ -- - (tipo) * & sizeof Der. A Izq. Unario

*/% Izq. A Der. Multiplicativo

+- Izq. A Der. Aditivo

<< >> Izq. A Der. Corrimiento

< <= >= > Izq. A Der. Relacional

== ¡= Izq. A Der. Igualdad

& Izq. A Der. And bits

^ Izq. A Der. Xor bits

| Izq. A Der. 0r bits

&& Izq. A Der. And Lógico

|| Izq. A Der. Or Lógico

¿: Der. A Izq. Condicional

= += -= *= /= %= <<= >>= &= |= ^= Der. A Izq. Asignación

MENOR , Izq. A Der. Coma

Octubre 2010 Página 32 de 89


Introducción al
Lenguaje de Programación C

Evaluación en Cortocircuito

En relación con los operadores lógicos && y || son evaluados de izquierda a


derecha y la evaluación se detiene tan pronto se conoce el resultado ya seas
falso en los and (&&) y verdadero en los or (||), es decir se realiza una evaluación
en cortocircuito. La evaluación en cortocircuito podría tener efectos colaterales
sin embargo se pueden mencionar dos beneficios importantes:

 Se considera una optimización ya que en la mayoría de los casos ahorra


bastante trabajo en la evaluación expresiones.
 Una expresión relacional se puede utilizar para proteger una operación
potencialmente insegura en una segunda expresión, por ejemplo, en la
expresión (n != 0) && (x < 1 / n) nunca se podrá producir un error de división
entre 0 debido a que si por alguna razón n es igual a 0, el operando de la
izquierda (n!=0) se hace falsa y por lo tanto el operador de la izquierda no
se evalúa.

Muchos de los programas en C se basan en esa propiedad, por ejemplo



for(i=0;i<tamano && (c=getchar())!=EOF); i++)
buffer[i]=c;

Es decir antes de leer se verifica que hay espacio en el buffer.

A continuación se muestran varios programas que ejemplifican el uso de usos


de diferentes operadores:
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
int i,j,k=0;

i=j=1;
printf("i:%d j:%d k:%d\n",i,j,k);
printf("i+=2:%d j-=1:%d \n",i+=2,j-=1);
i=j=1;
printf("i:%d j:%d \n",i,j);

Octubre 2010 Página 33 de 89


Introducción al
Lenguaje de Programación C

printf("i++:%d ++j:%d \n",i++,++j);


printf("i>2:%d j==2:%d !k:%d\n",i>2,j==2,!k);
printf("(i=5,3+4):%d\n",(i=5,3+4));
printf("(i=1,j=(i==1?i*i:i+i)):%d\n",(i=1,j=(i==1?i*i:i+i)));
printf("Cuidado\n");
i=j=1;
printf("i:%d j:%d \n",i,j);
printf("(i==1||i++):%d\n",(i==1||i++));
printf("(j!=1&&++j):%d\n",(j!=1&&++j));
printf("Evitar\n");
printf("i:%d j:%d \n",i,j);
printf("(i=1,i=2?i*i:i+i):%d\n",(i=1,i=2?i*i:i+i));
printf("i:%d j:%d \n",i,j);
i=j=1;
printf("i:%d j:%d \n",i,j);
printf("(i---j):%d\n",(i---j));
printf("i:%d j:%d \n",i,j);
i=5;
j=4;
printf("i:%d j:%d \n",i,j);
printf("(i>j?i:j):%d\n",(i>j?i:j));return 0;
}

/* --- Fin ejemplo --- */

Evaluación en cortocircuito
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(int argc,char **argv){


int n,r;

for(n=-10;n<=10;n++)
if(n!=0 && (r=100/n))
printf("%3d %4d\n",n,r);
return 0;
}

/* --- Fin del programa --- */

Trabajando con bits


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

Octubre 2010 Página 34 de 89


Introducción al
Lenguaje de Programación C

#define SET(B,S) (B|=1<<S)


#define CLEAR(B,S)(B&=~(1<<S))
#define GET(B,S)(B>>S&1)

int main(void){
unsigned char b=0;

printf("%d\n",b);
SET(b,0);
printf("%d\n",b);
SET(b,2);
printf("%d\n",b);
CLEAR(b,0);
printf("%d\n",b);
printf("Get 2: %d\n",GET(b,2));
CLEAR(b,2);
printf("Get 2: %d\n",GET(b,2));
printf("%d\n",b);
SET(b,5);
printf("%d\n",b);
return 0;
}

/* --- Fin ejemplo --- */

Otros usos interesantes de los operadores de bits

Determinar si un número es par


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
int i;

printf("Ingrese un entero\n");
scanf("%d",&i);
printf("%d es %s\n",i,i&1?"Impar":"Par");
return 0;
}

/* --- Fin ejemplo --- */

Dividir entre dos un número entero


/*
* UCAB Guayana

Octubre 2010 Página 35 de 89


Introducción al
Lenguaje de Programación C

* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
int i;

printf("Ingrese un entero\n");
scanf("%d",&i);
printf("%d/2 %d\n",i,i>>1);
return 0;
}

/* --- Fin ejemplo --- */

Expresiones

Una expresión es una sucesión de operadores y operandos debidamente


relacionados, las expresiones combinan variables, constantes y llamadas a
funciones para producir nuevos valores, especificando los operadores lo que va a
ser realizado. Los operandos de diferente tipos que aparecen en una expresión se
convierten a un mismo tipo, tal cual como se especifica a continuación.

Conversión de tipo:
Implícita se realiza automáticamente por el compilador y a esta operación
se le denomina promoción:
 En una asignación se convierte el tipo al lvalue.
 En una expresión todos se convierte a un único tipo, usando el criterio de
convertirlos todos al tipo del mayor ―Tamaño‖
char o short  int
unsigned char o unsigned short  unsigned
int  unsigned  long  unsigned long  float  double

Explicita: se realiza a través uso del operador cast, es decir la solicita


específicamente el programador:

c = '0' + (char)num;

Octubre 2010 Página 36 de 89


Introducción al
Lenguaje de Programación C

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
int a=2,b=3;
float p;

p=(a+b)/2;
printf("%f\n",p);
p=(float)(a+b)/2;
printf("%f\n",p);
p=(a+b)/2.0;
printf("%f\n",p);
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 37 de 89


Introducción al
Lenguaje de Programación C

Instrucciones (sentencias ejecutables)


En sentido más general las instrucciones es una parte del programa que se puede
ejecutar, es decir, una instrucción (Sentencia o Proposición) especifica una
acción. En C las estructuras de control son sencillas (y familiares para
programadores que ya conozcan Pascal o cualquier otro lenguaje de
programación de alto nivel), permiten agrupar instrucciones, tomar decisiones (if-
else), seleccionar entre varias posibilidades (switch), hacer bucles (iteraciones)
evaluando la condición al empezar (while, for) o al acabar (do), etc.

En C hay cuatro tipos de instrucciones:


 Las de expresión.
 Las de bloques.
 De etiqueta.
 Las de control de flujo.

Instrucciones de expresión

Las instrucciones de expresión son aquellas en las que se especifica una expresión
válida seguida de un ―;‖.
A=5*6+8;
a+=b=1;
m=x>y?x:y;

Una instrucción de este tipo puede ser la llamada a una función seguida de ―;‖.
printf("Hello, world\n");

O la instrucción vacía
;

Instrucciones de bloques

Los bloques comienzan con una llave que abre y acaban con una llave que
cierra, conteniendo en su interior en primer lugar las posibles declaraciones
(locales al ámbito del bloque) y a continuación una lista de instrucciones.

Octubre 2010 Página 38 de 89


Introducción al
Lenguaje de Programación C

{
int i=0,j=1;

if(i && ++j)


printf("Entro\n");
printf("Valor de i:%d j:%d\n",i,j);
}

Observe que No hay ―;‖ luego de la ―}‖ que cierra el bloque.

En C99 se permite intercalar declaraciones e instrucciones dentro de un bloque.


{
int i;
i=2;
int j; // esta declaracion no es valida en C89
j=i*3;
printf("Los valores de i:%d y j:%d\n",i,j);
}

Instrucciones de etiqueta
La instrucción goto necesita de una etiqueta para operar, una etiqueta es un
identificador seguido de ―:‖ y pueda estar adherida a cualquier instrucción en la
misma función en la que está el goto, es decir el alcance de un goto es la función
y no se puede saltar entre funciones.
for(i=0;i<n;i++)
for(j=0;j<m;j++)
if(x[i]==y[j])
goto iguales;
...
iguales:
...

El código que involucra goto por lo general es mas difícil de entender y


mantener, adicionalmente se puede escribir códigos equivalentes sin él, aunque
tal vez con el precio adicional de variables extras y/o instrucciones de control
adicionales.
/*
* UCAB - Guayana
* Introduccion al Lenguaje C

Octubre 2010 Página 39 de 89


Introducción al
Lenguaje de Programación C

*/

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

int main(void){
int i=0;

printf("Inicio\n");
while(1){
for(;;){
if(i==10) goto salir;
printf("\tValor i:%d\n",i);
i++;
}
}
salir:
printf("Fin\n");
return 0;
}

Instrucciones de control de flujo


Van a permitir controlar el flujo de la ejecución de instrucciones, es decir
especifican el orden en que se realiza el procesamiento, y se clasifican en:
 Secuencia
 Las de selección
 Las de iteración
 Las de salto

Secuencia
En C el ―;‖ es un terminador de instrucción no un separador como en otros
lenguajes como Pascal. En C las instrucciones se ejecutan una después de
la otra, en el orden en que están escritas, es decir, en secuencia.
m=x>y?x:y;
tasa = 0.15 * pago;
; /* instruccion vacia */

Selección
C contempla los if y el switch como instrucciones de selección.
if(exp)
instrucción

Octubre 2010 Página 40 de 89


Introducción al
Lenguaje de Programación C

En el if , si la expresión exp es verdadera se ejecuta la instrucción,


que puede ser una instrucción sencilla (incluyendo la vacía) o de
bloque.
if (estado == 'P')
tasa = 0.17 * pago;

if(exp)
instrucción (verdadero)
else
instrucción (falso)
En el if-else, si la expresión es verdadera se ejecuta la instrucción
(verdadero) en caso contrario se ejecuta la instrucción (falso).
if (estado == 'P')
tasa = 0.17 * pago;
else
tasa = 0.25 * pago;

Los if se pueden anidar, es decir if dentro de if, en C89 se especifican


al menos 15 niveles de anidamiento. En if anidados el else se asocia
con el if más próximo que ya no esté asociado.

switch(exp){
case valor:
instrucciones
case valor:
instrucciones

[default:
instrucciones]
}
El switch es una instrucción de selección múltiple, que compara el
resultado de una expresión exp con una lista de valores (enteros o

Octubre 2010 Página 41 de 89


Introducción al
Lenguaje de Programación C

de carácter) especificada por los case en orden, cuando se


encuentra la correspondencia se ejecutan las instrucciones
asociadas con ese case, hasta que se encuentra un break o se llega
a la ultima instrucción del switch. Las instrucciones asociadas al
default (el cual es opcional) se ejecutan si no se ha encontrado
ninguna correspondencia.
/* Ejemplo: Imprime un color segun su codigo */
char color;
switch (color) {
case 'a':
case 'A': printf("AMARILLO\n");
break;
case 'r':
case 'R': printf("ROJO\n");
break;
case 'v':
case 'V': printf("VERDE\n");
break;
case 'b':
case 'B': printf("BLANCO\n");
break;
default: printf("CODIGO DESCONOCIDO\n");
}

Se pueden tener un switch formando parte de la secuencia de


instrucciones de otro switch.

Iteración
Las instrucciones de iteración (bucles) permiten ejecutar una instrucción o
un conjunto de ellas, mientras se cumpla una determinada condición. C
contempla tres instrucciones de iteración: while, do-while y for.
while (exp)
instrucción
En el while, la instrucción se ejecuta mientras la expresión sea
verdadera.
/* Ejemplo: Calculo de la media de un vector */
int vector[100], i = 0, media, suma = 0;
while (i < 100)
suma += vector [i++];

Octubre 2010 Página 42 de 89


Introducción al
Lenguaje de Programación C

media = suma / 100;

do
instrucción
while(exp);
En el do-while la instrucción se ejecuta mientras la expresión sea
verdadera, a diferencia del while, que analiza la condición del bucle
al principio, el bucle do-while comprueba la condición al final. Esto
significa que las instrucciones se ejecuta al menos una vez.
/* Ejemplo: Calculo de la media de un vector */
int vector[100], i, media, suma = 0;
i = 0;
do
suma += vector [i++];
while (i < 100);
media = suma / 100;
Aunque sea una sola instrucción, con frecuencia se colocan entre
llaves ―{‖ y ―}‖ por legibilidad del código.
int vector[100];

int main(void){
int i=0, media, suma = 0;

do{
suma += vector [i++];
}while (i < 100);
media = suma / 100;

return 0;
}

for(exp1;exp2;exp3)
instrucción

La instrucción for equivale directamente a lo siguiente:


exp1;
while (exp2) {
instrucción
exp3;
}

Octubre 2010 Página 43 de 89


Introducción al
Lenguaje de Programación C

/* Ejemplo: Calculo de la media de un vector */


int v[100], i, media, suma = 0;
for (i = 0; i < 100; i++)
suma += v[i];
media = suma / 100;

Dos casos interesantes del for puede ser bucles infinitos (aunque se
puede hacer con otras instrucciones)
for (;;){
...
/* un bucle infinito */
...
}

Y los sin cuerpos


for (i=0;i<5;i++); /* bucle sin cuerpo */

Saltos
C tiene cuatro instrucciones de salto incondicional: return, continue, break
y goto. Las instrucciones de return y goto se pueden utilizar en cualquier
parte de una función, mientas las instrucciones continue y break se utilizan
junto a instrucciones de iteración, Adicionalmente ya se estudio el uso del
break en los switch.
return [exp];
Retorna de una función, hace que la ejecución vuelva a punto en
que se realizó la llamada, cuando se acompaña de una expresión,
el valor de la expresión es el valor de vuelta de la función. Un return
sin expresión se utiliza para retornar de las funciones tipo void.

Continue;
Solo puede aparecer dentro de una instrucción de iteración y fuerza
una nueva iteración (a verificar la condición) del ciclo más anidado
que encierra la instrucción.

break ;

Octubre 2010 Página 44 de 89


Introducción al
Lenguaje de Programación C

Cuando esta dentro de una instrucción de iteración fuerza la


terminación inmediata del ciclo más anidado que encierra la
instrucción, pasando el control a la instrucción siguiente a la
terminada. Como se menciono anteriormente ya se estudio el uso
del break en los switch.

goto etiqueta;
Salta a una etiqueta dentro de la misma función, transfiriéndose el
control a la instrucción etiquetada.

exit(int)
Aunque no es una instrucción de control es una función de la
biblioteca estándar que termina la ejecución del programa cuando
se le llama, el argumento de exit está disponible para proceso de
llamada (se usa 0 para indicar la terminación normal del programa)
se tienen definido las siguientes macros EXIT_SUCESS y EXIT_FAILURE
como códigos de vueltas. La función exit requiere la inclusión del
archivo de cabecera <stdio.h>

A continuación se muestra estructuras de control de flujo, destaca el uso del


operador , (coma) en el for y la instrucción break en el while:

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
int i,j;

for(i=0,j=10;i<j;i++,j--)
printf("for: %d - %d\n",i,j);
j=0,i=10;
while(1)
if(i==5)

Octubre 2010 Página 45 de 89


Introducción al
Lenguaje de Programación C

break;
else
printf("while: %d\n",i--);
do
printf("do-while:%d\n",j);
while(j!=0);
}

/* --- Fin ejemplo --- */

Ahora se destaca el uso de la instrucción continue:


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
int i=0,j=100,k;

while(i<j){
printf("%d-%d\n",i,j);
i+=5;
j-=5;
if((k=j-i)>50){
printf("%d\n",k);
continue;
}
i+=5;
j-=5;
}
return 0;
}

/* --- Fin ejemplo --- */

Ahora se observa el uso de la instrucción return y la función exit

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

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

void funcion(int opcion){


switch (opcion) {
case 1:

Octubre 2010 Página 46 de 89


Introducción al
Lenguaje de Programación C

break;
case 2:
printf("Retornar al medio de la funcion\n");
return;
case 3:
printf("Salir del programa aqui\n");
exit(EXIT_FAILURE);
}
printf("Retornar por final de la funcion\n");
}

void siempre(void){
printf("Por aqui siempre paso\n");
}

int main(void){
atexit(siempre);
funcion(1);
funcion(2);
funcion(3);
printf("por aqui nunca paso\n");
exit(EXIT_SUCCESS);
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 47 de 89


Introducción al
Lenguaje de Programación C

Funciones y parámetros
El lenguaje C sólo permite funciones, no hay procedimientos. La forma de
declarar las funciones es la siguiente:

<tipo> identificador(lista parámetros formales){


declaraciones
enunciados
}

Donde tipo es el tipo del dato que devuelve la función, que si no aparece se
asume siempre del tipo int y nombre es el identificador de la función.

La forma de invocar a una función es similar a otros lenguajes, pero aunque la


función no tenga parámetros se deben colocar los paréntesis. Por otra parte, es
lícito no realizar ninguna acción con el resultado que devuelve una función, pero
en ese caso debiera ponerse un type cast (void) en la llamada.

Las listas de parámetros formales y de parámetros reales no tienen por qué


coincidir en número, e incluso en tipo. Para evitar este problema se puede
declarar el prototipo de la función antes de invocarla. La forma de escribir el
prototipo de una función es la siguiente:

<tipo> identificador (<tipo parámetro_1>, … <tipo parámetro_N>);

Sólo existe paso de parámetros por valor excepto los arreglos que pasan por
referencia.

Para terminar la ejecución de una función se usa la sentencia return. Su sintaxis es:

return [expresión];

Donde la expresión es el valor que retorna la función y debe ser del mismo tipo
que el de la función.
/*
* UCAB Guayana

Octubre 2010 Página 48 de 89


Introducción al
Lenguaje de Programación C

* Introduccion al Lenguaje C
*/

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

/* Ejemplo: Búsqueda binaria */


int busqueda_binaria(int elemento,int vector[], int tamano){
int bajo=0,alto=tamano-1,central,valor;

while(bajo<=alto){
central=(alto+bajo)/2;
valor=vector[central];
if(elemento==valor)
return central;
if(elemento < valor)
alto=central-1;
else
bajo=central+1;
}
return -1;
}

int main(void){
int valores[10]={1,5,8,9,10,23,25,30,40,50};
int elemento=12;

printf("Posición de %d es %d\n",elemento,
busqueda_binaria(elemento,valores,10));
exit(EXIT_SUCCESS);
}

/* --- Fin ejemplo --- */

C soporta el llamado recursivo de una función, es decir una función que se llame
a sí misma. La recursión es el proceso de definir algo en términos de sí mismo.

El siguiente programa resuelve el clásico problema de las torres de Hanói:


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

void hanoi(int n,char desde,char hasta,char usando){


if(n==1){
printf("%c --> %c\n",desde,hasta);
return;
}

Octubre 2010 Página 49 de 89


Introducción al
Lenguaje de Programación C

hanoi(n-1,desde,usando,hasta);
printf("%c --> %c\n",desde,hasta);
hanoi(n-1,usando,hasta,desde);
}

int main(void){
hanoi(3,'A','B','C');
return 0;
}

/* --- Fin ejemplo --- */

Ahora se puede observar usando el valor de retorno de la función, para mostrar la


cantidad de movimientos realizados:

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int hanoi(int n,char desde,char hasta,char usando){


int m;

if(n==1){
printf("%c --> %c\n",desde,hasta);
return 1;
}
m=hanoi(n-1,desde,usando,hasta);
printf("%c --> %c\n",desde,hasta);
m+=hanoi(n-1,usando,hasta,desde);
return m+1;
}

int main(void){
printf("movimientos:%d\n",hanoi(3,'A','B','C'));
return 0;
}

/* --- Fin ejemplo --- */

Las funciones pueden devolver valores de los tipos básicos o de los estructurados,
en las funciones que no retornen nada (procedimiento) el tipo devuelto se
declara como void. Así mismo, cuando no exista ningún parámetro formal, se
deben poner simplemente los dos paréntesis ( ) o (void).
/*

Octubre 2010 Página 50 de 89


Introducción al
Lenguaje de Programación C

* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

struct punto{
int x,y;
} p1;

/* funcion minima */
nada(){}

void saludo(void){
printf("Operaciones con puntos\n");
}

void imprime(struct punto p){


printf("punto: %d,%d\n",p.x,p.y);
}

struct punto suma(struct punto p1, struct punto p2){


struct punto t;

t.x = p1.x + p2.x;


t.y = p1.y + p2.y;
return t;
}
int main(int argc,char **argv){
struct punto p1 = {1,2},p2;

saludo();
imprime(p1);
p2=p1;
imprime(p2);
p2=suma(p1,p2);
imprime(p2);
nada();
return 0;
}

/* --- Fin del programa --- */

Octubre 2010 Página 51 de 89


Introducción al
Lenguaje de Programación C

Arreglos, Estructuras, Uniones y Enumeraciones


El lenguaje C proporciona varias formas para la creación de tipos de datos, ya
sean simples o compuestos, a partir de los tipos ya definidos.
Arreglos: Colección de variables del mismo tipo que se referencia por un
nombre en común, y un elemento especifico se accede mediante un
índice colocando el nombre de arreglo seguido del índice entre corchetes.
En C los arreglos tiene como 0 como el índice para su primer elemento.
Declaración:
tipo identificado[tamaño];
double balance[100];

Acceso al primer elemento:


balance[0] = 123.5;

C permite arreglos de más de una dimensión (multidimensionales), siendo


su forma de declaración la siguiente:
tipo identificador [tamaño1] [tamaño2]… [tamañoN]

Se puede observar que en la declaración cada dimensión se coloca en sus


corchetes, de igual forma se procede para acceder a un elemento en
particular. C permite inicializar los arreglos durante la inicialización con una
lista de elementos separados por coma encerados entre llaves. Se puede
hacer que el compilador calcule la dimensión de los arreglos para
mantener a todos los elementos de la inicialización, a esto se le denomina
arreglos no delimitados, donde no se especifica la dimensión.
char tablero[8][8];
int numero[]={1,2,4,3,9};
int i,cuad[5][2]={{1,1},{2,4},{3,9},{4,16},{5,25}};

for(i=0;i<5;i++)
printf("el cuadrado de %d es %d\n",cuad[i][0],cuad[i][1]);

En arreglos bidimensionales, se almacenan por filas y columnas (donde el

Octubre 2010 Página 52 de 89


Introducción al
Lenguaje de Programación C

primer índice indica las filas y el segundo las columnas) es decir que el
índice más a la derecha cambia más rápido que el de más a la izquierda
cuando se recorren los elementos en la memoria.
En C89 se deben declarar las dimensiones de los arreglos con dimensiones
constantes, sin embargo C99 permite la declaración de arreglos locales de
longitud variables.
int vector(int n){ // en C99 arreglos de longitud variable
int i,vector[n];

for(i=0;i<n;i
vector[i]=i;
return i;
}

Estructuras: Agrupación de miembros diferentes que se referencian bajo un


único nombre.
Struct datos_persona{
unsigned int codigo;
char nombre[30];
char apellido[30];
char sexo;
};

Los elementos que forman parte de una estructura se denomina miembros


(elementos o campos). La palara struct indica al compilador que se está
declarando un nuevo tipo, para declarar una o varias variables del tipo
añadido se puede hacer de de la siguiente forma:
struct datos_persona persona;

O se puede declarar una o más variables a la vez que se declarar la


estructura, como muestra el ejemplo:
struct datos_persona {
unsigned int codigo;
char nombre[30];
char apellido[30];
char sexo;
} persona;

Octubre 2010 Página 53 de 89


Introducción al
Lenguaje de Programación C

O si solo se necesitan las variables de la estructura, no se requiere el nombre


de la estructura:
struct {
unsigned int codigo;
char nombre[30];
char apellido[30];
char sexo;
} persona;

Las únicas operaciones permitidas sobre una estructura son asignarla, tomar
su dirección con &, tener acceso a sus miembros, pasarla como
argumentos a funciones y también regresarlas de funciones. Las estructuras
no se pueden comparar. Las estructuras se pueden inicializar con una lista
de variables constantes y una estructura automática también se puede
inicializar con una asignación.

El acceso a los miembros de una estructura se realizar con el operador .


(Punto) por ejemplo:
persona.sexo = ‘F’;

A través de una única asignación, se puede asignar el contenido de una


estructura a otra, no es necesaria la asignación de cada miembro por
separado.

Cuando se pasa una estructura a una función como un parámetro


realmente se pasa por valor la estructura completa.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

struct punto{
int x,y;
} p1;

void imprime(struct punto p){


printf("punto: %d,%d\n",p.x,p.y);
}

Octubre 2010 Página 54 de 89


Introducción al
Lenguaje de Programación C

struct punto suma(struct punto p1, struct punto p2){


struct punto t=p2;

t.x += p1.x;
t.y += p1.y;
return t;
}
int main(int argc,char **argv){
struct punto p1 = {1,2},p2;

imprime(p1);
p2=p1;
imprime(p2);
p2=suma(p1,p2);
imprime(p2);
return 0;
}

/* --- Fin del programa --- */

Uniones: Permite que una misma parte de la memoria sea definida como
dos o más tipos.
Union numero{
int entero;
float real;
}
Cuando una variable se declara como una union el compilador reserva
espacio para el mayor de los miembros. Pos su naturaleza las uniones
proporciona un mecanismo para interpretar una secuencia de bits de
diferentes formas. Y su declaración y uso es similar a las estructuras.

Campo Bits: tipo especial de estructura o unión que permite el fácil acceso
a bits individuales. Se declaran de la siguiente forma:

tipo nombre: longitud;

Y no es necesario nombrarlos, por ejemplo:


Struct estado{
unsigned: 4;
unsigned cts:1;
unsigned drs:1;
}

Octubre 2010 Página 55 de 89


Introducción al
Lenguaje de Programación C

Enumeraciones: Una lista de constantes enteros con nombres.


La declaración tendrá la siguiente forma:
enum nombre { lista_de_enumeracion } ;
Ejemplo:
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

enum dias{Domingo, Lunes, Martes, Miercoles, Jueves, Viernes,


Sabado};

int main(int argc,char **argv){


enum dias dia;

for(dia=Domingo;dia<=Sabado;dia++){
printf("%d\n",dia);
}
return 0;
}

/* --- Fin del programa --- */

En una enumeración cada una de los elementos se corresponde con un


valor entero, donde el primero se corresponde con el cero y los siguientes
se incrementan en uno sucesivamente, aunque se pueden definir otros
valores utilizando un inicializador (el signo de igualdad seguido de un valor
entero) a los elementos que siguen a uno inicializado se les hace
corresponder el valor previo incrementado en uno sucesivamente.
Como los elementos se corresponden con constantes enteras se puede
usarse en cualquier expresión entera.

Ejemplo usando estructuras y uniones:


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

Octubre 2010 Página 56 de 89


Introducción al
Lenguaje de Programación C

#include <stdio.h>

typedef unsigned int PALABRA;

typedef struct{
unsigned a:1;
unsigned b:1;
unsigned :4;
unsigned c:1;
unsigned d:1;

} SBITS;

typedef union{
PALABRA palabra;
SBITS bits;
}AMBOS;

int main(void){
AMBOS a;
a.palabra=0;
printf("Sizeof Palabra%d\n",sizeof(PALABRA));
printf("Sizeof SBITS%d\n",sizeof(SBITS));
printf("Sizeof UNION%d\n",sizeof(AMBOS));
printf("%d\n",a.palabra);
a.bits.c=1;
printf("%d\n",a.palabra);
return 0;
}

/* --- Fin ejemplo --- */

Ahora un ejemplo con arreglos:


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int buscar(int e,int a[],int t){


register int i;

for(i=0;i<t;i++)
if(a[i]==e)
return i;
return -1;
}

int main(void){
int a[]={2,3,6,8,10,12,23,15,22,1,9,7};
printf("Buscar 12:%d\n",buscar(12,a,12));
printf("Buscar 25:%d\n",buscar(25,a,12));
return 0;

Octubre 2010 Página 57 de 89


Introducción al
Lenguaje de Programación C

/* --- Fin ejemplo --- */

Octubre 2010 Página 58 de 89


Introducción al
Lenguaje de Programación C

Apuntadores
Los apuntadores son una de las características más útiles y a la vez más peligrosas
de que dispone el lenguaje C. En C se permite declarar una variable que
contiene la dirección de otra variable, o sea, un apuntador. Cuando se declara
un apuntador éste contiene una dirección arbitraria, si leemos a dónde apunta
nos dará un valor indefinido y si se escribe en tal dirección estamos variando el
contenido de una posición de memoria que no conocemos por lo que podemos
hacer que el sistema tenga comportamientos no deseados, por lo que antes de
hacer uso de un puntero debemos asignarle una dirección de memoria en
nuestro espacio de trabajo.

La forma de declarar un apuntador es la siguiente:

tipo *nombre;

Por ejemplo la declaración:

int i, *p=&i;

Se muestra gráficamente:

El tipo indica al tipo de datos a los que apuntará el apuntador, pero como efecto
de la declaración se reservará espacio en memoria para guardar un apuntador,
no para el tipo de datos al que apunta es decir la declaración de un apuntador
no lleva asociada la reserva de espacio para el tipo de datos apuntado.

Existe un carácter especial que se usa como prefijo y aplicado a las variables
indica la dirección de memoria que ocupa la variable, no el contenido (valor).
Este símbolo es &. Además existe otro prefijo, *, que aplicado a una variable de
tipo puntero indica el contenido de la dirección a la que apunta dicho
apuntador. A estos dos símbolos se les llama dirección e indirección (contenido)
respectivamente.

Octubre 2010 Página 59 de 89


Introducción al
Lenguaje de Programación C

A los apuntadores se les puede añadir o restar una cierta cantidad entera.
Admiten comparaciones e incrementos y decrementos. Cuando un apuntador es
incrementado en uno pasa a apuntar al siguiente elemento del arreglo al que
apuntaba, no al siguiente byte, es decir, se incrementa en el número de bytes
que ocupa el tipo al que apunta. También se permite restar dos punteros para
calcular la distancia entre ellos.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
int i,*ip;

i=0;
ip=&i;
*ip=5;
printf("i:%d\n",i);
return 0;
}

/* --- Fin ejemplo --- */

Un buen ejemplo para aritmética de apuntadores de la función que


calcula la longitud de una cadena de caracteres (string).
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>
#include <malloc.h>

char *ms = "HOLA MUNDO";

int mystrlen(const char *s){


const char *p = s;

while(*p)
p++;
return p-s;
}

int main(void){

Octubre 2010 Página 60 de 89


Introducción al
Lenguaje de Programación C

printf("ms:%s\n",ms);
printf("Longitud ms:%d\n",mystrlen(ms));
return 0;
}

/* --- Fin ejemplo --- */

Apuntadores a apuntadores

Se puede hacer un apuntador que apunte a otro apuntador que apunte a una
variable. Esta situación se denomina apuntador de apuntador o indirección
múltiples. Esta situación puede llevarse a la extensión que se desee por ejemplo
un apuntador a un apuntador a un apuntador, pero son pocos los casos que lo
requieran más allá de un apuntador a un apuntador, La indirección múltiple en
exceso puede resultar difícil de seguir y puede ser propensa a errores.

Por ejemplo la siguiente declaración:

int i=10 *p=&i, *pp=&p;

Se representa gráficamente:

Como se puede observar en la figura en el caso de un apuntador a un


apuntador, el primer apuntador contiene la dirección del segundo apuntador,
que apunta a la variable que contiene el valor deseado.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(int argc,char **argv){


int i=5,j=10,*p=&i,**pp=&p;

printf("valor de *p:%d\n", *p);


printf("valor de **pp:%d\n", **pp);
p=&j; /* No se cambia pp pero ... */
printf("valor de **pp:%d\n", **pp);
return 0;

Octubre 2010 Página 61 de 89


Introducción al
Lenguaje de Programación C

/* --- Fin del programa --- */

Un arreglo de apuntadores es un caso particular de apuntadores a apuntadores.

Relación entre apuntadores y arreglos

Los apuntadores y los arreglos en C están relacionados íntimamente y puede ser


utilizado casi en forma indistinta. Un nombre de arreglo puede considerarse como
un apuntador, los apuntadores pueden utilizarse para realizar cualquier operación
que involucre arreglos.

Por ejemplo si se declara un arreglo de enteros int ia[5]; y un apuntador a


entero int *iap; Dado que el nombre del arreglo si elemento es un apuntador
al primer elemento se hacer al apuntador igual a la dirección del primer elemento
iap=ia; (o más extraño iap=&ia[0];) alternativamente el elemento ia[3]
puede ser referenciado como *(ia+3)
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(int argc,char *argv[]){


int *iap, ia[5]={0,1,2,3,4};

iap=ia;
printf("ia[3]:%d\n",ia[3]);
printf("*(iap+3):%d\n",*(iap+3));
printf("*(3+iap):%d\n",*(3+ia));
printf("3[ia]:%d\n",3[ia]);
return 0;
}

/* --- Fin ejemplo --- */

Un NO buen ejemplo:

/*
* UCAB Guayana

Octubre 2010 Página 62 de 89


Introducción al
Lenguaje de Programación C

* Introduccion al Lenguaje C
*/

#include <stdio.h>

#define MAX 12

int a[MAX]={11,7,6,5,4,3,1,0},
b[MAX]={10,9,8,4,3,2,0},
c[2*MAX-1]={0};

void imprime(const int *x){


for(;*x;x++)
printf("%d ",*x);
printf("\n");
}

void merge(const int *f1,const int *f2,int *d){


while(*d++=*f1>*f2?*f1++:*f2++);
}

int main(void){
imprime(a);
imprime(b);
merge(b,a,c);
imprime(c);
return 0;
}

/* --- Fin ejemplo --- */

Puede resultar difícil de leer la función merge y si se detalla se puede observar


que no es del todo eficiente.
El siguiente ejemplo se evidencia la diferencian de usar arreglos y apuntadores,
en la manipulación de cadenas de caracteres (string), en nuestro caso en
particular la copia de una cadena de caracteres.
Empezamos implementado la función mystrcpy con arreglos.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>
char A[] = "HOLA MUNDO",B[12];

void mystrcpy(char d[],char f[]){


register int i;

Octubre 2010 Página 63 de 89


Introducción al
Lenguaje de Programación C

i=0;
while((d[i]=f[i])!='\0')
i++;
}

int main(void){
printf("A:%s\n",A);
mystrcpy(B,A);
printf("B:%s\n",B);
return 0;
}

/* --- Fin ejemplo --- */

Ahora con apuntadores, razone las ventajas de esta aproximación.


void mystrcpy(char *d,const char *f){
while((*d=*f)!='\0'){
d++;
f++;
}
}

Ahora un poco más eficiente, utilizando apuntadores.


void mystrcpy(char *d,const char *f){
while(*d++=*f++);
}

Aunque nos pueda parecer poco legible al principio, es importante


acostumbrase a este estilo de programación puesto que el que se
consigue con frecuencia en aplicaciones y ejemplos.

Como utilizar el calificador const con apuntadores

El calificador const permite informarle al compilador que el valor de una variable


en particular no deberá ser modificado. Existen cuatro formas de declara o pasar
un apuntador a una función:

Un apuntador no constante a datos no constantes

char *s

Un apuntador no constante a datos constantes

Octubre 2010 Página 64 de 89


Introducción al
Lenguaje de Programación C

const char *s

Un apuntador constante a datos no constantes

int * const ip
o
int i;
int * const ip=&i;

Un apuntador constante a datos constantes

const int * const ip

int i=5;
const int * const ip=&i;

Ejemplo intento de modificación de datos a través de un apuntador no constante


a datos constantes
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(void){
int i=5;
const int *ip=&i;

*ip=2; /* operacion invalida */


printf("%d",*ip);
return 0;
}

/* --- Fin ejemplo --- */

Arreglos de apuntadores

Al igual que de cualquier otro tipo de dato se permite declarar un array de


punteros. La forma de hacerlo es:

Octubre 2010 Página 65 de 89


Introducción al
Lenguaje de Programación C

tipo *nombre[expresion1][expresion2]…;
int *iap[5];
float *afp[];
void (*funciones[3])()={funcion1,funcion2,funcion3};

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

void print_error(const int num){


static char *err[]={
"Error numero cero",
"Error numero uno",
"Error numero dos",
"Error numero tres"
};

printf("Error (%d):%s\n",num,err[num]);
}

int main(void){
print_error(2);
return 0;
}

/* --- Fin ejemplo --- */

Arreglo de apuntadores vs. arreglos multidimensionales

Analicemos el siguiente programa


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

char *cpa[]={"Esta"," es"," una"," Prueba"};


char cba[][8]={"Esta"," es"," una"," Prueba"};

int main(void){
printf("sizeof(char *cpa[]):%d\n",sizeof(cpa));
printf("sizeof(char cba[][8]):%d\n",sizeof(cba));
printf("por la dudas %c es %c\n",cpa[3][1],cba[3][1]);
printf("*cpa[2]:%s\n",cpa[2]);
printf("cba[2]:%s\n",cba[2]);
return 0;

Octubre 2010 Página 66 de 89


Introducción al
Lenguaje de Programación C

/* --- Fin ejemplo --- */

Para cba , al ser un arreglo bidimensional, se reserva 4x8 sizeof(char) ahora para
cpa, al ser un arreglo de apuntadores a caracteres, se reserva 4xsizeof(char *). En
el caso en que cpa apunte a elementos de 8 sizeof(char) entonces se tiene 4x8x
sizeof(char) + 4xsizeof(char *). Ahora la ventaja del arreglo de apuntadores es que
los reglones pueden ser de longitudes diferentes.

Apuntadores a estructuras

Existe una pequeña variación a la hora de acceder a una estructura mediante un


apuntador, por ejemplo, (*p).miembro se puede escribir como p->miembro.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

struct campos{
char campo1;
int campo2;
int campo3[4];
} e, *ep, ea[4];

int main(void){
e.campo1='a';
e.campo2=25;

Octubre 2010 Página 67 de 89


Introducción al
Lenguaje de Programación C

ep=&e;
printf("campo1:%c\n",(*ep).campo1);
printf("campo2:%d\n",ep->campo2);
ea[2].campo3[1]=123;
printf("ea[2].campo3[1]:%d\n",ea[2].campo3[1]);
return 0;
}

/* --- Fin ejemplo --- */

Pase de apuntadores a funciones

El lenguaje C sólo admite paso de parámetros por valor, pero tiene una forma de
simular un paso por referencia (variable), pasando un apuntador que es la
dirección donde están los datos (p. ej. &v). En realidad se pasa un valor que es
una dirección de una variable.

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

void intercambia1(int x,int y){


int z;

z=x;
x=y;
y=z;
}

void intercambia2(int *x,int *y){


int z;

z=*x;
*x=*y;
*y=z;
}

int main(void){
int a=2,b=3;

printf("a:%d b:%d\n",a,b);
intercambia1(a,b);
printf("a:%d b:%d\n",a,b);
intercambia2(&a,&b);
printf("a:%d b:%d\n",a,b);

Octubre 2010 Página 68 de 89


Introducción al
Lenguaje de Programación C

return 0;
}

/* --- Fin ejemplo --- */

Apuntadores a funciones

Es posible crear apuntadores que apunten a funciones, en lugar de datos los


apuntadores a funciones apuntan a código ejecutable. Los apuntadores a
función permiten pasar a una función como argumento a otra función.

Un ejemplo de arreglo de funciones:


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

void funcion1(){
printf("Funcion 1\n");
}

void funcion2(){
printf("Funcion 2\n");
}

void funcion3(){
printf("Funcion 3\n");
}

int main(void){
void (*funciones[3])()={funcion1,funcion2,funcion3};
int opcion;

printf("Opcion (1-3) => ");


scanf("%d",&opcion);
if(opcion>=1 && opcion <=3)
(*funciones[opcion-1])();
else
printf("Error en opcion\n");
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 69 de 89


Introducción al
Lenguaje de Programación C

Paso de una función como argumento a otra función:

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

void funcion1(){
printf("Funcion 1\n");
}

void funcion2(){
printf("Funcion 2\n");
}

void funcion3(){
printf("Funcion 3\n");
}

void llamar_opciones(void (*funcion)()){


printf("Llamar opciones:");
(*funcion)();
}

int main(void){
void (*funciones[3])()={funcion1,funcion2,funcion3};
int opcion;

printf("Opcion (1-3) => ");


scanf("%d",&opcion);
if(opcion>=1 && opcion <=3)
llamar_opciones(funciones[opcion-1]);
else
printf("Error en opcion\n");
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 70 de 89


Introducción al
Lenguaje de Programación C

La biblioteca estándar
En el lenguaje C toda la entrada y salida de un programa se realiza a través de
funciones definidas en Bibliotecas. También se encuentran definidas en
bibliotecas otros tipos de funciones. Dichas bibliotecas, o la mayoría, son estándar
(las funciones en ellas definidas tienen nombres estándar) lo que facilita la
portabilidad de los programas. Al inicio de cada archivo se debe indicar las
declaraciones de las bibliotecas que se utilizarán. Esto se realiza utilizando la
directiva #include, cuya sintaxis es:

#include nombre_archivo

Donde nombre_archivo es el archivo de encabezado, donde se realizan las


definiciones de la bibliotecas, se coloca entre signos de menor y mayor
(<nombre_archivo>) o entre comillas dobles ("nombre_archivo") según el lugar en
que haya que buscar el archivo ya sea en los directorios asignados por defecto a
los archivos ―incluye‖ o en el actual.

Estos archivos de definiciones suelen tener la extensión .h (de header, cabeceras)


y contienen definiciones necesarias para la utilización de las funciones contenidas
en la biblioteca.

Por ejemplo:
#include <stdio.h>
Incluye la cabecera para usar la biblioteca de entrada/salida desde el directorio
estándar
#include "stdio.h"
Igual al anterior pero las busca en el directorio actual
#include "c:\proyecto\colas.h"
Incluye el archivo colas.h del directorio proyecto

Algunas librerías estándares importantes en ANSI C:


 conio.h Soporte para I/O por consola y puertos.

Octubre 2010 Página 71 de 89


Introducción al
Lenguaje de Programación C

 ctype.h Manejo de caracteres.


 errno.h Informes de error.
 float.h Valores de flotantes dependiente de la implementación.
 io.h Entrada y salida a bajo nivel.
 malloc.h Soporte para las funciones para obtener y liberar memoria.
 math.h Conjunto de funciones matemáticas.
 setjmp.h Soporte para saltos no locales
 signal.h Soporte para el manejo de señales.
 stdarg Soporte para lista de argumentos de longitud variable.
 stdio.h Entrada y salida estándar.
 stdlib.h Conjunto de funciones estándar, p.e. conversión de tipos.
 string.h Funciones de manejo de cadenas.
 time.h soporte para las funciones de tiempo del sistema

Adicionalmente dependiendo del Sistema de Operación se puede encontrar


biblioteca para el llamado a las primitivas del Sistema de Operación, en el caso
de UNIX, se tiene entre otras:
 signal.h para el manejo de señales del sistema
 sys/sem.h para el manejo de semáforos
 sys/shm.h manipulación de memoria compartida
 sys/socket manejo de manejo de socket

A continuación se muestra el uso de biblioteca <setjmp.h> para saltos no locales


/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>
#include <setjmp.h>

jmp_buf env;

void funcion4(void){
printf("Primera de Funcion4\n");
longjmp(env,1);
printf("Segunda de Funcion4\n");

Octubre 2010 Página 72 de 89


Introducción al
Lenguaje de Programación C

void funcion3(void){
printf("Primera de Funcion3\n");
funcion4();
printf("Segunda de Funcion3\n");
}

void funcion2(void){
printf("Primera de Funcion2\n");
funcion3();
printf("Segunda de Funcion3\n");
}

void funcion1(void){
printf("Primera de Funcion1\n");
funcion2();
printf("Segunda de Funcion1\n");
}

int main(void){
if(setjmp(env)==0){
printf("Ahora vamos a llamar las funciones\n");
funcion1();
}
else
printf("Regresamos con \"un salto no local\"\n");
printf("Ahora si salimos\n");
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 73 de 89


Introducción al
Lenguaje de Programación C

Entrada y Salida
Los principales archivos estándar definidos en toda aplicación en C son:
 stdin (entrada estándar, asociado al teclado)
 stdout (salida estándar, asociado a la pantalla)
 stderr (error estándar, asociado a la pantalla)

Las principales funciones de entrada y salida son:


 putchar(char c) Función de salida de carácter. Imprime el carácter c por
la salida estándar.
 char getchar(void) Entrada estándar de carácter. Obtiene un carácter de
la entrada estándar y este es el valor que devuelve la función.
 int printf(const char *format, ...) Imprime por la salida estándar una
secuencia de caracteres cuya estructura está definida en la string format.
Se permite dar salida a cualquier tipo de dato predefinido. La string format
tiene la estructura de una string normal pero admite además caracteres de
conversión (%) para indicar qué tipo de dato se ha de imprimir.
La estructura de una cadena de formato es:
%[flags][.width][.prec][F|N|h|l]type
donde type es un caracter de conversión. El campo flag afecta al tipo de
justificación; width a la anchura utilizada para imprimir; .prec a la precisión
a la hora de escribir números en coma flotante. El último campo opcional
afecta a la interpretación del argumento.
 %c Carácter
 %d Entero decimal
 %e Flotante se representa con exponente
 %f Flotante se representa sin exponente
 %g Menor entre %e y %f
 %o Entero octal, sin el cero inicial
 %u Entero decimal sin signo
 %x Entero representado en hexa sin 0x
 %s Strings (cadenas)

Octubre 2010 Página 74 de 89


Introducción al
Lenguaje de Programación C

 int scanf(const char *format, ...) Es el equivalente a printf para la entrada


estándar de datos de forma estructurada. En la cadena de formato se
pueden poner todos los caracteres de conversión (%) de la tabla anterior.
Además admite %[cadena], donde se acepta cualquier cadena formada
por elementos pertenecientes a cadena, o si la cadena comienza con '^',
los no pertenecientes y %*c carácter de supresión de asignación.
 FILE *fopen(const char *filename,const char *mode) Abre un fichero en alto
nivel. filename es el nombre del fichero y mode indica el modo de apertura
del fichero. Retorna un puntero al descriptor del fichero (FILE).
 "r" Abrir un archivo existente solo para lectura
 "w" Abrir un archivo solo para escritura
 "a" Abrir un archivo para añadir. Si no existe se crea uno
 "r+" Abrir un archivo existente para lectura y escritura
 "w+" Abrir un archivo nuevo para lectura y escritura
 "a+" Abrir un archivo nuevo para leer y añadir
 int fclose(FILE *fp) Cierra un fichero cuyo puntero al descriptor es fp.
 int fprintf(FILE *fp,const char *format, ...) Escribe en el fichero descrito por fp
datos con el formato indicado por format.
 int fscanf(FILE *fp,const char *format, ...) Lee del fichero descrito por fp.
Tanto esta llamada como la anterior son las equivalentes de printf y scanf
que operan sobre ficheros.

Octubre 2010 Página 75 de 89


Introducción al
Lenguaje de Programación C

Memoria Dinámica
Aunque el lenguaje NO define ninguna facilidad para asignación de
almacenamiento que no sea definición estática o automática (usando la pila) no
emplea el head y no define mecanismo de recolección de basura, la biblioteca
estándar facilita un sistema de gestión dinámica de memoria (utilizando el
montón) cuyo núcleo está compuesto por las funciones malloc() y free(). Estas
funciones trabajan junta para establecer y gestionar la memoria disponible. La
función malloc asigna memoria y la función free la libera.

Cualquier programa que utiliza las funciones malloc y free debe incluir el archivo
de cabecera <stdlib.h>. Los prototipos de ambas funciones son:

void *malloc(size_t numero_de_bytes)

Donde el número de bytes es la cantidad de memoria que se requiere y


malloc retorna un void * que se asignar a cualquier tipo de apuntador. Si
hay un fallo durante la asignación malloc retorna nulo (NULL).

La función free en la complementaria a malloc ya que devuelve al sistema


la memoria previamente asignada-

void free(void *p)

Ahora p es apuntador previamente asignado. Es muy importante no llamar


nuca a free con un argumento no valido, se dañaría el sistema de
asignación. Ahora hay otras funciones para la gestión de memoria, para
mayor información revisar las referencias del lenguaje.

A continuación se muestra un ejemplo del uso de memoria dinámica en la


implementación de la función mystrdup que obtiene una asignación de espacio
de memoria antes de realizar la copia a diferencia de la función mystrcpy que
asume que el apuntador de destino contiene un espacio asignado para realizar
la copia
/*
* UCAB Guayana
* Introduccion al Lenguaje C

Octubre 2010 Página 76 de 89


Introducción al
Lenguaje de Programación C

*/

#include <stdio.h>
#include <malloc.h>

char *B, *A = "HOLA MUNDO";

int mystrlen(const char *s){


const char *p = s;

while(*p)
p++;
return p-s;
}

void mystrcpy(char *d,const char *f){


while(*d++=*f++);
}

char *mystrdup(const char *f){


char *p;

p = (char *)malloc(mystrlen(f)+1);
if(p!=NULL)
mystrcpy(p,f);
return p;
}

int main(void){
printf("A:%s\n",A);
printf("Longitud A:%d\n",mystrlen(A));
B = mystrdup(A);
printf("B:%s\n",B);
return 0;
}

/* --- Fin ejemplo --- */

Como se puede observar los apuntadores proporcionan el soporte


necesario para la asignación de memoria dinámica. La asignación
dinámica es la forma como un programa puede obtener, de forma
explícita, memoria mientras se está ejecutando. Lo cual puede resultar útil
cuando el programa no conoce por anticipado las necesidades de
almacenamiento.

Octubre 2010 Página 77 de 89


Introducción al
Lenguaje de Programación C

Funciones con un número de parámetros variables


En C es posible crear funciones que reciban un número no especificado de
argumentos, un buen ejemplo puede ser la función printf. En el prototipo de la
función los puntos suspensivos (…) indica que la función recibe un número
variable de argumentos de cualquier tipo:
int printf(const char *, …);

Las macros y definiciones necesarias para construir funciones con un número de


parámetros variables, se encuentran en archivo de encabezados <stdarg.h>
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>
#include <stdarg.h>

double avg(int n, ...){


double total = 0;
int register i;
va_list ap;

va_start(ap,n);
for(i=1;i<=n;i++)
total+=va_arg(ap,double);
va_end(ap);
return total/n;
}

int main(void){
double a=1.0,b=2.0,c=3.0,d=4.0;

printf("avg con 2 parametros:%f\n",avg(2,a,b));


printf("avg con 3 parametros:%f\n",avg(3,a,b,c));
printf("avg con 4 parametros:%f\n",avg(4,a,b,c,d));
printf("avg con 5
parametros:%f\n",avg(5,5.0,10.0,15.0,20.0,25.0));
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 78 de 89


Introducción al
Lenguaje de Programación C

Pase de parámetros a la función main


En los entornos que maneja C, hay una forma de transmitir al programa los
argumentos de la línea de comando. Cuando se invoca al main al comienzo de
la ejecución, se llama con dos argumentos. El primero (argc) es el número de
argumentos en la línea de comando con que se invoco al programa; el segundo
(argv) es un apuntador a un arreglo de cadenas de caracteres, que contiene los
argumentos uno en cada una de las cadena.

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

int main(int argc,char *argv[]){


if(argc>0)
printf("Argumento 1 %s\n",*argv++);
if(argc>1)
printf("Argumento 2 %s\n",*argv++);
if(argc>2)
printf("Argumento 3 %s\n",*argv++);
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 79 de 89


Introducción al
Lenguaje de Programación C

El preprocesador
El preprocesador se ejecuta al comenzar el proceso de compilación, es decir una
fase previa a éste, y se encarga de realizar ciertas tareas básicas; entre estas
tareas están la inclusión de archivos, expansión de macros y procesar de
directivas.

Las directivas del preprocesador empiezan por # y las principales son:


 #include Incluir archivos.
 #define Declara una macro o una constante
 #undef Borra una macro de la tabla de definiciones.
 #if, #ifdef, #ifndef, #else, #elif y #endif
Son utilizadas para Compilación Condicional. Se puede
preguntar por el valor de una constante o la existencia o no
de una macro. En caso de ser cierta la condición se compila
el código entre #if y #else, en caso de ser falsa se compila el
código entre #else y #endif.
 #error, #line, #pragma

Consideraciones sobre macros

Debido a que el preprocesador de macros trabaja por simple reemplazo, se


pueden cometer algunos errores, para evitarlos se siguen unas simples
convenciones:

Colocar paréntesis alrededor de los argumentos siempre que sea posible:


Incorrecto: #define square(x) x*x
Mejor: #define square(x) (x)*(x)
Colocar paréntesis alrededor de la expresión entera, para que el orden de las
operaciones no afecte el resultado.
Incorrecto: #define square(x) (x)*(x)
Mejor: #define square(x) ((x)*(x))

Octubre 2010 Página 80 de 89


Introducción al
Lenguaje de Programación C

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

#define MAX(X,Y) ((X)>(Y)?(X):(Y))

int main(void){
int a,b;

printf("Un entero:");
scanf("%d",&a);
printf("Otro entero:");
scanf("%d",&b);
#ifdef DEPURAR
printf("valor a:%d\n",a);
printf("valor b:%d\n",b);
#endif
printf("Maximo entre %d y %d es %d\n",a,b,MAX(a,b));
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 81 de 89


Introducción al
Lenguaje de Programación C

Llamadas a otros lenguajes de programación


C soporta compilación separada, y posteriormente se enlazan los diferentes
módulos para crear el ejecutable, ahora no necesariamente todos los módulos
deben estar programado en C, como es el caso que se muestra a continuación,
donde un programa escrito en lenguaje C invoca a una función escrita en
fortran77.

Archivo en C
#include <stdio.h>

extern void fortfunc_(int *a, int *b);

int main(){
int a=1,b=2;

printf("Antes a:%d b:%d\n",a,b);


fortfunc_(&a, &b);
printf("Despues a:%d b:%d\n",a,b);
return 0;
}

Archivo en Fortran77
subroutine fortfunc(i,j)
integer i
integer j

i=5
j=6

return

Octubre 2010 Página 82 de 89


Introducción al
Lenguaje de Programación C

end

Salida
E:\Pruebas
Antes a:1 b:2
Despues a:5 b:6

Octubre 2010 Página 83 de 89


Introducción al
Lenguaje de Programación C

Caso de estudio: implementar un comando del sistema


El lenguaje C es utilizado en para programar partes del sistema de operación y/o
utilidades, a continuación se muestra la implementación de un comando del
sistema cat que escribirá a la salida estándar el contenido de cada uno de los
archivos dados como argumentos, en el mismo orden en el que fueron dados. Si
no se especifica ningún archivo, leerá sólo de la entrada estándar

/*
* UCAB
* Introduccion al Lenguaje C
* Comando cat como en UNIX
*/

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

/*
* Copia el archivo fp en la salida estandar
*/

void filecopy(FILE *fp){


int c;

while((c=getc(fp))!=EOF)
putc(c,stdout);
}

int main(int argc, char* argv[]){


FILE *fp;

if(argc==1)
filecopy(stdin);
else
while(--argc>0)
if((fp=fopen(*++argv,"r"))==NULL){
fprintf(stderr,"cat: can't open %s\n",*argv);
exit(EXIT_FAILURE);
}
else{
filecopy(fp);
fclose(fp);
}
exit(EXIT_SUCCESS);
}

/* fin del ejemplo */

Octubre 2010 Página 84 de 89


Introducción al
Lenguaje de Programación C

Algoritmos y aplicaciones
A continuación se muestran algunos programas que pueden ser de interés
Implementar conjuntos con bits

/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/

#include <stdio.h>

#define METER(B,S) (B|=1<<S)


#define SACAR(B,S)(B&=~(1<<S))
#define PERTENECE(B,S)(B>>S&1)

typedef unsigned long Conjunto;

Conjunto C_vaciar(void){
return 0;
}

Conjunto C_meter(Conjunto c, int e){


if(e>=0 && e<sizeof(Conjunto)*8)
return (c|1<<e);
return c;
}

Conjunto C_sacar(Conjunto c, int e){


if(e>=0 && e<sizeof(Conjunto)*8)
return (c&~(1<<e));
return c;
}

int C_pertenece(Conjunto c,int e){


if(e>=0 && e<sizeof(Conjunto)*8)
return (c>>e&1);
return 0;
}

void C_imprime(Conjunto c){


register int i;

printf("{ ");
for(i=0;i<sizeof(Conjunto)*8;i++,c>>=1)
if(c&1)
printf("%d ",i);
printf("}\n");
}

Conjunto C_union(Conjunto A, Conjunto B){

Octubre 2010 Página 85 de 89


Introducción al
Lenguaje de Programación C

return A|B;
}

Conjunto C_interseccion(Conjunto A, Conjunto B){


return A&B;
}

int main(void){
char *r[2]={"No","Si"};
Conjunto A,B,C;

A=C_vaciar();
B=C_vaciar();
A=C_meter(A,0);
A=C_meter(A,5);
A=C_meter(A,10);
B=C_meter(B,10);
B=C_meter(B,15);
C=C_union(A,B);
printf("A:");
C_imprime(A);
printf("B:");
C_imprime(B);
printf("A Union B:");
C_imprime(C);
printf("A Interseccion B:");
C_imprime(C_interseccion(A,B));
C=C_sacar(C,10);
printf("C:");
C_imprime(C);
printf("%d %s pertenece a C",5,r[C_pertenece(C,5)]);
return 0;
}

/* --- Fin ejemplo --- */

Octubre 2010 Página 86 de 89


Introducción al
Lenguaje de Programación C

Bibliografía recomendada
 Kernigan Brian & Ritchie Dennis, El lenguaje de programación C, Segunda
edición, Prentice Hall, 1988.
 Deitel P.J. & Deitel H.M., Como programar en C/C++, Prentice Hall, 1995.
 American National Standards Institute, Programming Language C, ANSY
X3.159-1989.
 Schildt Herbert, C Manual de referencia, Cuarta edición, McGraw-Hill, 2001.
 Joyanes A. Luis, Castillo S. Andrés, Sánchez G. Lucas & Zahonero M. Ignacio,
Programación en C – Libro de Problemas, McGraw-Hill, 2002.

Octubre 2010 Página 87 de 89


Introducción al
Lenguaje de Programación C

ANEXOS

Octubre 2010 Página 88 de 89


Introducción al
Lenguaje de Programación C

Anexo A
Ejemplo de algunas declaraciones que pueden ser de ayuda y utilidad para
entender o hacer declaraciones, sobre todo cuando se utilizan apuntadores.

int i; // integer variable


int *p; // pointer to integer variable
int a[]; // array of integer
int f(); // function with return value integer
int **pp; // pointer to pointer to integer
int (*pa)[]; // pointer to an array of integer
int (*pf)(); // pointer to a function with return value integer
int *ap[]; // array of pointers to integer
int *fp(); // function, which returns a pointer to an integer
int ***ppp; // pointer to a pointer to a pointer to integer
int (**ppa)[]; // pointer to a pointer to an array of integer
int (**ppf)(); // pointer to a pointer to a function with return value integer
int *(*pap)[]; // pointer to an array of pointers to integer
int *(*pfp)(); // pointer to function with return value pointer to integer
int **app[]; // array of pointer to pointer to integer
int (*apa[])[];// array of pointers to array of integer
int (*apf[])();// array of pointers to functions with return value integer
int ***fpp(); // function with return value pointer to pointer to pointer to int
int (*fpa())[];// function with return value pointer to array of integers
int (*fpf())();// function with return value pointer to function, which returns an
integer

Octubre 2010 Página 89 de 89

You might also like