Download 4 LENGUAJE C

Document related concepts
no text concepts found
Transcript
59
Arquitectura y programación de ordenadores
4 LENGUAJE C
4.1 INTRODUCCIÓN
El C fue inventado por Dennis Ritchie en los años 70, pero no fue estandarizado hasta 1989
(C89), por el instituto nacional de estándares americano (ANSI), y adoptado por la
organización internacional de estándares (ISO). Por eso es habitual que el estandar se refiere
al estándar ANSI/ISO. En 1995 se adoptó la enmienda 1 del estándar de C, que entre otras
cosas, incorpora varias funciones de biblioteca nuevas. Este documento es la base del estandar
de C++, definiendo el subconjunto de C presente en C++.
En 1999 se desarrolló uno de estandar para C, normalmente referido como C99, que mantiene
prácticamente todas las características de C89, incorporando varias bibliotecas numéricas,
arrays de tamaño variable, el cualificador restric para punteros, dos nuevos tipos de variables
(complex y bool) y un nuevo cualificador de almacenamiento (mutable).
Aunque en sentido estricto, C no es un lenguaje estructurado en bloques, normalmente este
lenguajes de define como sencillamente estructurado. Tienen similitudes estructurales con
otros lenguajes estructurados como Algol, Pascal y Modula-2. La razón por la que C no es
tecnicamente un lenguaje estructurado en bloques es de que un lenguaje estructurado en
bloques permite declarar procedimientos o funciones dentro de otros procedimientos o
60
Arquitectura y programación de ordenadores
funciones. Puesto que C no permite la creación de funciones dentro de funciones, no puede ser
formalmente denominado lenguaje estructurado en bloques. La característica distintiva de
lenguaje estructurado es la compartimentalización del código y datos. Se trata de la
capacidad de un lenguaje de seccionar y esconder del resto del programa toda la información y
las instrucciones necesarias para llevar a cabo una determinada tarea. Una forma con la que se
consigue la compartímentalización es mediante el uso de subrutinas que empleen variables
locales (temporales). Con el uso de variables locales es posible escribir subrutinas de forma
que lo que ocurra en su interior no provoque efectos secundarios en otras partes del programa.
Esta posibilidad hace que sea muy fácil que los programas en C compartan secciones de
código. Al desarrollar funciones compartimentalizadas sólo es necesario conocer qué es lo que
la función hace, no como lo hace. El uso excesivo de variables globales (variables conocidas a
lo largo de todo el programa) para hacer que los errores proliferen en el programa, al permitir
a los efectos secundarios no deseados.
El componente estructural principal de C es la función; una subrutina independiente. En C, las
funciones son los bloques constitutivos en los que se desarrolla toda la actividad de los
programas. Permiten definir las tareas de un programa y codificarlas por separado, haciendo
que los programas sean modulares. Una vez que se ha creado una función que trabaja
perfectamente, se pueda aprovechar en distintas situaciones, sin crear efectos secundarios en
otras partes del programa. El hecho de poder crear funciones independientes es algo
extremadamente crítico en grandes proyectos donde es importante que el código de un
programador no afecte accidentalmente al de otro.
Otra forma de estructuración viene dada por uso de bloques de código, que es un grupo de
instrucciones de un programa conectadas de forma lógica y tratadas como una unidad. En C
un bloque de código se crea colocando una serie de instrucciones entre llaves. En este
ejemplo:
if (x<10) {
printf("Demasiado bajo, prueba han de nuevo. \n");
scanf( y en " la %d", &x);
61
Arquitectura y programación de ordenadores
}
las dos instrucciones que las el if y entre las llaves se ejecuta juntas si x es menor que 10.
Estas dos instrucciones junto con las llaves representan un bloque de código.
4.1.1 Compiladores frente a intérpretes
Es importante comprender que un lenguaje de computadora define la naturaleza de un
programa y no la forma en que el programa se ejecuta. Dos son los métodos generales de
ejecución de un programa: puede ser compilado o interpretado. Aunque los programas
escritos en cualquier lenguaje de programación pueden ser compilados o interpretados,
algunos lenguajes han sido diseñados más para una forma de ejecución que para la otra. Por
ejemplo, Java fue diseñado para ser interpretado y C para ser compilado. Sin embargo, en el
caso de C es importante tener en cuenta que ha sido especialmente optimizado como lenguaje
compilado. Aunque se han escrito algunos intérpretes de C para ciertos entornos
(especialmente, ayudas en la depuración o plataformas experimentales), C fue desarrollado
con la compilación en mente.
En su forma más sencilla, un intérprete le el código fuente de un programa línea a línea,
realizando las instrucciones específicas contenidas en esa línea. Esta es la forma en la que
trabajan las primeras versiones de BASIC. El lenguajes como Java, el código fuente de un
programa se convierte primero en una forma intermedia que es la que se interpreta entonces.
En cualquier caso se requiere la presencia de un intérprete de tiempo de ejecución para que se
pueda ejecutar el programa.
Un compilador le el programa entero y lo convierte a código objeto, que es una traducción del
código fuente del programa a una forma que puede ser ejecutada directamente por la
computadora. El código objeto también se suelen denominar código binario o código
máquina. Una vez que el programa está compilado, las líneas de código fuente dejan de tener
sentido durante la ejecución del programa.
62
Arquitectura y programación de ordenadores
En general un programa interpretado se ejecuta más despacio que un programa compilado.
Recuérdese que un compilador convierte el código fuente de un programa en código objeto
que se puede ejecutar directamente en la computadora. Por tanto, la compilación sólo pasa
factura una vez, mientras que el código interpretado tiene un precio cada vez que se ejecuta un
programa.
Forma de un programa en C
Existen 32 palabras claves definidas en el estándar C89. Estas son también las palabras clave
que constituyen el subconjunto de C presente en C++. La siguientes tablas muestran estas
palabras clave y las añadidas estándar C99.
Tabla 1 Palabras clave definidas en C89
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
Tabla 2 Palabras clave añadidas en C99
_Bool
_Imaginary
_Complex
inline
restrict
63
Arquitectura y programación de ordenadores
Además, muchos compiladores de C han añadido varias palabras clave más que permiten
explotar de forma más eficiente su entorno operativo. Por ejemplo, varios compiladores
incluyen palabras clave que gestionan la organización de la memoria de la familia de
procesadores 8086, facilita la programación usando distintos lenguajes y acceden a las
interrupciones. A continuación se muestra una lista de las palabras clave ampliadas que se
usan con más frecuencia:
Tabla 3 Palabras clave añadidas más frecuentemente
asm
_ds
huge
pascal
cdecl
_es
interrupt
_ss
_cs
far
near
En C, las mayúsculas y las minúsculas son diferentes. Una palabra clave no debe ser usada
para otro propósito en un programa; es decir, no debe servir como nombre de variables o de
función.
Todos los programas en C consisten en una o más funciones. Como regla general, la única
función que debe estar presente es la denominada main( ), siendo la primera función que es
invocada cuando comienza la ejecución del programa. En códigos de bien escrito, main( )
contiene lo que esencia esboza lo que el programa ha de. Aunque no es una palabra clave, se
ha de tratar como si lo fuera. Por ejemplo, no se debe intentar utilizar main como nombre de
una variable, ya que probablemente confundida al compilador.
La forma general que tiene un programa en C es la que se ilustra a continuación:
Inclusión de librerías (funciones necesarias en el programa, incluidas en ficheros.h)
Declaraciones globales (int, double, float, char, void, _Bool(C99) _Complex(C99), etc…)
64
Arquitectura y programación de ordenadores
int main(lista de parámetros)
{
secuencia de instruccones
}
tipo-devueto f1(lista de parámetros) /*tipo-devuelto puede ser int, char, double etc..*/
{
secuencia de instrucciones
}
tipo-devueto f2(lista de parámetros)
{
secuencia de instrucciones
}
.
.
.
tipo-devueto fN(lista de parámetros) /*Estas N funciones son utilizadas en main */
{
secuencia de instrucciones
}
4.1.2 La biblioteca y el enlace
Técnicamente, se puede crear un programa en C que se ha funcional y útil y que consiste
únicamente en instrucciones que se construyan con las palabras clave de C. Sin embargo, esto
es bastante raro ya que C no proporciona palabras clave para llevar a cabo cosas como
operaciones de entrada/salida. Por ello, la mayoría de los programas incluyen llamadas a
varias funciones contenidas en la biblioteca estándar de C.
65
Arquitectura y programación de ordenadores
Todos los compiladores de C incorporan una biblioteca estándar que proporciona las
funciones que se necesitan para llevar a cabo las tareas más usuales. El estándar de C
específica el conjunto mínimo de funciones que deben ser proporcionadas por todos los
compiladores. Sin embargo, es muy probable que el compilador contenga muchas otras
funciones. Por ejemplo la biblioteca estándar no define ninguna función gráfica, pero
seguramente el compilador que se tenga incluirá algunas.
Cuando se llama a una función de la biblioteca, el compilador de C recuerda su nombre. Más
tarde, el enlanzador combina el código que se ha escrito, con el código objeto que ya se
encuentra en la biblioteca estándar. Este proceso se denomina enlace. Algunos compiladores
tiene su propio enlanzador, mientras que otros usan el enlanzador estándar proporcionado por
el sistema operativo.
Las funciones de la biblioteca se encuentran en formato reubicable. Esto significa que las
direcciones de memoria de las diferentes instrucciones de código máquina no han sido
definidas de forma absoluta; sólo se mantiene información sobre desplazamientos. Cuando
salazón programa con funciones de la el lote que estándar, se usan esos desplazamientos de
memoria para obtener las direcciones reales.
Muchas de las funciones necesarias para escribir programa se encuentra en el biblioteca
estándar. Se comporta como bloques constitutivos que simplemente hay que combinar. Si se
escribe una función que se va a usar repetidas veces, también puede ser situada en la
biblioteca.
Compilación separada
La mayoría de los programas cortos de C están enteramente contenidos en un archivo fuente.
Sin embargo, a medida que aumenta la longitud del programa, así lo hace el tiempo de
compilación. Por ello, se permite partir un programa en muchos archivos y que cada uno se ha
66
Arquitectura y programación de ordenadores
compilado por separa. Una vez que han sido compilados todo los archivos, se enlazan entre sí,
junto con las rutinas de la biblioteca, para formar el código objeto completo. La ventaja de la
compilación separada es que un cambio en el código de uno de los archivos no requiere la
recompilación del programa entero, y además permite que los proyectos trabajen fácilmente
varios programadores en equipó, como también se constituye en un medio de realización del
código en los grandes proyectos.
Compilación de un programa en C.
La creación de una forma ejecutable de un programa en C consiste en estos tres pasos:
1. Creación del programa.
2. Compilación del programa.
3. Enlace del programa con las funciones que se necesitan de la biblioteca.
Actualmente, la mayoría de los compiladores de C proporcionan entornos integrados de
desarrollo que incluyen un editor. Los compiladores sólo admiten como entrada archivos de
texto estándar. Por ejemplo, el compilador no aceptará archivos creados con ciertos
procesador este texto, debido a que contendrán códigos de control y caracteres no
imprimibles.
El método exacto que se utilice para compilar un programa, dependerá del compilador de C
que según se usan. Además, la forma en que se lleve a cabo el enlace variará entre distintos
compradores y entornos. Por ejemplo, el lanzador puede estar incluido como parte del
compilador o puede ser una aplicación independiente .
67
Arquitectura y programación de ordenadores
Mapa de memoria de C
Un programa compilado en C crea y usa cuatro regiones de memoria lógicas diferentes que
sirven para funciones específicas. La primera región es la memoria contiene realmente el
código del programa. La siguiente región es la memoria donde se guardan las variables
globales. Las dos regiones restantes son la pila (stack) y el montón (heap). La pila se usa para
muchas cosas durante la ejecución del programa. Mantiene las direcciones de vuelta para las
llamadas funciones, así como las variables locales. También sirve para guardar el estado
actual de la CPU. El montón es la región de memoria libre que puede usar el programa
mediante las funciones de asignación dinámicas.
Aunque la disposición física esa acta de cada una de las cuatro regiones de memoria difiere
entre los distintos tipos de procesador y entre las distintas implantaciones de C, las siguiente
figura muestra de forma conceptual como aparece el programa de C en la memoria.
68
Arquitectura y programación de ordenadores
4.2 FUNCIONES, EXPRESIONES, VARIABLES Y OPERANDOS
Las expresiones se forman con los elementos atómicos de C: los datos y los operadores. Los
datos se puede representar mediante variables, constante o valores devueltos por las funciones.
C admite varios tipos diferentes de datos. También proporciona una amplia variedad de
operadores.
Tipos de datos básicos
C89 define cinco tipos de datos básicos: carácter, entero, real, real de doble presión y sin
valor. Se declaran con char, int, float, double y void, respectivamente. Todos los demás tipos
de datos se basan en alguno de estos. El tema y el intervalo de estos tipos varían con cada tipo
de procesador y de compilador. Sin embargo, en todo los casos un carácter es un byte. El
tamaño entero normalmente es el mismo que el tamaño de una palabra de memoria en el
entorno de ejecución del programa. Para la mayoría de los entornos de 16 bits, como DOS y
Windows 3.1, un int ocupa dieciséis bits. Para la mayoría de los entornos de 32 bits, como
Windows 95/98/NT/2000, un int ocupa 36 bits punto es importante saber que C sólo estipula
el intervalo mínimo para cada tipo de datos pero los tamaño en byte.
Los valores del tipo char, se usan normalmente para guardar valores definidos en el juego de
caracteres ASCII. Los valores que estén fuera de este intervalo pueden ser manejados de
forma diferente por los distintos compiladores.
el intervalo de los tipos float y double dependerá del método utilizado para representar los
reales en como flotante el estándar de C específica el mínimo intervalo para un valor real en
coma flotante es de 1E-37 a 1E+37. En la tabla 4 se muestra los mínimos números de dígitos
de presión para cada tipo de como flotante.
El tipo void, o bien declara explícitamente una función que no devuelve valor alguno, o crea
punteros genéricos.
69
Arquitectura y programación de ordenadores
Tabla 4 Todos los tipos de datos definidos por el estándar de C
Tipo
Tamaño
Intervalo mínimo
aproximado
en bits
char
8
-127 a 127
unsigned char
8
0 a 255
signed char
8
-127 a 127
int
16 o 32
-32278 a 32767
unsigned int
16 o 32
0 a 65535
signed int
16 o 32
Igual que int
short int
16
-32278 a 32767
unsigned short int
16
0 a 65535
signed short int
16
Igual que short int
long int
32
-2.147.483.467 a 2.147.483.467
long long int
64
-(263-1) a 263-1 (Añadido por C99)
signed long int
32
Igual que long int
unsigned long int
32
0 a 4.294.967.295
unsigned long long int
64
0 a 264-1 (Añadido por C99)
float
32
1E-37 a 1E+37 con seis dígitos de precisión
double
64
1E-37 a 1E+37 con diez dígitos de precisión
long double
80
1E-37 a 1E+37 con diez dígitos de precisión
Modificación de los tipos básicos
A excepción del tipo void, los tipos de datos básicos pueden tener distintos modificadores
precediendolos. Un modificador se usa para alternar el significado del tipo base de forma que
se ajuste a las necesidades de cada momento. A continuación se muestra la lista de
modificadores:
signed
70
Arquitectura y programación de ordenadores
unsigned
long
short
El tipo int se pueda modificar con signed, unsigned, long y short. El tipo char se puede
modificar con signed y unsigned. long también se puede aplicar a double ( C99 también
permite que long modifique a long).
Variables
Una variable es una posición de memoria con nombre que se usa para mantener un valor que
puede ser modificado por el programa. Todas las variables han de estar declaradas antes de
poder ser utilizadas. La forma general de declaración es:
tipo lista_de_variables;
Aquí, tipo debe ser un tipo de dato válido con algún modificador, y la lista de variables puede
consistir en uno o más nombres de identificadores separados por comas. A continuación se
muestran algunas declaraciones:
int i, j, l;
short int si;
unsigned int ui;
double balance, beneficio, pérdida;
Existen tres sitios básicos donde se pueden declarar variables: dentro de las funciones, en la
definición de parámetros de función y de fuera de todas las funciones que. En las variables
son, respectivamente, las variables locales, los parámetros formales y las variables globales.
71
Arquitectura y programación de ordenadores
Variables locales
Las variables que se declaran dentro de la función se denominan variables locales, y sólo
pueden ser utilizadas en las instrucciones que estén dentro del bloque en el que han sido
declaradas. Por lo tanto no pueden ser conocidas fuera de su propio bloque de código. La
variables locales sólo existe mientras está ejecutando el bloque de código en el que son
declaradas, o sea, se crea al entrar en el bloque y se destruye al salir de él. Más aún, una
variable declarada dentro de un bloque de código no tienen que ver con otra variable con el
mismo nombre que se ha declarado en otro bloque de código distinto. En C89 se deben
declarar todas las variables locales al principio del bloque en el que se usen, antes de cualquier
instrucción ejecutable. Sin embargo en C99 (y en C++), pueden declararse variables locales en
cualquier lugar de un bloque, previamente a su primer uso.
Aunque las variables locales no pueda retener sus valores entre llamadas, se puede indicar al
compilador que retenga sus valores mediante el uso del modificado static.
A menos que se especifique lo contrario, las variables locales se guardan en la pila. El hecho
de que la pila sea la región de memoria dinámica y cambiante, explica por qué las variables
locales no pueden, en general, mantener sus valores entre llamadas a funciones.
A los nuevos continuación se muestran algunos ejemplos del uso de variables locales en
funciones y bloques.
void func1(void)
{
int x;
x=10;
}
72
Arquitectura y programación de ordenadores
void func2(void)
{
int x;
x=-100;
}
La variable entera x se declara dos veces, pero se corresponden ni tiene relación. Tal como se
dijo, cada x sólo es conocida en el propio código en que se ha declarado la variable.
void f(void)
{
int t;
scanf(" %d ",&t);
if (t==1) {
char s[80]; /*esta se crea solo al entrar en este bloque*/
printf("Introduzca el nombre:");
gets(s);
/*Hacer algo*/
}
/* s no se conoce aquí*/
}
Aquí la variable local s las se crea tras entrar en el bloque de código if y se destruye al salir de
él, y sólo es conocida dentro de éste y no puede ser referenciada fuera de él; ni siquiera en
otras partes de la función que la contiene.
73
Arquitectura y programación de ordenadores
#include <stdio.h>
int main(void)
{
int x;
x=10;
if(x==10) {
int x; /* esta x oculta a la x externa */
x=99;
printf("x interna: %d\n", x);
}
printf("x externa: %d\n", x);
return 0;
}
el programa muestra los siguiente:
x interna: 99
x externa: 10
en este ejemplo, la x que se ha declarado en el bloque if interno oculta a la x externa. por
tanto, la x interna y la x externa son dos objetos independientes y distintos. Una vez que se
bloque termina, la x externa vuelve a ser visible.
74
Arquitectura y programación de ordenadores
Parámetros formales
Si una función va a usar argumentos, debe declarar las variables que van a aceptar los valores
de los argumentos. Esas variables son los parámetros formales de la función. Se comportan
como cualquier otra variable local de la función. Como se muestra en el siguiente fragmento
de programa, sus declaraciones se dan las el nombre de la función y entre paréntesis:
/* Devuelve 1 si c es parte de la cadena s; si no, 0 */
int esta_en(char *s, char c)
{
while(*s)
if (*s==c) return 1;
else s++;
return 0;
}
Esta función tiene dos parámetros, s y c, y devuelve 1 se el carácter especificado en c está
contenido en la cadena s; si no es así, devuelve 0.
Parámetros formales reciben los valores de los argumentos que se pasan a la función, por lo
demás se comportan como cualquier otra variable local. Por ejemplo, se pueden realizar
asignaciones a los parámetros o utilizarlos en expresiones correctamente construidas. Hay que
tener presente que, igual que las variables locales, también son dinámicas y se destruyen a
salir de la función.
Variables globales
A diferencia de las variables locales, las variables globales se conocen a lo largo de todo el
programa y se pueden usar en cualquier parte del código. Además, mantiene en sus valores
durante toda la ejecución del programa. Las variables globales se crean al declararlas en
75
Arquitectura y programación de ordenadores
cualquier parte fuera de una función. Pueden ser accedidas por cualquier expresión,
independientemente de la función en la que se encuentre la expresión.
#include <stdio.h>
int cuenta; /* cuenta es global */
void func1(void);
void func2(void);
int main(viod)
{
cuenta=10;
func1( );
return 0;
}
void func1(void)
{
int temp;
temp=cuenta;
func2( );
printf("cuenta es %d", cuenta); /* imprimirá 100 */
}
void func2(void)
{
int cuenta;
76
Arquitectura y programación de ordenadores
for(cuenta=1; cuenta<10; cuenta++);
putchar('.');
}
Debe estar claro que aunque ni main( ) ni func1( ) han declarado la variable cuenta, ambas
pueden usarla. Sin embargo, la función func2( ) declara una variable local llamada cuenta.
Cuando func2( ) referencia cuenta como sólo referencias su variable local, no la global. Si
una variable global y una variable local tienen el mismo nombre, todas las referencias a ese
nombre de variable dentro de la función donde se ha declarado la variable local se efieren a
esa variable local y no tienen efecto sobre la variable global.
El almacenamiento de las variables globales se hace en una región de memoria fija establecida
para este propósito por el compilador. Las variables globales son muy útiles cuando se usan
los mismos datos en varias funciones del programa. Sin embargo, se debe evitar el uso de
variables globales innecesarias. Utiliza memoria todo el tiempo de ejecución programa, no
sólo cuando se necesitan. Además, en uso de una variable global cuando se podría usar una
variable local hace que la función sea menos general debido a que depende de algo que debe
estar fuera de ella. Finalmente, que el uso de un gran número de variables globales puede
producir errores en el programa debido a efectos secundarios desconocidos y no deseables. Un
gran problema en el desarrollo de grandes programas es el cambio accidental del valor de una
variable por haber sido utilizada en alguna otra parte del programa. Esto puede ocurrir en C si
se usan demasiadas variables globales en los programas.
77
Arquitectura y programación de ordenadores
4.3 INSTRUCCIONES
Secuenciación
La manera de secuenciar instrucciones en C consiste en escribirlas consecutivamente
(notemos que el punto y coma en C es un terminador de instrucciones y no un separador). Por
otra parte, para aumentar la legibilidad de los programas, es hábito escribir instrucciones
diferentes en líneas diferentes y respetar el uso habitual de la identación que ya hemos
utilizado en los algoritmos.
Alternación
La estructura de alternación en C se realiza mediante la instrucción if—else—if, con el
formato siguiente:
if (condicion_1) {
secuencia_de_instrucciones_1
}
else if (condicion_2) {
secuencia_de_instrucciones_2
}...
else if (condicion_k) {
secuencia_de_instrucciones_k }
Observaciones:
78
Arquitectura y programación de ordenadores
( i ) En caso de tener una sola instrucción en lugar de una secuencia, no hace falta cerrarla
entre llaves.
( ii ) Si condicion_k es exactamente la negación de todas las condiciones precedentes,
podemos no escribir la parte if (condicion_k), ya que C interpreta el else como lo que
significa en inglés, o sea, en caso contrario.
( iii ) Además, si secuencia_de_instrucciones_k es la secuencia vacía, podemos no escribir
toda la parte
else if (condicion_k) {
secuencia_de_instrucciones_k
}
Hay un caso para el cual C provee una instrucción alternativa especifica. Cuando en la
estructura anterior todas las protecciones afirman la igualdad de una variable determinada con
constantes predefinidas, se puede utilizar la instrucción switch que presenta el formato
siguiente:
switch (variable) {
case constante_1: {
secuencia_de_instrucciones_1
} break;
case constate_2: {
secuencia_de_instrucciones_2
} break;
...
79
Arquitectura y programación de ordenadores
case constante_k: {
secuencia_de_instrucciones_k
} break;
default: {
secuencia_de_instrucciones_por_defecto
}
}
Al igual que con el if, si la secuencia_de_instrucciones_por_defecto es la secuencia vacía, se
pueden omitir el default y la secuencia misma.
Iteraciones
En C hay tres maneras de implementar iteraciones. La más básica es la sentencia de iteración
while:
while (condición) {
secuencia_de_instrucciones
}
Una variación del while es la instrucción do, que se diferencia de la primera en que verifica la
condición después de ejecutar la secuencia_de_instrucciones y, por lo tanto, la ejecuta como
mínimo una vez. Su sintaxis es
do {
80
Arquitectura y programación de ordenadores
secuencia_de_instrucciones
} while (condición)
Finalmente, la instrucción
for (variable = valor_inicial; variable<=valor_final; variable = variable + paso) {
secuencia_de_instrucciones
}
donde paso indica el incremento (o decremento) de la variable.
4.4 ARRAYS, CADENAS Y PUNTEROS
La declaración de vectores en C se hace mediante el siguiente formato:
tipo nombre_vector [dim]
Donde dim ha de ser un número entero y denota la dimensión o longitud del vector. Así, las
instrucciones siguientes:
int datos[10]:
char nombre[12], apellido[12];
Declaran un vector de enteros de 10 elementos y dos vectores de caracteres de 12 elementos
cada uno.
Una vez declarado un vector de n elementos, se puede leer y escribir en sus componentes
teniendo en cuenta que éstas se numeran de la 0 a la n-1. Por ejemplo, para referenciar la
81
Arquitectura y programación de ordenadores
primera letra de apellido se ha de escribir apellido[0] y para referenciar el último elemento de
datos se ha de escribir datos[9].
Para declarar variables indexadas con mas de un índice, el formato es
Tipo nombre_variable[dim_1][dim_2]...[dim_k]
Usualmente se llama vector una variable con un índice y matriz una variable con dos índices.
Además, el primer índice se llama fila, el segundo columna y el tercero página. A partir del
cuarto índice no se utilizan nombres específicos.
Cadenas de caracteres
Es frecuente emplear vectores para la manipulación de cadenas de caracteres, como nombres
de personas, de calles, etc. En estos casos, hemos de declarar la variable con una longitud
capaz de alojar el nombre más largo de entre los valores posibles. Así, para almacenar
nombres de personas, podemos declarar el vector
char nombre[15]
si se sabe que 15 es una cota para el tamaño de los nombres que aparecerán durante la
ejecución. Ahora bien, resulta muy incómodo tener que rellenar con espacios el resto de las
letras hasta la 15ª cada vez que se lee un nombre desde el teclado. Para facilitar el tratamiento
de estos vectores de caracteres con longitud variable, C tiene un tipo predefinido que resulta
muy conveniente: el tipo string (cadena de caracteres). Su declaración es la normal de un
vector de caracteres. Lo que cambia es su lectura y escritura. En el momento de leer una
cadena de caracteres, el computador los leerá uno a uno hasta encontrar un cambio de línea.
Cuando esto suceda, añadirá al final de la secuencia leída un carácter nulo (el carácter con
código ASCII igual a 0). Esta marca le permitiría saber posteriormente donde se encuentra el
fin de la cadena. Huelga decir que si sabemos que la longitud máxima de las cadenas a tratar
es k, hace falta declarar el vector de dimensión k+1 para poder alojar también esta marca. Así,
en el ejemplo anterior se debería declarar
char nombre[16]
82
Arquitectura y programación de ordenadores
La lectura de una cadena de caracteres se realiza poniendo en el control el formato %s y
quitando del nombre de la variable el prefijo & en la instrucción scanf. Existe otra manera de
leer una cadena desde el teclado, usando la instrucción gets con el formato
gets (nombre_cadena);
que lee todos los caracteres tecleados hasta pulsar la tecla retorno. Para la escritura, también
hace falta utilizar el formato %s en la parte de control del printf.
C también posee una gran variedad de funciones para manipular cadenas. Las más corrientes
son
strcpy (cad_1, cad_2);
strcat (cad_1, cad_2);
res_comparacion = strcmp (cad_1, cad_2);
longitud = strlen (cad);
La primera copia el contenido de cad_2 sobre cad_1. La segunda añade el contenido de cad_2
al final de cad_1. La tercera compara lexicográficamente los contenidos de las cadenas que
recibe como entradas y retorna 0 si son la misma. Si cad_1 es mayor que cad_2, entonces
retorna un número positivo y, si es menor, uno negativo. Finalmente strlen(cad) retorna la
longitud de cad. Para utilizar estas instrucciones hace falta importarlas del módulo string.
Finalmente, cabe destacar que las cadenas constantes se encierran entre comillas dobles ". Por
ejemplo, en
strcpy (cad, "hola");
se asocia la cadena hola a la variable cad.
83
Arquitectura y programación de ordenadores
4.5 ESTRUCTURAS, UNIONES, ENUMERACIONES Y TYPEDEF
4.5.1 Estructuras
Las estructuras (struct) son agrupaciones de una o más variables de tipos posiblemente
diferentes, agrupadas bajo un solo nombre, que permite un manejo más cómodo de la
agrupación de la información. Las struct son estructuras de datos similares a los registros
(record) de Pascal. La forma general de definición es:
struct tipo_estructura
{
tipomiembro_1;
tipo miembro_2;
.
tipo miembro_n;
} variable_estructura;
donde tipo_estructura o variable_estructura pueden omitirse pero no ambos. Las estructuras
ayudan a agrupar información relacionada, por ejemplo los datos de un carnet de identidad, las
coordenadas de un punto, etc.
Ejemplos de declaración de estructuras
struct datos
{
char nombre[20];
char direccion[20];
int codigo;
};
struct datos a,b[5]; /* b es un array de estructuras */
84
Arquitectura y programación de ordenadores
Estructuras anidadas: se producen cuando algún miembro de la estructura es a su vez otra
estructura.
struct fecha
{
int dia;
int mes;
int anio;
};
struct persona
{
char nombre[20];
struct fecha nacimiento;
};
struct persona p;
Referencia a los elementos de una estructura
Los elementos individuales de una estructura se referencian utilizando el operador punto (.)
entre el nombre de la variable de tipo estructura y el nombre del miembro de la estructura. A
los
elementos
de
una
estructura
se
les
denomina
miembros.
Continuando con las estructuras de los ejemplos anteriores, se pueden tener las siguientes
referencias a miembros:
a.nombre
a.dirección
p.nombre
a.codigo
p.nacimiento.dia
b[2].codigo
p.nacimiento.mes
Funciones y Estructuras
A Paso por valor de miembros de una estructura a una función.
Se realiza como si fueran variables simples. Por ejemplo, para pasar por valor el miembro
a.codigo
85
Arquitectura y programación de ordenadores
void funcion f1(int) /*declaración de la función prototipo*/
f1(a.codigo); /* llamada a la función */
void f1(int x) /* definición de la función */
{ ........
..........}
B Paso por dirección de miembros de una estructura a una función.
Se realiza como si fueran variables simples. Por ejemplo, para pasar por valor el miembro
a.codigo
void funcion f1(int *) /*declaración de la función prototipo*/
f1(&a.codigo); /* llamada a la función */
void f1(int *x) /* definición de la función */
{..........
..........}
Hay que tener en cuenta que si lo que se pasa a una función es un miembro de una estructura
que sea un array éste siempre se pasa por dirección (ya que el nombre de un array es la
dirección del primer elemento del mismo).
C Paso por valor de estructuras completas a funciones.
En el siguiente ejemplo suma es una función que recibe dos estructuras pasadas por valor y a
su vez devuelve una estructura.
struct vector
{
int x,y,z;
};
struct vector (struct vector, struct vector);
void main()
{
86
Arquitectura y programación de ordenadores
struct vector v1,v2,v3;
...
v3=suma(v1,v2);
...
}
struct vector suma(struct vector v1, struct vector v2)
{
v1.x+=v2.x;
v1.y+=v2.y;
v1.z+=v2.z;
return (v1);
}
D Paso por dirección de estructuras completas a funciones
Cuando las estructuras son muy grandes es más eficiente pasarlas por dirección. En ese caso
se utilizan punteros a estructuras para realizar la comunicación. Para acceder a los miembros
de la estructura debe utilizarse la combinación de los operadores * y punto. Sin embargo el
operador punto tiene más precedencia que * siendo necesario el uso de paréntesis para
asegurar que se aplique primero * y después punto. También puede utilizarse el operador ->
para acceder a los miembros de una estructura referenciada por un puntero.
#include <stdio.h>
struct pareja
{
int a,b;
};
void f1(struct pareja *);
void main()
{
struct pareja p = { 13, 17} /* inicialización de los
87
Arquitectura y programación de ordenadores
miembros*/
f1(&p);
printf("valor de a:%d valor de b:%d\n",p.a,p.b);
/* escribe 14 y 18 */
}
void f1(struct pareja *q)
{
q->a++; /* equivalente a (*q.a)++ pero más usado */
q->b++;
return;
}
4.5.2 Uniones
Las uniones tienen una sintaxis similar a las estructuras, pero se diferencian de éstas en que
sus miembros comparten almacenamiento. Una variable unión define a un conjunto de valores
alternos que pueden almacenarse en una porción compartida de memoria. Es una versión C de
los
registros
variantes
de
otros
lenguajes
como
Pascal.
El compilador asigna una porción de almacenamiento que pueda acomodar al más grande de
los miembros especificados. La notación para acceder a un miembro de la unión es idéntica a
la que se emplea para acceder a un miembro de una estructura. Un ejemplo de unión es el
siguiente:
union ejemplo{
int codigo;
char nombre[10];
char nif[12];
};
Una unión sólo puede ser inicializada con un valor del tipo de su primer miembro union
ejemplo a={123};
88
Arquitectura y programación de ordenadores
4.5.3 Enumeraciones
La definición de tipos por extensión o enumeración sirve para definir tipos de datos que toman
valores en un conjunto finito y tiene una sintaxis muy simple.
typedef enum {valor_1, valor_2, ..., valor_k} nombre_tipo
con esta definición, además, los k valores quedan ordenados con el orden en el que han sido
declarados. Si se mira la definición del tipo booleano se puede ver que ha sido definida de esta
manera. El hecho de poder hacer estas identificaciones puede resultar útil en muchos casos.
Por ejemplo:
typedef enum {lunes=0, martes=1, miercoles=2, jueves=3,viernes=4} dia_laborable;
Para definir tipos nuevos mediante el constructor tabla se ha de utilizar la palabra reservada
typedef,
esta vez con el formato
typedef tipo_conocido tipo_nuevo[dim_1]...[dim_k];
donde dim_k es la longitud del k-ésimo dominio.
El formato del constructor tupla en C es el siguiente
typedef struct {
tipo_1 nombre_1;
tipo_k nombre_k; } tipo_nombre_nuevo;
donde, naturalmente, los tipos de la declaración anterior pueden ser cualesquiera de entre los
tipos primitivos o definidos previamente y los nombres pueden ser de variables indexadas.
Para acceder a los campos de una variable con un tipo definido mediante el constructor tupla,
también se utiliza el punto. Así, si cliente es una variable del tipo persona y este tipo está
definido como:
typedef struct {
char nombre[15];
char apellido[15];
int edad;
char ciudad[15]; } persona;
89
Arquitectura y programación de ordenadores
las expresiones siguientes:
cliente.nombre
cliente.edad
cliente.ciudad[0]
denotan respectivamente, el nombre, la edad y la primera letra de la ciudad de cliente.