martes, 2 de febrero de 2010

Estructuras

Una estructura es un conjunto de variables de los mismos o diferentes tipos. Las estructuras son semejantes a los registros (records) de Pascal.

Los elementos integrantes de una estructura se denominan miembros. Pascal llama campos (fields) a los elementos de una estructura, pero C reserva este nombre para referirse a los miembros particulares de estructuras que permiten manejo de bits.

La declaraciòn de una estructura se hace como:

struct x {

miembros-y-declaraciones

}

utilizándose como un tipo de dato más en declaraciones de tipo de variables.

"x" es el nombre de la estructura. Las referencias posteriores a la misma estructura pueden omitir las llaves ({}) y la declaración de sus miembros.

Los miembros y declaraciones incluidos en la estructura son como cualquier declaración de variable de las vistas hasta ahora, con la única diferencia de que no puede especificarse'un tipo de almacenamiento (auto, static, register, extern). Por ejemplo:

struct fecha {

int dias;

int mes;

int año;

};

struct persona {

char nombre[TAMAÑO1];

long cod_postal;

long ss_num;

double sueldo;

struct date nacim_fecha;

struct date incorp_fecha;

};

serìa equivalente a los records de Pascal:

type

date = record

dia: 1..31;

mes: 1..12;

año: integer;

end;

person = record

nombre: array [1.. TAMAÑO2] of char;

cod_postal: integer;

sueldo: real;

nacim_fecha: date;

incorp_fecha: date;

end;


En este ejemplo definimos una estructura "fecha" conteniendo tres variables enteras: día, mes y año, y definimos una estructura "persona", conteniendo dos arrays de caracteres (nombre y dirección) cod_postal de tipo long, ss_num de tipo long, sueldo de tipo double, y dos miembros consistentes en estructuras "fecha", correspondientes a nacim_fecha e incorp_fecha.

Para acceder a un miembro de una estructura se utiliza el operador ".", como ya anticipábamos al hablar.de las variables en C y sus tipos. Se recurre a él en la forma:

estructura-variable.miembro 4

Por ejemplo, para declarar una variable "d" como una estructura "fecha" e inicializarla a la fecha 1 de junio de 1986, se haría del modo siguiente:

struct fecha d;

d.dia = 1

d.mes = 6;

d.año = 1986;

Como podemos observar en la declaración de la estructura persona las estructuras se pueden encadenar, pudiendo utilizarse como miembros de otras estructuras. Si se define "emp" como una estructura persona:

struct persona emp;

entonces

emp.nacim_fecha.año

se referirá al año de nacimiento del empleado en cuestión.

Una estructura externa o estática se puede inicializar acompañando el nombre de la estructura por una lista de inicializado-res rodeados por llaves ({}).

struct fecha d = {1, 6, 1986};

Declara "d" como una estructura fecha, e inicializa d.día a 1, d.mes a 6 y d.año a 1986.


Operaciones sobre estructuras

En implementaciones antiguas de C las únicas operaciones permitidas sobre las estructuras eran el acceso a sus miembros (mediante un "."), o el acceso a su dirección (mediante un &). ,

Los compiladores más recientes permiten la asignación de variables estructura, incluyendo el paso de estructuras como parámetros y devolviendo estructuras como valores de funciones.

No está permitido realizar comparaciones de estructuras.

Muchos compiladores que admiten la devolución de estructuras lo hacen de manera no-reentrante, dejando el valor de la estructura devuelta en una variable estática en lugar de en el stack.

Las estructuras automáticas no pueden inicializarse a pesar de que inicializar variables automáticas es equivalente a realizar una asignación. En el S.O. Unix se obtendría el mensaje de error "No auto aggregate inicialitation" como respuesta a este intento.


Punteros a estructuras:

Debido a las restricciones impuestas a las variables de tipo estructura normalmente se suelen utilizar punteros a estructuras para realizar el traspaso de estructuras como argumentos de funciones.

Incluso si se permiten asignaciones de estructuras, pasar una estructura como argumento puede no ser muy buena idea en ocasiones; por ejemplo:

#define LINEAS 24

#define COLUMNAS 80

