Guía Completa de Programación en C
Guía Completa de Programación en C
#include <stdio.h>
int main() {
/* comentario (modalidad propia de c) */
// comentario (esta modalidad de dos ‘/’ es tomada del c++)
printf(“Hola Mundo\n”); //secuencia de escape (salto de linea)
printf(“Hola de nuevo”);
return 0;
}
Secuencias de escape
\b -> retrocede un carácter (backspace)
\a -> campanilla
\t -> tabulador (se imprimen 8 espacios en blanco)
2) Tipo de dato:
a) int (números enteros)
b) char (caracteres, un solo carácter)
c) float (números con decimales, hasta 7 decimales exactos)
d) double (números con decimales, hasta 15 decimales exactos)
e) void (sin valor, solo para funciones que no retornen
valores)
#include <stdio.h>
int main() {
int a, b, c;
float pi;
char letra;
a = 3; //estoy asignando una constante
b = a; //estoy asignando el valor de otra variable
c = a + b + 1; //estoy asignando una expresión (en c se guarda
//el resultado de esa expresión)
pi = 3.1416;
letra = ‘a’; //asigno la letra ‘a’ (usando comillas simples)
printf(“La variable a contiene %d, b contiene %d y c contiene
%d\n”, a, b, c);
printf(“La variable pi contiene %f y letra contiene %c\n”, pi,
letra);
return 0;
}
%4d -> el 4 hace las veces de ancho (cuantas columnas de pantalla uso
para ver ese número, si sobran columnas, se ponen adelante del número, si
el ancho fuera muy chico para el número, se ignora y el número se muestra
completo).
%04d -> en lugar de rellenar con espacios en blanco, rellena lo que falte
con ceros.
int main() {
int a = 10;
float b;
b = a/3;
printf(“%f”, b); //muestra: 3.000000, en lenguaje c dividir
dos //números enteros da como resultado un entero
return 0;
}
Estructuras de Control
Estructura Secuencial: de arriba hacia abajo, secuencial o lineal
Estructura Alternativa o Condicional: una condición solo puede valer
verdadero o falso.
Formato 1:
if (<condición>){
//si condición es verdadera, ingresa a este bloque
}
//si condición es falsa, no entra al bloque del if
Formato 2:
if (<condición>){
//si condición es verdadera, ingresa a este bloque
} else {
//si condición es falsa, entra a este bloque
}
Operadores relacionales:
´==´ igual ‘!=’ distinto
‘>’ mayor ‘<’ menor ‘>=’ mayor o igual ‘<=’ menor o igual
0 -> falso (el cero se considera como falso)
!0 -> verdadero (cualquiera distinto de cero es verdadero)
Operadores Lógicos:
&& and || or ! not
Operador Condicional
Se aplica sobre tres operandos
(<condición>) ? <valor1>:<valor2>;
Si condición es verdadera, devuelve valor1. Si condición es falsa,
devuelve valor2;
b = (a<0)?-a:a; (siempre devuelve el positivo, es como la función modulo)
Elecciones Múltiples
switch (<variableselectora>){
case <valor1>:
….;
….;
break;
case <valor2>:
….;
….;
break;
default:
….;
….;
break;
}
variableselectora: debe ser entera o char
#include<stdio.h>
int main(){
int mes;
printf(“Ingrese el mes”);
scanf(“%d”,&mes);
switch(mes){
case 1: printf(“enero”);
break;
case 2: printf(“febrero”);
break;
default: printf(“mes invalido”);
break;
}
return 0;
}
Funciones
#include <stdio.h>
int potencia(int a, int b); //prototipo de la función
int main(){
#include <stdio.h>
void duplicar(int * n);
int main(){
int a;
printf(“ingrese un numero entero”);
scanf(“%d”,&a);
duplicar(&a); //paso la dirección de memoria
printf(“el doble es %d\n”, a);
return 0;
}
void duplicar(int * n){ //recibe un puntero a entero
*n = *n * 2;
}
Pasaje de parámetros
1) Por referencia: pila, cola, arreglo.
El parámetro formal ocupa la misma zona de memoria que el
parámetro real, cuando cambio uno, se cambia el otro.
2) Por Valor: variable simple.
El parámetro formal ocupa un espacio de memoria diferente al del
parámetro real, lo único que envío a la función es el valor.
Punteros
Un puntero es una variable que contiene una dirección de memoria. El
puntero apunta a la variable. Guarda la dirección de memoria donde
comienza la variable a la que apunto. Un puntero es una categoría de
variable, no un tipo de dato.
Operadores de puntero
‘&’ operador de dirección, aplicado a una variable devuelve la dirección
que esta variable ocupa en memoria.
‘*’ operador de indirección, aplicado a un puntero devuelve el valor
almacenado en la zona de memoria a la que apunta el puntero. Solo se
puede aplicar a variables de tipo puntero.
#include <stdio.h>
int main(){
int a, b;
int *ptr;
a=3;
ptr= &a;
b= *ptr;
printf(“%d”, b); //voy a ver 3 en pantalla
return 0;
}
#include <stdio.h>
int main(){
int a, *ptr;
a=3;
ptr= &a; //la dirección de memoria de a vale 1000, por ej.
*ptr = *ptr + 1;
//si quisiera usar++: (*ptr)++
printf(“%d”, a); //voy a ver 4 en pantalla
printf(“%p”, ptr); //voy a ver 1000 en pantalla
//usando %p voy a ver un valor hexadecimal
return 0;
}
Usos de punteros:
1) Pasaje de parámetros por referencia
2) Trabajar con arreglos y cadenas de caracteres
3) Utilizar asignación dinámica de memoria
#include <stdio.h>
void intercambiar(int * a, int * b);
int main(){
int x,y;
x=3;
y=5;
intercambiar(&x, &y);
printf(“%d, %d\n”, x, y);
return 0;
}
void intercambiar(int * a, int * b){
int c;
c = *a;
*a = *b;
*b = c;
}
Arreglos
-Vectores: conjunto de variables (del mismo tipo de datos) agrupadas bajo
el mismo nombre, cada variable tiene un numero de orden (la posición
dentro del vector), subíndice (números enteros, comienzan desde cero),
dentro del contexto del vector.
int vec1[10]; //10 es el tamaño del vector, cantidad de variables que
//el vector admite.
int vec2[5] = {10, 20, 30, 40, 50}; //declaración e inicialización a
//la vez.
int vec3[100] = {10, 20, 30}; //inicializo solo 3 valores e
inicializa usando esos tres
valores hasta que se terminen,
luego pone valores nulo
(correspondiente al tipo de dato
del vector).
int vec4[2] = {10, 20, 30, 40}; //declaro más valores del tamaño
del vector, da error, no compila
int vec5[] = {10, 20, 30, 40}; //arreglo abierto, inicializa
con tamaño 5.
#include <stdio.h>
#define MAX 100 // las constantes simbólicas se acostumbran escribir
// con mayúsculas, sin ‘;’ y sin ‘=’
int main(){
int i, suma = 0, v[MAX];
float prom;
for (i=0; i<MAX; i++){
printf(“Ingrese un numero entero”);
scanf(“%d”, &v[i]);
suma = suma + v[i];
}
prom = (float)suma/MAX;
for (i=0; i<MAX; i++){
if (v[i]>prom)
printf(“%4d”,v[i]);
}
return 0;
}
Aritmética de punteros
Se permite la suma y la resta (nada más):
+ - += -= ++ --
#include <stdio.h>
int main(){
int a, *ptr;
a=5;
ptr=&a;
printf(“%p”, ptr);
ptr=ptr+1;
//si ptr=1000, ptr+1=1004, porque suma una unidad del tipo de datos
(para un int son 4 bytes)
printf(“%p”, ptr);
return 0;
}
#include <stdio.h>
int main(){
int i, w[]={10,20,30,40,50};
for (i=0;i<5;i++)
printf(“%4d”, w[i]);
return 0;
}
#include <stdio.h>
int main(){
int i, *ptr, w[]={10,20,30,40,50};
ptr=&w[0]; //podría escribirse: ptr=w (el nombre del vector es
simpre un puntero a su primer
elemento: &w[0] w )
for (i=0;i<5;i++){
printf(“%4d”, *ptr);
ptr++;
}
return 0;
}
Pasaje de vectores como parámetro:
#include <stdio.h>
#define MAX 50
void inicializar(int w[MAX]); // void inicializar(int w[]) ó
// void inicializar(int *w);
int main(){
int v[MAX];
inicializar(v);
…
…
return 0;
}
void inicializar(int w[MAX]){
int i;
for (i=0;i<MAX;i++)
w[i]=0; //*(w+i)=0;
}
Cadenas de caracteres
Conjunto de cero (cadena vacia, no contiene nada) o mas caracteres.
(ver código ascii)
Lo que se almacena son los valores ascii (ocupa un byte, no es un entero)
de la cadena.
El carácter ´\0´ representa al carácter nulo, su valor ascii es cero, y
toda cadena finaliza con este carácter. Por eso toda cadena tiene que
tener un carácter mas del necesario para poner este carácter de
finalización.
char cad1[80]; //tiene una capacidad útil de 79 caracteres
char cad2[]={‘M’,‘a’,‘x’,‘i’,‘\0’}; //declarada como arreglo abierto
char pais[]=”Alemania”; //constantes de cadenas de caracteres, el ‘\0’
lo agrega el lenguaje en este caso.
#include <stdio.h>
int longuitud(char cad[]);
int main(){
char nombre[40];
printf(“Ingrese nombre”);
scanf(“%s”, nombre); //no uso & porque la cadena es un puntero
a //la primer posición, el scanf permite ingresar de a una palabra,
//cuando encuentra un espacio deja de ingresar
printf(“Ud. Se llama %s”, nombre);
printf(“y su nombre contiene %d caracteres\n”, longitud(nombre));
return 0;
}
int longuitud(char cad[]){
int i=0;
while(cad[i]!=’\0‘)
i++;
return i;
}
//fragmento de programa:
char materia[80];
copiarcadena(materia, “Programación”);
void copiarcadena(char destino[], char origen[]){
int i;
for (i=0; origen[i]; i++)
destino[i]=origen[i];
destino[i]=’\0’
}
void concatenar(char destino[], char origen[]){
int i, j;
for (i=0; destino[i]; i++)
;
for (j=0; origen[j];j++)
destino[i+j]=origen[j];
destino[i+j]=’\0’;
}
#include <stdio.h>
int main(){
char datos[2][40];
printf(“Nombre?”);
scanf(“%s”, datos[0]); //uso solo el subíndice de fila
printf(“Apellido?”);
scanf(“%s”, datos[1]);
printf(“Bienvenido %s%s\n”, datos[0], datos[1]);
return 0;
}
Para evitar que isalpha cancele todo cuando le paso un carácter mayor a
128 (que c los ve como negativos), tendría que castear el cad[i]:
isaplha((unsigned)cad[i])
Otra forma es declarar la cadena como:
unsigned char cad[80];
isdigit(<char>) -> devuelve verdadero (un entero ¡=0) cuando el carácter
que le pasamos sea un digito numerico, o falso (un entero=0)
#include <stdio.h>
#include <ctype.h>
int main(){
char cad[80];
int i;
printf(“Ingrese una cadena de caracteres”);
scanf(“%s”, cad);
for (i=0;cad[i];i++)
if (isdigit(cad[i]))
printf(“%c es un digito numerico\n”, cad[i]);
return 0;
}
//fragmento de programa:
char cad[80];
char cad1[]=”Lunes”;
char cad2[]=”Martes”;
char cad3[]=”Miércoles”;
sprintf(cad, “%s, %s, %s”, cad1, cad2, cad3); //concateno las cadenas, la
cadena que recibe (el primer parametro) no debe ser ninguna de las otras
cadenas que participan.
sscanf(<cadena>,<cadena de control de formato>,<direcciones de
variables>); -> toma datos de la cadena primer parametro y los guarda en
las variables del ultimo parámetro. La función convierte hasta donde
encuentra un carácter inválido.
#include <stdio.h>
int main(){
char cad[]=”3.1416”;
float pi;
sscanf(cad, “%f”, &pi); //pase una cadena a una variable numerica
printf(“%f”, pi); //voy a ver 3.141600
return 0;
}
//fragmento de código
char calle[]=”Lima 717”;
int altura;
sscanf(&calle[5], “%d”, &altura); //esto: &calle[5] lo hago porque quiero
que empiece a leer desde la quinta posición, porque si arranca desde cero
se corta porque ya no es numérico el primer carácter. Tambien podría usar
calle+5 (aritmética de punteros).
char cad1[]=”Hola”;
char cad2[]=”Hola”;
if (cad1==cad2) //comparo direcciones de memoria, no contenidos
puts(“son iguales”);
else
puts(“son distintas”); //son distintas!!
Estructuras
Una estructura es un conjunto de datos agrupados bajo un solo nombre. A
cada uno de estos datos se lo denomina ‘campo’ y, por lo tanto, una
estructura es un conjunto de campos. (Una estructura es un tipo de dato
creado.) La estructura tiene como mínimo dos campos, porque el objetivo
es agrupar campos, si tiene un solo campo no esta agrupando nada.
#include<stdio.h>
#include<string.h>
struct sfecha {
int dia, mes, anio;
}; //no olvidar el ; final de la estructura
struct salumno {
char lu[10];
char nombre[40];
char domicilio[40];
int materias;
struct sfecha fechanac;
};
int main(){
struct sfecha hoy;
struct sfecha ayer={14,2,2016}; //variable creada e inicializada
struct salumno alumno1, alumno2;
struct salumno curso[50]; //vector de estructuras
[Link] = 15; //el punto . es el operador de pertenecia
[Link] = 2;
[Link] = 2016;
//uso la función ‘strcpy’ porque no puedo asignar una cadena de
//caracteres de otra forma:
strcpy([Link], “1000000”);
strcpy([Link], “Juan Ruiz”);
strcpy([Link], “Lima 717”);
[Link] = 0;
[Link] = 1;
[Link] = 5;
[Link] = 1990;
alumno2 = alumno1; //asignación directa, copia todos los
campos. //Las estructuras no se pueden comparar, por los bits de
relleno en //los distintos campos
curso[0] = alumno1;
strcpy(curso[1].lu, “1000001”);
strcpy(curso[1].nombre, “Luis Arce”);
strcpy(curso[1].domicilio, “Lima 775”);
curso[1].materias = 0;
curso[1].[Link] = 25;
curso[1].[Link] = 12;
curso[1].[Link] = 1994;
}
//uso de la función:
Imprimirmaterias([Link]);
struct sfecha{
int dia, mes, anio;
};
typedef struct sfecha tfecha;
// uso:
struct sfecha hoy;
tfecha ayer;
Operador sizeof
Obtengo los bytes de una variable o de un tipo de dato. No es una
función, es un operador de comparación, durante la compilación se
reemplaza las apariciones de sizeof por el valor que representa.
//fragmento de programa:
int a,b;
a = sizeof(char); //a recibe el valor 1
b = sizeof(a); //b recibe el valor 4, lo que ocupa un int
printf(“%d-%d\n”, a, b);
Archivos
Un archivo es un conjunto de elementos, llamados registros, todos del
mismo tipo que se almacenan en un dispositivo auxiliar para conservar su
contenido a través del tiempo.
Clasificacion de archivos:
1) Según el contenido de los registros:
a) Binarios: los datos se guardan respetando el mismo formato que
tienen en memoria.
b) De Texto: en los archivos de texto los datos se almacenan en
forma de cadenas de caracteres.
2) Según el sentido de la transferencia de datos:
a) Entrada (de lectura)
b) Salida (de escritura)
c) E/S
3) Según el método de Acceso:
a) Acceso Secuencial: para acceder al registro n debo pasar por los
n-1 registros anteriores.
b) Acceso Directo: conociendo el número de registro es posible
acceder a él en forma directa.
4) Cierre:
//fclose(<archivo>)
//fclose lleva como parámetro una variable puntero a FILE
fclose(arch);
//fclose devuelve un entero, si devuelve 0 -> no hubo error
(esto //no lo verificamos)
//para cambiar de modo hay que cerrar primero el archivo y
luego //volver a abrirlo en el nuevo modo.
int main(){
FILE * arch;
talumno alumno;
arch = fopen(“[Link]”, “wb”);
if (arch == NULL)
{
puts(“no se puede crear el archivo”);
return 1;
}
printf(“LU?(-1 para terminar)”);
scanf(“%d”, &[Link]);
while([Link]!=-1){
fflush(stdin); //ó getchar();
printf(“Nombre?”);
gets([Link]);
printf(“Domicilio?”);
gets([Link]);
fwrite(&alumno, sizeof(alumno), 1, arch);
printf(“LU?(-1 para terminar)”);
scanf(“%d”, &[Link]);
}
fclose(arch);
return 0;
}
#include<stdio.h>
typedef struct {
int lu;
char nombre[80];
char domicilio[80];
} talumno;
int main(){
FILE * arch;
talumno alumno;
arch = fopen(“[Link]”, “rb”);
if (arch == NULL)
{
puts(“no se puede abrir el archivo”);
return 1;
}
fread(&alumno, sizeof(alumno), 1, arch);
//feof necesita una lectura previa de fread
while(¡feof(arch)){
if ([Link]<1000000)
printf(“%d-%s\n”, [Link], [Link]);
fread(&alumno, sizeof(alumno), 1, arch);
}
fclose(arch);
return 0;
}
#include<stdio.h>
typedef struct {
int lu;
char nombre[80];
char domicilio[80];
}talumno;
int main(){
FILE * arch;
talumno alumno;
arch = fopen(“[Link]”,”rb+”);
if (arch==NULL){
puts(“Error”);
return 1;
}
fread(&alumno,sizeof(alumno),1,arch); //fread antes de foef
while(!feof(arch)){
if ([Link]>0 && [Link] < 1000000){
//vuelvo una posición para grabar el alumno
modificado //en la misma posición donde estaba, porque
una vez que //lo leyó, el indicador de registro activo
se movio un //posicion
fseek(arch, -1*(int)sizeof(talumno),SEEK_CUR);
//casteo sizeof a int, para que tenga signo y pueda
//multiplicarlo por menos uno, porque sizeof me devuelve
un //entero sin signo
[Link]=-1*[Link];
fwrite(&alumno,sizeof(alumno),1,arch);
//si estoy leyendo y quiero grabar (o al reves)
estoy //obligado a usar un fseek, por eso lo uso con un cero, para que no
haga //nada
fseek(arch,0,SEEK_CUR);
}
fread(&alumno,sizeof(alumno),1,arch);
}
fclose(arch);
return 0;
}
///////////////////////////////////////////////////////
Fragmento de programa:
Utilizo técnicas de acceso directo para recorrer un archivo en forma
secuencial, no esta bien, es al pedo.
fseek(arch,0,SEEK_END);
cantreg=ftell(arch)/sizeof(…);
for(i=0;i<cantreg;i++){
fseek(arch,i*sizeof(…),SEEK_SET);
fread(&dato,sizeof(dato),1,arch);
…
…
}
fclose(arch);
Archivos de Texto
LUNES
MARTES
MIERCOLES
En disco se almacena asi: LUNES\nMARTES\nMIERCOLES\n
El \0 que marca el final de las cadenas en memoria no se graba en los
archivos de texto, aca se graba un \n, que separa un registro del
siguiente.
\n: delimitador en archivos de texto, al grabarlo se convierte en:
a) DOS/Windows: Retorno de carro (CR, ASCII 13) + Avance de Linea (LF,
ASCII 10)
b) Unix/Linux: Avance de Linea (LF, ASCII 10)
c) Mac: Retorno de carro (CR, ASCII 13)
Binarios De Texto
Contenido de los Datos binarios Cadenas de caracteres
registros Variables Simples y
Estructuras
Longitud de registro Fija Variable
Delimitador entre Ninguno \n
registros
Conversión de Texto No Si
Método de acceso Secuencial o directo Secuencial
Método de Apertura Entrada, Salida o E/S Entrada o Salida
#include<stdio.h>
#include<ctype.h> //para usar toupper
int main(){
FILE *entrada, *salida;
char c;
entrada = fopen(“[Link]”,”rt”);
if(entrada == NULL){
puts(“Error”);
return 1;
}
Salida = fopen(“[Link]”,”wt”);
if(salida == NULL){
puts(“Error”);
return 1;
fclose(entrada);
}
c=fgetc(entrada);
while(!feof(entrada)){
fputc(toupper(c),salida);
c=fgetc(entrada);
}
fclose(entrada);
fclose(salida);
return 0;
}
2) Línea a Línea
fgets(<cadena de caracteres>, <maximo>, <archivo>);
lee la próxima línea del archivo de texto y la guarda en la
variable de cadena de caracteres que pusimos como parámetro, lee
hasta que encuentra el \n. El máximo establece un limite a la
cantidad de caracteres a leer, se detiene la lectura por lo primero
que ocurra.
Char cad[80];
…
…
fgets(cad, 80, arch);
Si leo una línea de 60 caracteres, se corta por \n
Si leo una línea de 90 caracteres, se corta por el máximo (80), se
leen 79 caracteres y se agrega automáticamente un \0. Los 11
caracteres que quedaban (90-79) se leen en una tercer lectura que
se cortara por el \n.
Si fgets corta por el máximo, no fue leído el \n y entonces no
estará en la cadena, si corta por \n, entonces lo agrega a la
cadena
3) Con formato
//para leer (si encuentra espacio en blanco deja de leer):
fscanf(<archivo>, <cadena de control de formato>, <dir. de vars>);
podría leer un archivo de fechas
5/10/2001
6/3/2006
fscanf(arch,”%d/%d/%d”,&dia,&mes,&anio);
fgetc(arch); //retiro el \n que quedo porque sino sigue
esperando
//para grabar:
fprintf(<archivo>, <cadena de control de formato>, <datos>);
#include<stdio.h>
#include<stdlib.h>
int main(){
int *ptr;
ptr = (int*)malloc(sizeof(int));
*ptr = 3;
*ptr = *ptr + 1;
printf(“%d”, *ptr);
free(ptr);
ptr = NULL;
return 0;
}
malloc: pide un bloque de memoria (del área del montón) para ser usado
como memoria dinámica, necesita un entero que representa el tamaño en
bytes que quiero (uso sizeof para eso). Me devuelve la dirección de
memoria donde comienza ese bloque, y guardo esa dirección en mi puntero.
Si fracasa la asignación de memoria, malloc me devuelve un valor null.
El cast (int*) está para evitar un error o advertencia, malloc no
devuelve un puntero a entero (ni a nada), devuelve un puntero genérico y
podría dar error. Lo que escribo dentro del cast es la declaración que
utilicé para declarar al puntero, en este caso int*.
#include<stdio.h>
#include<stdlib.h>
int main(){
char *cad;
cad = (char*)malloc(40);
strcpy(cad, “Programación”);
puts(cad);
free(cad);
cad = NULL;
return 0;
}
////////////////////////////
/////////////////////////////
Listas Enlazadas
Conjunto de elementos a los que llamo nodos, el nodo es un bloque de
memoria, dividido en dos áreas:
Nodo:
Valor (puede ser un entero, toda una estructura, Sig (puntero hacia
etc, según el tipo de dato) el siguiente nodo
de la lista)
int main(){
tpuntero cabeza;
int e;
cabeza = NULL; //Fundamental!!!
printf(“ingrese números enteros, -1 para terminar”);
scanf(“%d”, &e);
while(e!=-1)
{
insertarenelprincipio(&cabeza, e);
scanf(“%d”, &e);
}
imprimirlista(cabeza);
borrarlista(&cabeza);
return 0;
}
actual = *cabeza;
anterior = NULL;
while(actual!=NULL)
{
anterior = actual;
actual = actual->sig;
}
nuevo = malloc(sizeof(tnodo));
nuevo->valor = dato;
nuevo->sig = NULL;
if (anterior != NULL)
anterior->sig = nuevo;
else
//sino había ningún nodo, cabeza pasa a ser el nuevo elemento
*cabeza = nuevo;
}
actual = *cabeza;
anterior = NULL;
anteanterior = NULL;
while(actual!=NULL){
anteanterior = anterior;
anterior = actual;
actual = actual->sig;
}
if (anteanterior!=NULL){ //2 o mas nodos
free(anterior);
anteanterior->sig = NULL;
} else if (anterior!=NULL) {
free(anterior);
*cabeza = NULL;
}
}
actual = *cabeza;
anterior = NULL;
while(actual!=NULL && actual->valor < dato)
{
anterior = actual;
actual = actual->sig;
}
nuevo = malloc(sizeof(tnodo));
nuevo->valor = dato;
nuevo->sig = actual;
if (anterior != NULL)
anterior->sig = nuevo;
else
//sino había ningún nodo, cabeza pasa a ser el nuevo elemento
*cabeza = nuevo;
}
Listas Enlazadas Dobles
Cada nodo tiene dos punteros: sig (apunta al siguiente) y ant (apunta al
anterior)
ant Valor sig