Professional Documents
Culture Documents
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
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
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.
Compilador
Objeto Objeto Objeto Librerias
Enlazador
Ejecutable
[Depurador] Aplicacion.exe
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).
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;
}
siendo su salida
Hello, world
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.
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.
Modificadores:
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.
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:
Donde tipo es cualquier tipo de datos valido, ya sea un tipo base o una
estructura, por ejemplo:
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
int main(void){
int a;
float f;
int n = 123;
Se puede distinguir
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).
Declaración de variables
La forma general de declaración es:
<tipo> lista_de_variables;
int numero1=5,numero2;
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.
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.
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 funcion2(void){
int numero1, numero2; Variable local
int funcion3(void){
int numero1, numero2;
if (numero1==numero2){
int numero1, numero2; Variable Local (Bloque)
}
}
#include <stdio.h>
int a=0;
int main(void){
int a=1;
Calificadores de acceso
Controlan las formas que se acceden o se modifican las variables.
const
No pueden ser modificadas por el programa, solo inicializar.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
int main(void){
const unsigned char b=0xFF;
nocambiar(b);
printf("%d\n",b);
return 0;
}
volatile
El valor de una variable puede cambiar por medios no explícitamente
especificados por el programa.
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.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*
* archivo: modulo1.c
*/
#include <stdio.h>
#include <stdlib.h>
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;
}
/*
* 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);
}
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.
/*
* 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;
}
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;
}
int main(void){
funcion1();
funcion1();
funcion1();
funcion2();
funcion2();
funcion2();
return 0;
}
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*/
"HOLA MUNDO"
Enumeradas
Operadores y expresiones
El lenguaje C es sumamente rico en operadores incorporados, entre los que se
tienen:
Asignació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)
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
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
Evaluación en Cortocircuito
#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);
Evaluación en cortocircuito
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
for(n=-10;n<=10;n++)
if(n!=0 && (r=100/n))
printf("%3d %4d\n",n,r);
return 0;
}
#include <stdio.h>
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;
}
#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;
}
* 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;
}
Expresiones
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
c = '0' + (char)num;
/*
* 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;
}
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.
{
int i=0,j=1;
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:
...
*/
#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;
}
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
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;
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
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++];
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
Dos casos interesantes del for puede ser bucles infinitos (aunque se
puede hacer con otras instrucciones)
for (;;){
...
/* un bucle infinito */
...
}
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 ;
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>
/*
* 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)
break;
else
printf("while: %d\n",i--);
do
printf("do-while:%d\n",j);
while(j!=0);
}
#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;
}
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
#include <stdlib.h>
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);
}
Funciones y parámetros
El lenguaje C sólo permite funciones, no hay procedimientos. La forma de
declarar las funciones es la siguiente:
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.
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
* Introduccion al Lenguaje C
*/
#include <stdio.h>
#include <stdlib.h>
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);
}
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.
#include <stdio.h>
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;
}
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
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;
}
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).
/*
* 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");
}
saludo();
imprime(p1);
p2=p1;
imprime(p2);
p2=suma(p1,p2);
imprime(p2);
nada();
return 0;
}
for(i=0;i<5;i++)
printf("el cuadrado de %d es %d\n",cuad[i][0],cuad[i][1]);
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;
}
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.
#include <stdio.h>
struct punto{
int x,y;
} p1;
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;
}
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:
#include <stdio.h>
for(dia=Domingo;dia<=Sabado;dia++){
printf("%d\n",dia);
}
return 0;
}
#include <stdio.h>
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;
}
#include <stdio.h>
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;
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.
tipo *nombre;
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.
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;
}
#include <stdio.h>
#include <malloc.h>
while(*p)
p++;
return p-s;
}
int main(void){
printf("ms:%s\n",ms);
printf("Longitud ms:%d\n",mystrlen(ms));
return 0;
}
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.
Se representa gráficamente:
#include <stdio.h>
#include <stdio.h>
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;
}
Un NO buen ejemplo:
/*
* UCAB Guayana
* 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};
int main(void){
imprime(a);
imprime(b);
merge(b,a,c);
imprime(c);
return 0;
}
#include <stdio.h>
char A[] = "HOLA MUNDO",B[12];
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;
}
char *s
const char *s
int * const ip
o
int i;
int * const ip=&i;
int i=5;
const int * const ip=&i;
#include <stdio.h>
int main(void){
int i=5;
const int *ip=&i;
Arreglos de apuntadores
tipo *nombre[expresion1][expresion2]…;
int *iap[5];
float *afp[];
void (*funciones[3])()={funcion1,funcion2,funcion3};
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
printf("Error (%d):%s\n",num,err[num]);
}
int main(void){
print_error(2);
return 0;
}
#include <stdio.h>
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;
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
#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;
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;
}
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>
z=x;
x=y;
y=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);
return 0;
}
Apuntadores a funciones
#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;
/*
* 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;
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
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
#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");
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;
}
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)
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:
*/
#include <stdio.h>
#include <malloc.h>
while(*p)
p++;
return p-s;
}
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;
}
#include <stdio.h>
#include <stdarg.h>
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;
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
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.
/*
* UCAB Guayana
* Introduccion al Lenguaje C
*/
#include <stdio.h>
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;
}
Archivo en C
#include <stdio.h>
int main(){
int a=1,b=2;
Archivo en Fortran77
subroutine fortfunc(i,j)
integer i
integer j
i=5
j=6
return
end
Salida
E:\Pruebas
Antes a:1 b:2
Despues a:5 b:6
/*
* UCAB
* Introduccion al Lenguaje C
* Comando cat como en UNIX
*/
#include <stdio.h>
#include <stdlib.h>
/*
* Copia el archivo fp en la salida estandar
*/
while((c=getc(fp))!=EOF)
putc(c,stdout);
}
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);
}
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>
Conjunto C_vaciar(void){
return 0;
}
printf("{ ");
for(i=0;i<sizeof(Conjunto)*8;i++,c>>=1)
if(c&1)
printf("%d ",i);
printf("}\n");
}
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;
}
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.
ANEXOS
Anexo A
Ejemplo de algunas declaraciones que pueden ser de ayuda y utilidad para
entender o hacer declaraciones, sobre todo cuando se utilizan apuntadores.