Documente Academic
Documente Profesional
Documente Cultură
La definición de tipos se realiza mediante la clausula typedef. Por ejemplo, puede ser
útil en la documentación del programa redefinir tipos básicos con nombres que indiquen
su función. Por ejemplo si vamos a definir variables que van a ser contadores, el
programador puede definir un tipo Contador, renombrando el tipo int, para dejar claro
que esas variables tienen esa función. Igualmente en una aplicación de la Secretaría de
un Centro puede resultar adecuado definir el tipo CódigoAsignatura como una
denominación particular del tipo long. Así se pondría en la sección de definición de
tipos:
1.1 Los tipos enumerados. C permite definir un tipo especial de datos para aquellos
valores que sólo pueden tomar un conjunto finito de valores, normalmente etiquetas.
Por ejemplo, imaginemos una aplicación que maneja sólo tres posibles colores: azul,
rojo y verde y que queremos definir una variable color_pantalla que sólo pueda tomar
esos valores. ¿Cómo lo haríamos?:
y la variable:
Tema 4. El tipo Array
De esta manera, la variable color_pantalla sólo podrá tomar los valores señalados:
color_pantalla = azul;
color_marco = verde;
Nótese que azul o verde son etiquetas, no cadenas de caracteres (no hay ni debe haber
dobles comillas). Internamente en la memoria del ordenador las variables enumeradas se
guardan como valores enteros a partir del cero. Esto es, en la definición anterior azul es
el valor 0, rojo el valor 1 y verde el 2.
1.2 El tipo Lógico. Como hemos señalado anteriormente la versión ANSI del lenguaje
C no tiene predefinido un tipo booleano para guardar los valores de cierto y falso, sino
que a partir de valores enteros simula el valor de falso con un cero y el valor de cierto
con cualquier valor distinto de cero. Una forma de soslayar este inconveniente es definir
nuestro propio tipo booleano:
Nótese que el orden de las etiquetas debe ser obligatoriamente primero falso (igual a 0)
y después cierto (igual a 1). De esta forma podemos definir o incluso inicializar
variables booleanas:
if (encontrado){
…
…
}
while(!ordenado){
…
…
}
2. El tipo array
día, etc. La gestión de un aeropuerto tiene colecciones de vuelos, listas de pasajeros, etc.
En el ámbito de la salud, listas de pacientes, conjuntos de médicos, resultados de
experimentos, etc.
lo cual es una solución al menos engorrosa ya que tendríamos que manejar tres variables
para un solo concepto. La mejor solución es agrupar las tres coordenadas en un solo
tipo, como vemos en la siguiente sección.
2.2 El tipo array. Al ser C un lenguaje antiguo sólo dispone de un tipo “colección”1, un
tipo que podríamos llamar vector, tabla o por su nombre en inglés: array2. Podemos
entonces definir un array como un conjunto indexado de datos del mismo tipo
agrupados bajo un mismo identificador. En un array hay que tener en cuenta los
siguientes conceptos:
1
Java tiene este problema perfectamente resuelto.
2
Nosotros en este manual emplearemos normalmente el nombre de array, ya que vector es un
concepto matemático y tabla se suele reservar para representaciones bidimensionales tipo matriz. En
algunos textos hispano-americanos incluso es fácil encontrar la palabra “arreglo”.
Tema 4. El tipo Array
float punto3D[3];
Entonces podremos dar el valor (1.1, 3.2, -4.5) con tres sentencias de asignación:
punto3D[0]=1.1;
punto3D[1]=3.2;
punto3D[2]=-4.5;
#define DIM 3
…
typedef int Vector3D[DIM];
…
Vector3D punto3D = {1.1, 3.2, -4.5};
3
Esta es la explicación por la que los bucles for se suelen iniciar en 0 y terminar en n-1, en vez de
empezar en 1 y acabar en n.
Autor: José C. Riquelme (Universidad de Sevilla)
Una de las cuestiones que más trabajo cuesta entender a un alumno principiante es la
diferencia entre dimensión y tamaño real. Intentemos explicarlo:
2.4 Uso de un array. Las variables de tipo array no se pueden asignar con un operador
de asignación (=) o no se pueden comparar con un operador ==. Las variables de tipo
array se deben manejar posición a posición mediante el operador [ ] y un índice. Por
tanto, la mejor opción son los bucles for.
2.4.1 Bucles for. Como se ha señalado anteriormente los arrays tienen un número de
elementos que es su tamaño real. Por tanto, su tamaño aunque puede ser variable es
conocido por el programador y por eso, el uso habitual de los arrays es mediante bucles
for. Por ejemplo, cómo almacenar los n primeros cuadrados perfectos en un array:
Ejemplo 1:
#define DIM 20
4
Es la memoria que se conoce como memoria estática, en contraposición con la memoria dinámica que
se puede reservar en tiempo de ejecución y que no será materia de este manual.
Tema 4. El tipo Array
…
typedef int VectorEntero[DIM];
…
void main(void){
int i,n;
VectorEntero v;
Sin embargo, ya hemos señalado que la programación en C debe ser modular y que, por
tanto, es imprescindible manejar los arrays en las funciones de C.
2.4.1 ¿Cómo se pasa un argumento de tipo array a una función? Hay varias maneras de
que una función reciba y devuelva un array. Aunque profundizaremos en temas
posteriores sobre el uso de argumentos de entrada/salida en las funciones en C, en este
momento nos basta con saber dos cuestiones importantes:
Ejemplo 2:
#include <stdio.h>
#define DIM 20
typedef int VectorEntero[DIM];
void prueba_argumento(int);
void prueba_array(VectorEntero);
void main(void){
int i,n;
VectorEntero v={1,2,3,4,5};
n=1;
prueba_argumento(n);
printf("n vale %d\n",n);
for(i=0;i<5;i++){
printf("%d\n",v[i]);
}
prueba_array(v);
a vale 2
n vale 1
valores de v antes de función
1
2
3
4
5
valores de v después de función
2
2
3
4
5
¿Qué quiere decir este experimento? Que el valor int cómo argumento de la función
prueba_argumento es sólo de entrada, de forma que si el parámetro formal a se modifica
en la función sí cambia de valor dentro de la función, pero cuando se termina el
parámetro real n sigue valiendo lo mismo. Esto mismo hubiera ocurrido con argumentos
de tipo float, long, char, etc. Si embargo, con el argumento v no pasa lo mismo en la
función prueba_array. El parámetro real v tiene unos valores {1,2,3,4,5} antes de la
invocación, y el parámetro formal t cambia el primer valor del array. Este nuevo valor
de t queda reflejado en v cuando la función termina y el control vuelve al programa
principal.
¿Por qué sucede esto? Es decir ¿Por qué los argumentos de tipos básicos (int, float,
char, …) se comportan de forma diferente a los arrays cuando son argumentos de
funciones? La razón viene dada porque los argumentos reales de tipo básico son
“copiados” en los parámetros formales, pero no comparten zona de memoria. Sin
Tema 4. El tipo Array
embargo, por razones de eficiencia5 (de espacio y tiempo) los arrays no son copiados,
sino que el parámetro real (v en el caso anterior) y el formal (t) comparten zona de
memoria, y por tanto, toda acción sobre t tiene su efecto sobre v.
Para evitar efectos colaterales no deseados cuando una función recibe un vector que no
se debe modificar en la función, C proporciona el mecanismo de declararlo de tipo
constante. Para ello basta con poner la palabra const delante del tipo vector tanto en el
prototipo como en la cabecera de la definición:
En este caso, el compilador nos avisaría de que estamos intentando modificar un valor
declarado constante cuando compila la sentencia t[0]++.
Podemos intuir que este programa tiene dos partes claramente diferenciadas. En la
primera parte, deberemos leer un número indeterminado de valores reales y
almacenarlos para posteriormente usar esos valores para sumarlos y dividir por el
número de valores leídos. Es importante señalar que la división del problema en estas
dos partes, no sólo se hace por dividir el problema en tareas diferentes, sino que además
conseguimos que los “módulos” definidos puedan ser reutilizados con posterioridad.
Resolvamos entonces este problema en distintas fases. En primer lugar, tendremos que
definir los tipos necesarios para el problema. Necesitamos almacenar un número
indeterminado de valores reales pero menos de 100. Es decir, necesitamos un array de
datos de tipo float de dimensión 100. Para ello definiremos una constante MAXALUM
de valor 100 y un tipo VectorNotas de dimensión MAXALUM.
En la segunda fase tendremos que definir una función que lleve a cabo la lectura de los
valores. ¿Qué valores debe recibir esta función para llevar a cabo su cometido? ¿Qué
valores debe devolver? Hay dos posibilidades que el número de valores a leer se lea
también dentro de la función o que se lea antes de su invocación en el programa
principal. En el primer caso la función no necesita recibir ningún valor (void) y debe
5
Recuerde que el C es un lenguaje de los años 70 del siglo pasado
Autor: José C. Riquelme (Universidad de Sevilla)
Para ello, la función debe recibir un entero con el número de valores a leer (será un
argumento de entrada) y debe devolver un array que como ya hemos dicho no puede ser
devuelto en el nombre de la función. Entonces el prototipo de esta función es:
El código de esta función será básicamente un bucle for para leer el número de valores
que indique el argumento de tipo int y guardarlos en el VectorNotas:
Finalmente la función que calcule la media deberá recibir como argumento de entrada el
vector con las notas y el número de notas (tamaño real del vector) y devolver un valor
real con la media. Este valor de tipo float si puede ser devuelto en el nombre de la
función que tendrá un argumento de tipo int y otro const de tipo VectorNotas. El código
puede ser:
for(i=0;i<n;i++){
suma = suma + t[i];
}
return suma/n;
}
void main(void){
int i,n;
VectorNotas vnotas;
float media;
leerNotas(n,vnotas);
media=notaMedia(n,vnotas);
¿Cuál es la principal ventaja de este código? Su reutilización, con muy pocos cambios
nos sirve para leer cualquier lista de valores reales y calcular su media.
3. Cadenas de caracteres.
3.1 Tipo Cadena. Aunque casi todos los lenguajes modernos tienen un tipo predefinido
para las cadenas de caracteres (normalmente con el nombre de String), sin embargo el
ANSI C no lo tiene. Por tanto, las cadenas de caracteres se implementan mediante un
array de tipo char6. Para ello, lo más cómodo es al igual que hacemos con el tipo
Logico, definir nuestro propio tipo Cadena:
Nótese que la variable palabra es inicializada con la palabra Hola mientras que dni no
está inicializada a ningún valor.
3.2 Uso del tipo Cadena. No debemos olvidar que una variable de tipo Cadena es
realmente un array. Eso significa que no se le puede asignar valor directamente
mediante el símbolo = (salvo la inicialización a la vez que la declaración). Sin embargo,
a diferencia de otros arrays y debido al gran uso que este tipo de variables tiene, C
proporciona un conjunto de funciones predefinidas para el manejo de las cadenas de
caracteres:
6
En los libros de texto tradicionales las cadenas de caracteres se implementan de diversos modos,
incluyendo punteros a char. Desde nuestro punto de vista es un error inducir al alumno principiante en
esa forma de definir y usar las cadenas de caracteres debido a la gran probabilidad de cometer errores
por no tener bien reservada la memoria que se usa.
Autor: José C. Riquelme (Universidad de Sevilla)
Todas estas funciones se encuentran en la librería string.h que deberá ser importada
antes de su uso7.
Finalmente las variables de tipo Cadena pueden ser leídas y escritas mediante las
sentencias scanf y printf8 usando el formato %s. Es importante señalar que la función
scanf para cadenas de caracteres no antepondrá un símbolo & antes de la variable, como
ha sucedido hasta ahora.
Ejemplo 3 (Uso de cadenas de caracteres): Escriba un programa que lea tres cadenas de
caracteres con el nombre y dos apellidos de una persona y forme e imprima una cadena
de caracteres con el formato:
Primer_apellido Segundo_apellido, Inicial del nombre.
#include <stdio.h>
#include <string.h>
#define MAXCAR 256
typedef char Cadena[MAXCAR];
void main(void){
Cadena apel1, apel2, nombre;
Cadena inicial, resultado;
printf("escriba el nombre\n");
scanf("%s",nombre);
printf("escriba el primer apellido\n");
scanf("%s",apel1);
printf("escriba el segundo apellido\n");
scanf("%s",apel2);
inicial[0]=nombre[0];
inicial[1]='.';
inicial[2]='\0';
7
Una característica importante de las cadenas de caracteres y que es fundamental para poder usar las
funciones de la librería string.h es responder a la pregunta de cómo sabe el C cuando termina una
cadena. Dicho de otra manera, cómo distingue el compilador entre tamaño real y dimensión.
Recordemos que cuando se manipulan los arrays de tipo int o float debe usarse una variable que indique
el tamaño real de ese array. En las cadenas de caracteres ese problema está solucionado porque el
compilador añade un símbolo especial (‘\0’) al final de una cadena de caracteres. Este símbolo es usado
por el compilador de C para calcular la longitud de una cadena y a partir de esa información implementa
las funciones como strcpy o strcat. Esta cuestión es transparente para el programador salvo que se
vayan a manipular las cadenas de caracteres posición a posición, esto es, carácter a carácter.
8
Existen otras funciones para leer o escribir cadenas de caracteres en C, como gets o puts.
Tema 4. El tipo Array
strcpy(resultado,apel1);
strcat(resultado," ");
strcat(resultado,apel2);
strcat(resultado,", ");
strcat(resultado,inicial);
printf("%s\n",resultado);
}
Como se ha señalado en el último párrafo de la sección 2.2 de este tema, el tipo base de
un array puede ser cualquier tipo, incluyendo un array. En el caso de las cadenas de
caracteres, una forma habitual de uso en programación es organizarlas en tablas o arrays
de cadenas.
Para definir una variable de tipo TablaCadena basta con poner el tipo y la lista de
variables:
TablaCadena listaPalabras;
TablaCadena diasSemana={"lunes", "martes", "miércoles", "jueves",
"viernes", "sábado", "domingo"};
4.2 Uso de un array de cadenas. Los arrays de cadenas de caracteres se usan igual que
el resto de los arrays, salvo que hay que tener en cuenta que sus elementos son cadenas
de caracteres. Por tanto, no se pueden asignar o comparar directamente sino usando las
funciones de string.h. Por ejemplo vamos a resolver el siguiente:
4.2.1 Ejercicio. Se quiere realizar un programa que lea desde teclado una lista de
palabras (menos de 100) hasta leer la palabra FIN y después lea una palabra s, indicando
cuántas veces está s en las palabras leídas.
Para resolver este problema habrá que construir dos funciones, una que lea la lista de
palabras y otra que cuente las apariciones. La primera debe devolver el array de cadenas
y (MUY IMPORTANTE) el número de palabras leídas. La segunda debe recibir el array
Autor: José C. Riquelme (Universidad de Sevilla)
#include <stdio.h>
#include <string.h>
#define MAXCAR 256
#define NUMCAD 100
int leePalabras(TablaCadena);
int cuentaPalabra(const TablaCadena, int, Cadena);
void main(void){
int npal,contador;
Cadena palabra;
TablaCadena listaPalabras;
npal= leePalabras(listaPalabras);
for(i=0;i<n;i++){
if (strcmp(t[i],s)==0){
cont++;
}
}
return cont;
}
Tema 4. El tipo Array
5. Arrays bidimensionales.
Los arrays de cadenas de caracteres que hemos estudiado en la anterior sección son
realmente un array de array o array bidimensional9. Un array bidimensional corresponde
con el concepto matemático de matriz, entendiendo que una matriz no es más que un
array de filas y donde cada fila es a su vez un array.
#define NUMFIL 4
#define NUMCOL 6
…
typedef float MatrizReal[NUMFIL][NUMCOL];
…
MatrizReal m;
5.2 Uso de un array bidimensional. C permite usar los arrays bidimensionales de dos
maneras: por filas tratando cada una como un array de dimensión el número de
columnas o elemento a elemento. En general, los arrays bidimensionales presentan el
mismo problema respecto a diferenciar entre tamaño real y dimensión pero para cada
dimensión. Por tanto, cuando se usan deben venir acompañados de dos variables de tipo
entero que indiquen el número real de filas y columnas usadas.
Entonces el recorrido típico de una matriz es con un doble bucle for con dos índices
distintos (normalmente i, j) que para cada valor de i desde 0 hasta el número de filas
9
C permite arrays de cualquier número de dimensiones
Autor: José C. Riquelme (Universidad de Sevilla)
(menor estricto), esto es para cada fila, recorre las columnas con un bucle en que la j
varía de 0 hasta el número de columnas:
for(i=0;i<nf;i++){
for(j=0;j<nc;j++){
// acción sobre t[i][j]
5.2.1 Ejercicio. Escriba un programa que lea dos valores enteros indicando un número
de filas (menor de 4) y columnas (menor de 6) y después lea los elementos de una
matriz de reales y posteriormente sume todos los valores.
La solución debe tener dos funciones: la primera para leer la matriz que recibirá el
número de filas y columnas y devolverá la matriz leída; y la segunda, para sumar los
elementos que recibirá la matriz, el número de filas y columnas y devolverá en el
nombre de la función el valor de la suma.
void main(void){
int n,m;
MatrizReal mat;
float suma;
leeMatriz(mat,n,m);
suma = sumaMatriz(mat,n,m);
for(i=0;i<nf;i++){
for(j=0;j<nc;j++){
printf("deme el elemento (%d,%d): ",i,j);
scanf("%f",&t[i][j]);
}
}
}
for(i=0;i<nf;i++){
for(j=0;j<nc;j++){
sum= sum + t[i][j];
Tema 4. El tipo Array
}
}
return sum;
}
6. Problemas.
Para los siguientes problemas construya también un programa principal que invoque a
la función con distintos argumentos.
1. Construya una función tal que dado un array de números reales y un valor real p
devuelva cuántos elementos hay que sumar desde el primer valor para superar p.
2. Dado un valor entero n, construya una función que devuelva en un array los n
primeros valores primos. Supongamos n menor de 100.
4. Dada una matriz de números reales construya una función que devuelva su valor
máximo.
5. Dada una matriz de números reales construya una función que devuelva un array
con la suma de sus elementos por filas.
6. Dada una matriz de números reales construya una función que devuelva un array
con la suma de sus elementos por columnas.
7. Construya una función que dada una matriz de reales cuadrada devuelva en un
array su diagonal principal.
8. Construya una función que dada una matriz de reales cuadrada devuelva en un
array su diagonal secundaria.
9. Construya una función que reciba dos matrices reales de la misma dimensión y
devuelva su suma.
10. Dada una matriz y un índice de fila devuelva un vector con la fila que
corresponde.
Autor: José C. Riquelme (Universidad de Sevilla)
11. Dada una matriz y un índice de columna devuelva un vector con la columna que
corresponde.