Upload
david-grilli
View
2.297
Download
0
Embed Size (px)
Citation preview
UNIVERSIDAD CATOLICA ANDRES BELLO
GUAYANA
Introducción al
Lenguaje de Programación C
Jesús J. Lárez M.
Octubre 2010
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 2 de 89
"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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 3 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 4 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 5 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 6 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 7 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 8 de 89
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:
Ejecutable
Aplicacion.exe
Fuente
colas.c
Encabezado
colas.h
Objeto
colas.obj
Fuente
graficos.c
Encabezado
graficos.h
Objeto
graficos.obj
Fuente
Principal.c
Encabezado
colas.h
Objeto
Principal.obj
Encabezado
graficos.h
Encabezado
stdio.h
Librerias
.lib
Preprocesador
Compilador
Enlazador
Editor
[Depurador]
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 9 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 10 de 89
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)
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 11 de 89
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
tipo función(parámetros){
Declaraciones locales
secuencia de instrucciones
}
int main(int argc, char **argv){
Declaraciones locales
secuencia de instrucciones
}
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
int main(void){
printf("Hello, world\n");
return 0;
}
/* --- Fin ejemplo --- */
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 12 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 13 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 14 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 15 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 16 de 89
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>
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 17 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 18 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 19 de 89
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).
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 20 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 21 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 22 de 89
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.
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;
#include <stdio.h>
int numero1, numero2;
int funcion1(int numero1,int numero2){
}
int funcion2(void){
int numero1, numero2;
}
int funcion3(void){
int numero1, numero2;
if (numero1==numero2){
int numero1, numero2;
}
}
Variable Global
Parámetro Formal
Variable local
Variable Local (Bloque)
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 23 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 24 de 89
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;
char c;
int main(){
x=123;
}
extern int x,y;
extern char c;
void func23(void){
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 cuentas=0;
cuentas++;
return cuentas;
}
static int cuentas=0;
int veces(void){
cuentas++;
return cuentas;
}
A continuación se muestra un ejemplo de compilado por separado, note
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 25 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 26 de 89
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;
}
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 27 de 89
int main(void){
funcion1();
funcion1();
funcion1();
funcion2();
funcion2();
funcion2();
return 0;
}
/* --- Fin ejemplo --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 28 de 89
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*/
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 29 de 89
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};
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 30 de 89
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)
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 31 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 32 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 33 de 89
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);
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 34 de 89
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>
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 35 de 89
#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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 36 de 89
* 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;
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 37 de 89
/*
* 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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 38 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 39 de 89
{
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 40 de 89
*/
#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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 41 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 42 de 89
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++];
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 43 de 89
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;
}
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 44 de 89
/* 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 ;
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 45 de 89
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)
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 46 de 89
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:
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 47 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 48 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 49 de 89
* 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;
}
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 50 de 89
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).
/*
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 51 de 89
* 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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 52 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 53 de 89
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;
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 54 de 89
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);
}
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 55 de 89
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;
}
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 56 de 89
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
*/
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 57 de 89
#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;
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 58 de 89
}
/* --- Fin ejemplo --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 59 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 60 de 89
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){
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 61 de 89
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;
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 62 de 89
}
/* --- 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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 63 de 89
* 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;
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 64 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 65 de 89
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
O
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:
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 66 de 89
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;
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 67 de 89
}
/* --- 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;
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 68 de 89
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);
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 69 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 70 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 71 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 72 de 89
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");
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 73 de 89
}
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 74 de 89
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)
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 75 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 76 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 77 de 89
*/
#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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 78 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 79 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 80 de 89
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))
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 81 de 89
/*
* 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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 82 de 89
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
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 83 de 89
end
Salida
E:\Pruebas
Antes a:1 b:2
Despues a:5 b:6
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 84 de 89
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 */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 85 de 89
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){
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 86 de 89
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 --- */
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 87 de 89
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.
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 88 de 89
ANEXOS
Introducción al
Lenguaje de Programación C
Octubre 2010 Página 89 de 89
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