Programacion en C Conceptos Avanzados PR
Programacion en C Conceptos Avanzados PR
Programación en C:
ConceptosAvanzados
José Galindo
Índice de contenidos
1. Operador sizeof.
2. Preprocesador : Para el seguimiento de estos
Constantes simbólicas. contenidos se supone que se
Macros conocen los conceptos más
básicos de la programación
3. Arrays :
en Lenguaje C: funciones,
Conceptos Básicos. arrays, estructuras, paso de
Arrays y Punteros. argumentos por valor y por
4. Memoria Dinámica: referencia...
Arrays Dinámicos.
Arrays de Punteros.
5. Tipos Enumerados.
6. Clases de Almacenamiento de Variables.
7. Operadores a Nivel de Bits.
8. Campos de Bits.
9. Uniones.
10. Interrupciones Software.
11. E/S por Ficheros.
12. Tipos Abstractos de Datos (TAD): Introducción.
13. Estructuras de Datos Dinámicas.
2
Operador sizeof
• Este operador determina el Tamaño en Bytes de su operando:
– Si su operando es un tipo de dato debe ponerse entre paréntesis.
• Ejemplo: sizeof (int) es el tamaño de cualquier variable de tipo int.
– Si el operando no es un tipo, sino una expresión, los paréntesis
pueden quitarse, aunque también pueden ponerse (ya que los
paréntesis pueden formar parte de cualquier expresión).
• Ejemplo: sizeof x devuelve los bytes de memoria que requiere la
variable x.
– Al calcular el tamaño de una expresión esa expresión no es ejecutada
(o evaluada).
• Ejemplo: Al utilizar, sizeof (a=b+1) no asigna ningún valor a la
variable a.
– Si el operando es un array se devuelve el tamaño, en bytes, de todo
el array.
• Por tanto, es fácil descubrir el número de elementos del array dividiendo
ese valor por el tamaño de cada uno de sus elementos.
• Es una pena, pero esa técnica no funciona si se usa en el argumento de
una función, ya que dicho argumento es sólo un puntero y sizeof
devuelve el tamaño del puntero.
3
int *b; b ?
• DIFERENCIAS:
– Declarar un array reserva memoria para TODOS los elementos.
– Declarar un puntero reserva memoria SÓLO para el puntero.
– El nombre de un array almacena una dirección fija ( constante) que
apunta al principio de este espacio. NO se puede cambiar el valo r de
la constante.
• El nombre de un array es la dirección de memoria del primer
elemento: a ≡ &a[0]
– Una variable puntero almacena una dirección de memoria que puede
ser modificada. La variable puntero NO está inicializada para apuntar
a ningún espacio existente, por lo que inicialmente tiene cualquier
valor no válido. Para indicar que un puntero no apunta a ningún sitio
se le puede asignar el valor NULL.
7
Arrays y Punteros
• Los arrays y los punteros usan diferentes notaciones de indexación.
• Así, las siguientes expresiones son equivalentes: float arr[5];
8
Arrays y Punteros
Ejemplos Descripción
int arr[10]; Declara el array arr con 10 elementos.
int *ptr; Declara un puntero a entero.
arr[1]=5; Asigna 5 al segundo elemento del array arr.
*(arr+1)=5; Equivalente a la instrucción anterior.
ptr=&arr[2]; Asigna a ptr la dirección del tercer elemento:
ptr
arr 5
ptr Equivale a arr+2 y a &arr[2]
*ptr Equivale a arr[2] y a *(arr+2)
ptr[0] Equivale a arr[2]
ptr + 6 Equivale a arr+8 o &arr[8]
*ptr + 6 Equivale a arr[2] + 6
*(ptr + 6) Equivale a arr[8]
ptr[-1] Equivale a arr[1]
ptr[9] Equivale a arr[11] : ¡Fuera del array arr!
Tipos Enumerados
• En C, un Tipo Enumerado consiste en una sucesión de
constantes enteras con nombre que especifica todos los
valores válidos para cierta variable.
– Simplifican la programación haciendo que los programas sean más
legibles, ya que usarían los símbolos (los nombres válidos).
– Sintaxis: enum nombre_tipo {lista_nombres} variables;
– Ejemplo : enum semana {L, M, X, J, V, S, D} dia;
• Con ese ejemplo es correcto usar: dia = S;
if (dia == X) ...
– Cada símbolo de la enumeración tiene un valor entero: Se empieza
en cero y para cada símbolo se suma 1: L vale 0, M vale 1, X vale 2...
– Esos valores pueden alterarse:
enum semana {L, M, X, J, V, S=10, D} dia;
• Así, S vale 10 y D vale 11: El orden de los valores se mantiene.
– Los símbolos NO pueden escribirse directamente como si fueran
cadenas de caracteres: Son constantes enteras.
– Usualmente el sistema NO genera error si no se respetan las
restricciones de un tipo enumerado: Es responsabilidad del
programador usarlo correctamente. 16
Clases de Almacenamiento de Variables
• Clases de almacenamiento en C: En una declaración la clase de
almacenamiento puede especificarse antes de la declaración:
– auto, Variables Automáticas: Son las variables normales, que se crean
cuando se declaran y se destruyen cuando se termina la función en la que
están declaradas (o el programa si son globales).
• Es equivalente declarar int i; que auto int i ;
– extern, Variables Externas: Se refiere a que se trata de variables
declaradas en un módulo (fichero) distinto a donde aparece la declaración
con extern.•Ej.: extern int i; /* Variable ya declarada en otro módulo */
– register, Variables Registro : Son las variables que intentan ser
almacenadas en algún registro del procesador, para que las operaciones
sobre ellas sean más rápidas. Se usará en variables sobre las que recaigan
múltiples operaciones (control de bucles...). No puede abusarse de este tipo de
variables ya que, en tal caso, algunas variables no se almacenarán en
registros. •Ej.:register int i;
– static, Variables Estáticas: Variables que se crean la primera vez que
se ejecuta su declaración y no se destruyen al finalizar su función. Son como
variables globales, aunque sólo son vistas en la función en la que se declaran.
• La segunda vez (y las siguientes) que se llame a una función con una
variable estática, la función “recordará” el valor que obtuvo la variable en la
ejecución anterior, ya que la variable NO se destruyó al finalizar dicha
ejecución anterior. •Ej.: static int i=9; /* Valor inicial */
• Una variable estática siempre debe inicializarse al declararse.
• Si se usa static en una var. global será vista sólo en ese fichero. 17
Campos de Bits
• Ejemplo: Con la variable V se puede acceder a los 3 primeros
bits, a los 3 segundos bits y a al séptimo bit individualmente.
struct nombre_ estruct {
int cod1 : 3;
unsigned cod2 : 3;
unsigned cod3 : 1; cod1 cod2 cod3
} V;
• Observaciones:
– La variable V sólo ocupa 1 byte (8 bits) y el octavo bit no es accesible
(porque no es necesario, por ejemplo).
• Sin usar un campo de bits esto hubiera ocupado 3 bytes como
mínimo. Imagine el ahorro en un array de estructuras de este tipo.
– Campos de la variable V:
• V.cod1 es un entero de 3 bits, por lo que su rango es [–4,3]
(utiliza una representación en complemento a 2).
• V.cod2 es un entero positivo de 3 bits. Su rango es [0,7].
• V.cod3 es un entero positivo de 1 bits. Su rango es [0,1].
– Este último campo es usual para variables lógicas (booleanas) que
sólo admiten dos valores: Verdad (1) o Falso (0). 20
Uniones
• Uniones: Es una forma de hacer que diferentes variables
compartan la misma zona de la memoria.
– Su declaración tiene exactamente la misma sintaxis que las
estructuras, excepto que se usa la palabra union en vez de struct.
– Ejemplo:union tipo_union{ i
int i; ch
char ch;
} V;
• Si a V.i se le asigna un valor, el primer byte de ese entero
puede ser visto desde V.ch.
– Ejemplo: Para acceder de forma individual al segundo byte de V.i
podríamos declarar ch como un array de dos posiciones:
union tipo_union{ i
int i; ch [0] ch[1]
char ch[2];
} V;
V.i=258; /* En binario: 00000001 00000010 */
printf("%i-%i", V.ch[1], V.ch[0]);
• Escribe: 1-2. Observe que en un PC los bits se almacenan desde el menos significativo
al más significativo, (contrario a la intuición) y, por eso, mostramos primero el segundo byte.
21
Uniones
dual.numero = 65;
printf("\n%u-% u-%u-%u.", dual.cuatro.a4, dual.cuatro.a3,
dual.cuatro.a2, dual.cuatro.a1); 24
Interrupciones Software
• Una Interrupción es un tipo especial de instrucción que interrumpe la
ejecución de un programa:
– Una Interrupción hace que se pare la ejecución del programa
actual, se salte a la ejecución de una rutina de tratamiento de la
interrupción y, finalmente, vuelve para continuar el programa que
había sido interrumpido.
– Las interrupciones software, que suelen numerarse con números en
hexadecimal, pueden ser ejecutadas desde un programa y sirven para
solicitar que el programa efectúe determinadas tareas.
• En un PC esas tareas pueden ser: imprimir la pantalla (interrupción 5h),
operaciones de E/S de vídeo (10h), de E/S de disco (13h), de E/S del
puerto serie (14h), de E/S del teclado (16h), de E/S de impresora (17h),
operaciones de hora y fecha (1Ah), de control del ratón (33h)...
• En un PC, la función típica para ejecutar una interrupción software
está en dos.h: int int86 (int Num_Interrupcion ,
union REGS *Entrada,
union REGS *Salida);
– Num_Interrupcion es la interrupción que deseamos ejecutar.
– Entrada son los datos de entrada a dicha interrupción.
– Salida son los datos de salida de dicha interrupción.
– La función devuelve el valor del registro AX tras la interrupción. 25
Interrupciones Software
• El tipo union REGS está definido en dos.h de forma que pueda
accederse a los registros del procesador de forma completa (como ax) o
byte a byte individualmente ( al es la parte baja y ah es la parte alta,
lo w/hight): struct WORDREGS {
unsigned int ax, bx, cx, dx, si, di, cflag, flags;
};
struct BYTEREGS {
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};
union REGS {
struct WORDREGS x;
struct BYTEREGS h;
};
• Ejemplo : Borrar parte de la pantalla (en este ejemplo borra la pantalla
completa): union REGS r;
r.h.ah=6; r.h.al=0; Suele utilizarse la
misma variable de
r.h.ch=0; /* Fila inicial */ entrada también
r.h.cl=0; /* Columna inicial */ para la salida.
r.h.dh=24; /* Fila final */
r.h.dl=79; /* Columna final */
r.h.bh=7; /* Color de borrado */
int86(0x10, &r, &r);
26
E/S por Ficheros (Files):
Operaciones Básicas
• Para operar con un Fichero o Archivo hay que seguir 3 pasos:
– 1. Abrir el Fichero: Prepara un fichero para poder utilizarlo.
• La función fopen( ) (de stdio .h) devuelve un puntero a un fichero
(FILE * ), que es una estructura con la información que necesita el
sistema para acceder al fichero (nombre, estado, posición actual...).
– fopen() devuelve NULL si no se puede abrir el fichero (no existe,
disco lleno, disco protegido...).
• El primer argumento de fopen( ) es una cadena de caracteres con el
nombre (y la ruta) del fichero que se desea abrir.
• El segundo argumento de fopen() es una cadena de caracteres con
el modo con el que se desea abrir un fichero:
r Abre un fichero ya existente sólo para lectura (r ead).
w Crea un fichero para escritura (write). Si ya existe, lo borra.
a Abre un fichero para añadir (escribir) datos al final (append). Si no existe, lo crea.
r+ Abre un fichero existente para actualización (lectura y escritura).
w+ Crea un fichero para actualización (lectura y escritura). Si ya existe, lo borra.
a+ Abre un fichero para actualización (lectura y escritura) al final. Si no existe, lo crea.
• Puede añadirse una 't' para indicar que el fichero es de texto y una
'b' para indicar que el fichero es binario.
– Los ficheros de texto ocupan más espacio que los binarios , pero
pueden visualizarse (y modificarse) con cualquier editor de texto. 27
–
typedef struct element *lista;
– Funciones primitivas importantes:
/* Inicializa la lista */
void inicializar_lista(lista *list){
*list=NULL;
}
/* Devuelve TRUE si la lista está vacía. */
int lista_vacia(lista list){
if (list) return 0;
return 1;
} 38
EDD: Lista Dinámica de Números
/* Devuelve la longitud de la lista (n úm. de elems ). */
long long_lista(lista list){
long i=0;
while (list) {
list=list->sig;
i++;
}
return i;
}
/**********************************************************/
/* Devuelve en e, el elemento de la posición pos de list . */
/* Si no existe esa posici ón, devuelve -1. En otro caso 0.*/
int leer_element (lista list, long pos, struct element *e){
long i=1;
if ( lista_vacia(list) || pos<1)
return -1 ;
while (list && i<pos) {
list=list->sig;
i++;
}
if (list){
*e=*list;
return 0;
}
return -1;
}
39
Bibliografía