struct ventana { /* una parte de una pantalla */

char lineas [LINEAS] [COLUMNAS];

int ult_col; /* esquina superior */

int ult_fila; /* esquina superior */

int nlineas; /* esquina superior */

int ncols; /* esquina superior */


La llamada a "muestra" (definida en otro lugar) copia la totalidad de la estructura en el stack. Esta estructura contiene LINEAS * COLUMNAS = 24 x80 = 1920 caracteres, así como algunas variables enteras. Compárese esto frente a la alternativa de pasar como argumento un puntero a la estructura, que requeriría tan sólo una variable entera (la dirección de la estructura).

Hay que destacar que pantalla.lineas es un array, y aquí se pasaría un puntero al primer elemento de este araay en la llamada a la función borra_pantalla.

Los punteros a estructuras se declaran de la manera habitual, como los restantes punteros:

/* extrae elementos de la estructura

# persona y los imprime

*/

ppersona (p)

struct persona *p

{

print-f ( "Nombre: %s\n", p->nombre) ;

print-f ("Dirección: %s\n", p->direccion) ;

print-f ("Código Postal: "%d\n", p->cod_postal)

print-f ("Código de la S.S.:%d\n", p->ss_num)

printf("Sueldo: %.2-f\n", p->sueldo);

printf ("Fecha de nacimiento: %d %d %d\n",

p->nacim_fecha.dia,

p->nacim_fecha.mes,

p->nacim_fecha.año);

print-f ("Fecha de incorporación: %d $d %d\n",

p->incorp_fecha.dia,

p->incorp_fecha.mes

p->incorp_fecha. año);

La construcción

puntero-a-estructura->miembro

es equivalente a:

(*puntero-a-estructura) .miembro

El operador "->" consiste eb un signo menos "-" seguido por el signo mayor ">".
Los paréntesis en "(*p).nombre" son necesarios porque el orden de evaluación del operador "." (miembro de una estructura) es mayor que el del operador "*" (valor apuntado por) Así *emp.nombre es equivalente a emp.nombre[0].
Los operadores "." y "->" tienen mayor prioridad que los operadores aritmèticos. Así pues, dada la declaración:

struct {
int x;
inty;
} *p;
la expresiòn
++p->x

incrementaría el valor del miembro "x" de la estructura apuntada por "p", en lugar de incrementar el puntero "p".
Análogamente:

*p->y; obtiene el valor de "y" apuntado por "p"
*p->y++ idem. , incrementando p->y
(*p->y)++ incrementa la "y" apuntada por "p"
*p++->y; incrementa "p" tras buscar p->y
Del mismo modo que C permite arrays de arrays como un tipo más de variables, también permite la definición de arrays de estructuras.

lunes, 1 de febrero de 2010

Campos

Dentro de la declaraciòn de una estructura puede aparecer un miembro seguido por un caràcter de dos puntos ":" y una expresiòn constante, denominàndose entonces campo (field).

El tipo del miembro debe ser alguno de los tipos enteros (char, int, short, long o unsigned); la expresión constante especifica cuántos bits van a utilizarse para ese miembro.

Los campos de bit son una alternativa a las operaciones explícitas de desplazamiento y máscaras de bits, Consideremos el programa:

#define KEYWORD 01

#define EXTERNAL 02

# define STATIC 04

int flags;

...

flags |= EXTERNAL | STATIC;

...

if (flags & KEYWORD) {

...

Esto mismo se podría también haber escrito como:

struct {

int is_keyword:1;

int is_extern:1;

int is_static:1;

} flags;

...

flags.is_extern = flags.is_static =1;

...

if (flags.is_keyword){

...

Desafortunadamente, el empleo de campos de bit en C tiene algunos inconvenientes:

• en las versiones antiguas de compiladores de C, todos los campos tienen que ser cantidades sin signo;

• no pueden emplearse datos binarios empaquetadas de una máquina a otra, pues el empaquetado es dependiente dé la máquina al no especificarse si la asignación de bits se hace de izquierda a derecha, o de derecha a izquierda;

• los campos de bit no son exactamente variables, en el sentido de que no pueden definirse punteros a campos de bit, ni arrays de campos, aunque, por supuesto, sigue siendo válido definir punteros a estructuras que contengan campos de bit.

miércoles, 20 de enero de 2010

Uniones

Una uniòn consiste eb una variable que puede contener valores "normales". Las uniones son una de las alternativas para solventar la falta de comprobaciones de tipo del lenguaje C en tiempo de ejecución.

La sintaxis de la declaraciòn de una unión es semejante a la declaración de una estructura sin más que cambiar la palabra estructura por unión.

struct { /* entrada en la tabla de símbolos */

char *name;
int type;
union {
int u_ival;
float u_fval;
char *_sval;
} uval;
}symtab [NSYMJ;

Las variables symtab[i].uval tienen capacidad suficiente para contener variables de cualquiera de los tipos int, float o char, siendo responsabilidad del programador tener el cuidado necesario para controlar los tipos adecuados de las variables contenidas en una unión.

La unión definida anteriormente permitiría el acceso a susjdis-tintos elementos como:

switch (symtab[i].type) {

case INT:

printf ("%d\n", symtab[i].uval.u_ival);

break;

case STRING:

printf ("%s\n", symtab[i].uval_sval);

break

case FLOAT

printf ("%s\n", symtab[i].uval_fval);

break;

}

siendo similar al programa en Pascal:

type symtab =

record

name ; array[1..10] of char;

case stype : integer of

int : ( ival : integer );

float: (fval : real );

string : (svai . array[1..20] of char );

end;

var

symtab : array [1..nsymbols] of symtab;

...

case symtab[i].stype of

int : writeln(symtab[i].ival);

float : writeln(symtab[i].fval);

string : writeln(symtab[i].sval);

end


La enorme similitud entre ambos programas puede aún aumentarse mediante el empleo del preprocesador de C, empleando #defines que hagan más manejables los nombres de variables:

#define ival uval.u_ival
#define ival uval.u_fval
#define ival uval.u_sval

Las operaciones que pueden realizarse sobre las uniones las mismas que las permitidas sobre las estructuras, acceder a uno de sus miembros y acceder a la dirección de la unión.

miércoles, 6 de enero de 2010

Entrada/Salida en lenguaje C

El lenguaje C no incorpora directamente instrucciones para la realización de las operaciones de Entrada/Salida, implementándola a través de librerías y funciones.

Existe un conjunto de funciones de E/S que es portable a un gran número de sistemas operativos (UNIX, VMS, MS-DOS, CP/M, ...), imple-mentadas en la denominada librería de E/S estándar. Cualquier programador de C debe conocer los nombres y argumentos de estas funciones, considerando que van a ser las mismas en cualquiera de los sistemas operativos mencionados.

Además se incluyen funciones para permitir el interface directo con las propias funciones de E/S del sistema operativo.


Los ficheros en la librería estándar de E/S:

Las declaraciones necesarias para el manejo de la librerìa estàndar de E/S son accesibles mediante el empleo de un #include;

#include

En algunos sistemas puede llegar a ser necesario notificar expresamente al compilador la inclusión de la librería estándar. Por ejemplo, en los antiguos sistemas Unix Versión 6 se requería la inclusión de la opción "-1S" al compilar. Esto ya no es necesario en los sistemas Unix Versión 7 actuales o en sus derivados.

Internamente, la librería estándar de E/S almacena la información correspondiente a cada fichero abierto en una estructura, identificando a los ficheros por medio de punteros a estructuras. El fichero define FILE como una de estas estructuras.

martes, 15 de diciembre de 2009

Apertura de Ficheros

Antes de que se pueda acceder a un fichero èste debe ser abierto mediante la funciòn fopen:

FILE *fopen (name mode) /* Abre un -fichero */

char name; /* Nombre del -fichero a abrir */

char *mode; /* Modo de acceso al -fichero */

El primer argumento de fopen es el nombre del fichero, enviado como una cadena de caracteres. El formato del nombre de fichero es dependiente del sistema operativo. Sin embargo, muchas (no todas) las implementaciones de la librería C estándar de E/S trasladan los nombres de ficheros especificados como en Unix al formato requerido por el sistema operativo.

Por ejemplo, el formato de un nombre de fichero en Unix es:

/directorio l/directorio2/ ... /fichero

Cada uno de los componentes del nombre del fichero (directorio 1, directorio2, fichero) pueden estar formados por cualquier secuencia de caracteres excepto por un slash "A Sólo son significativos los catorce primeros caracteres. No hay un límite para el número de directorios que se pueden especificar.

El "mode" (modo) puede ser de tres tipos:

• modo de acceso "r" indica que el fichero se va a abrir para lectura. Si el fichero no existe, se devolverá un error.
• modo "w" especifica que el fichero se va a abrir para escritura. Si el fichero ya existe no se tendrá en consideración el contenido anterior, perdiéndose por corajoleto. Si el fichero no existiese, entonces lo creará la propia Tunción fopen.
• modo "a"especifica que el fichero se va a abrir para añadirle texto (append). Si el fichero ya existe, los nuevos datos se grabarán al final del mismo, y si no existiese será creado por la función.

En caso de que se produzca un error, fopen devuelve el valor NULL, definido en como (char *)0.

jueves, 10 de diciembre de 2009

E/S básica

Las operaciones más sencillas que se pueden realizar sobre un fichero que ya esté abierto son getc y putc.

int getc(fp)

FILE *fp;

getc devuelve el siguiente caràcter del fichero especificado for fp, o EOF (definida en ). EOF se devuelve cuando se produce algún error o se alcanza el final de fichero.

Análogamente:

int putc(fp)

FILE *fp;

putc escribe el caràcter dado en el fichero fp, y retorna "c" o EOF (en caso de error).

feof(fp)

FILE *fp;

feof devuelve un valor distinto de cero si se ha llegado al final del fichero fp.

ferror(fp)

FILE *fp;

ferror devuelve un valor distinto de cero si se ha encontrado algùn error durante la lectura o escritura del fichero fp

fclose(fp)

FILE *fp;

fclose cierra el fichero fP' devolviendo un EOF si se produce algún error (por ejemplo, si el fichero fp no fue abierto previamente).

La funciòn freopen

FILE *freopen(name, mode, fp)

char *name;

char *mode;

FILE *fp;

en primer lugar cierra el fichero especificado por fp y abre un nuevo fichero, de modo que el fichero recién abierto reutiliza la estructura FILE apuntada por fp. Freopen devuelve fp si la apertura se ha realizado sin problemas, o NULL en caso contrario.

ungetc(c, fp)

char c;

FILE *fp;


ungetc provoca que la siguiente llamada a getc(fd) (así como scanf, getw, gets, y las restantes funciones de lectura) lea el carácter "c". Mediante el empleo dé úngete sólo se puede poner un carácter en el buffer del fichero fd.

jueves, 3 de diciembre de 2009

Ficheros Estándar

Cuando se inicia la ejecución de un programa C se produce la apertura automàtica de tres ficheros, antes de que llegue a producirse la llamada a main(). Estos ficheros son la entrada estándar "stdin" (standard input, normalmente el teclado) la salida estàndar "stdout" (standard output, normalmente la pantalla de vídeo) y el fichero estàndar de salida de errores "stederr" (standard error, normalmente la pantalla de video).

Este encaminamiento normal de los ficheros estándar (pantalla y teclado por defecto), puede verse modificado a través del intérprete de comandos de Unix y reencaminado hacia ficheros, "tuberías" (pipes) o regiones de memoria compartida.

La librería estándar de E/S declara stdin, stdout y stderr como punteros a estructuras FILE para los ficheros de ejitrada estándar, salida estándar y fichero estándar de errores, respectivamente.

Las funciones getchar y putehar son macros, en lugar de auténticas funciones, definidas en como:

#define getchar() getc(stdin)

#define putchar(c) putc(c, stdout)

Hay que advertir que stdin, stdout y stderr son constantes y no pueden ser reasingnados mediante fopen como podrìa parecer natural.

stdout = fopen("myfile", "w");

no es un procedimiento válido para redireccionar la salida estándar al fichero myfile. Sin embargo,

freopen("myfile", "w", stdout);


sí podría servir para este propósito, debido a que freopen cierra y abre el fichero, como comentamos anteriormente.