TEMA 2. INTRODUCCION AL LENGUAJE C.
1
Evolución del lenguaje C
•
Dennis Ritchie diseña el lenguaje C en los laboratorios AT & T Bell en 1972.
Buscaba un lenguaje práctico y flexible para el desarrollo de UNIX. “The C Programming Lenguage”, Brian W. Kernighan y Dennis M. Ritchie, 1978. •
En 1989 apareció ANSI C y un año más tarde ISO C.
•
Surge C++ y en 1989 se crea un comité ANSI para la estandarización de este lenguaje.
•
En 1991 Bjarne Stroustrup publica un manual de referencia de C++.
•
Se crea el comité ISO WG21 que elabora un documento que será el estándar C++.
2
2.1. Conceptos básicos de C. 2.1.1. Estructura de un programa en C. Un programa en C consta de uno o más módulos llamados funciones. El programa comenzará con la ejecución de la función main. Cada función debe contener: -
Cabecera, con el nombre de la función y una lista de parámetros entre paréntesis.
-
Instrucción compuesta (entre llaves)
Ejemplo: Area de un círculo. #include <stdio.h> #define PI 3.14159 float procesar(float radio); main() { float radio, area; printf (“Radio = ?”); scanf(“%f”, &radio); area = procesar(radio); printf(“Area = %f”, area); } float procesar(float r) { float a; a = PI * r * r; return(a); }
3
2.1.2. El conjunto de caracteres de C. - Letras mayúsculas - Letras minúsculas - Dígitos del 0 al 9 - Caracteres especiales: + - * / = %
& #
¿ < > [ ] { } ( ) : ; ....
- Secuencias de escape: \b \n \t
2.1.3. Identificadores y palabras reservadas. - Los identificadores son nombres que se les da a elementos del programa. - Deben comenzar con letra y después admiten letras, dígitos y el carácter subrayado. - C distingue mayúsculas de minúsculas. Palabras reservadas. auto, extern, sizeof, break, float, static, case, for, struct, char, goto, switch, const, if, typedef....
2.1.4. Tipos de datos. int
Cantidad entera
2 bytes
char
Carácter
1 byte
float
Número en coma flotante 4 bytes
double
Número en coma flotante 8 bytes de doble precisión
Cualificadores de tipos: short, long, signed, unsigned.
4
2.1.5. Constantes. - Constantes enteras: en decimal, octal y hexadecimal. - Constantes en coma flotante: número en base 10 que contiene un punto decimal o un exponente o ambos. - Constantes de carácter: un sólo carácter entre comillas simples. Tabla de secuencias de escape. Caracter
Secuencia de escape
Valor ASCII
Sonido
\a
007
Retroceso
\b
008
Tabulador horizontal
\t
009
Tabulador vertical
\v
011
Nueva línea
\n
010
Avance de página
\f
012
Retorno de carro
\r
013
Comillas
\”
034
Comilla simple
\’
039
Signo de interrogación
\?
063
Barra inclinada
\\
092
Nulo
\0
000
- Constantes de cadena de caracteres: varios caracteres entre comillas. - Constantes enumeradas. 2.1.6. Variables. Variables: elementos del entorno que almacenan información dentro de una parte del programa. Cada variable tiene asociado un nombre, un tipo y un valor.
5
2.1.7. Declaraciones. Una declaración asocia un tipo de datos especificado a un grupo de variables. Ejemplos: int a, b, c; float raiz1, raiz2; char indicador, texto[80]; short q; double p; Se pueden asignar valores iniciales a las variables en la declaración de tipos: int c = 12; char estrella = ’*’; float suma = 0.; double factor = 0.23123e-6; char texto[10] = ”casa”; (automáticamente añade \0 al final de casa) 2.1.8. Instrucciones. Las acciones de un programa se ejecutan a través de instruciones. Las instrucciones en C terminan siempre en ; Las instrucciones compuestas se escriben entre llaves { } Ejemplos: a = 3; c = a + b; ++i; printf(“Area = %f”, area);
6
2.1.9. Constantes simbólicas. # define nombre texto Ejemplos: #define INTERES 0.23 #define PI 3.14159 #define TRUE 1 #define FALSE 0
2.2. Operadores y expresiones. 2.2.1. Operadores aritméticos. +
Suma
-
Resta
*
Multiplicación
/
División
%
Resto de la división entera
2.2.2. Operadores monarios. Operador incremento ++ Operador decremento -Operador sizeof: Devuelve el tamaño en bytes de su operando. Ejemplos: printf(“Entero: %d\n”, sizeof i); printf(“i=%d\n”, i++); printf(“i=%d\n”, i); ((int) f) % 2
con f en coma flotante
7
2.2.3. Operadores relacionales y lógicos. Operadores relacionales:
< , <= , > , >=
Operadores de igualdad:
==, !=
La expresión resultante será de tipo entero (verdadero=1 y falso=0) Operadores lógicos:
&&, ||
2.2.4. Operadores de asignación. Identificador = expresión Ejemplos: a = 3; suma = a+b; - En C están permitidas asignaciones múltiples: identificador1 = identificador2 = expresión Que es equivalente a identificador1 = (identificador2 = expresión) Ejemplo: i=j=5; Además hay otros operadores de asignación: +=, -=, *=, /= y %= La expresión de asignación: variable1 += variable2 Es equivalente a: variable1 = variable1 + variable2
8
2.2.5. El operador condicional. expresión1 ? expresión2 : expresión3 Si expresión1 es cierta (no nula) el operador devuelve el valor de expresión2, en otro caso, el valor de expresión3. Ejemplo: (i<0) ? 0 : 100 Jerarquía de prioridades de los operadores. Categoría de operador
Operadores
Asociatividad
Operadores monarios
- ++ - - ! sizeof(tipo)
Dcha a izda.
Multiplicación, división y * / %
Izda a dcha.
resto aritméticos Suma y resta aritméticas + -
Izda a dcha.
Operadores relacionales
< <= > >=
Izda a dcha.
Operadores de igualdad
== !=
Izda a dcha.
Y lógica
&&
Izda a dcha.
O lógica
||
Izda a dcha.
Operador condicional
?:
Dcha a izda.
Operad. de asignación
= += -= *= /= %=
Dcha a izda.
9
2.2.6. Funciones de biblioteca. Función
Tipo
abs(i)
int
Valor absoluto de i
ceil(d)
double
Redondear por exceso al entero más próximo
cos(d)
double
Coseno de d
cosh(d)
double
Coseno hiperbólico de d
exp(d)
double
Elevar e a la potencia d
fabs(d)
double
Valor absoluto de d
floor(d)
double
Redondear por defecto al entero más próximo
fmod(d1,d2)
double
Resto de d1/d2 (parte no entera del cociente) con el signo de d1
getchar()
int
Introducir un carácter del dispositivo de entrada estándar
log(d)
double
Logaritmo natural de d
pow(d1,d2)
double
d1 elevado a la potencia d2
printf(...)
int
Mandar datos al dispositivo de salida estándar
putchar(c)
int
Mandar un carácter al dispositivo de salida estándar
rand()
int
Devolver un entero positivo aleatorio
sin(d)
double
Devolver el seno de d
sqrt(d)
double
Devolver la raíz cuadrada de d
srand(u)
void
Inicializar el generador de números aleatorios
scanf(...)
int
Introducir datos del dispositivo de entrada estándar
tan(d)
double
Tangente de d
toascii(c)
int
Convertir el valor del argumento a ASCII
tolower(c)
int
Convertir una letra a minúscula
toupper(c)
int
Convertir una letra a mayúscula
10
2.3. Entrada y salida de datos. 2.3.1. Introducción. #include <stdio.h> archivo de cabecera para la entrada/salida estándar.
2.3.2. Entrada de un carácter. Función getchar. Variable_carácter=getchar() La función devuelve un carácter leído del dispositivo de entrada estándar (teclado). Si encuentra una condición de fin de archivo devuelve la constante EOF. Ejemplo: char c; c = getchar(); 2.3.3. Salida de un carácter. Función putchar. putchar(variable_caracter) La función transmite un carácter al dispositivo de salida estándar (pantalla). Ejemplo: #include <stdio.h> #include main() { char letras[80]; int cont, auxiliar; for (cont = 0; (letras[cont ] = getchar()) != ‘\n’; ++cont); auxiliar = cont; for (cont = 0; cont < auxiliar; ++cont) putchar(toupper(letras[cont])); }
11
2.3.4. Entrada de datos. Función scanf. scanf(cadena de control, arg1, arg2, ... argn) Para introducir valores numéricos, caracteres, cadenas de caracteres. Cadena de control tiene información sobre el formato de arg1, arg2, ... y está formada por grupos de caracteres que tienen el signo % y el carácter de conversión. Caracteres de conversión de datos de entrada. Carácter de conversión c
Significado
d
Entero decimal
e
Coma flotante
f
Coma flotante
g
Coma flotante
h
Entero corto
i
Entero decimal, octal o hexadecimal
o
Entero octal
s u
Cadena de caracteres seguida del carácter espaciado (se añade \0 al final) Entero decimal sin signo
x
Entero hexadecimal
[...]
Cadena de caracteres que puede incluir caracteres de espaciado
Carácter
Ejemplo: main() { char concepto[20]; int no_partida; float coste; ..... scanf(“%s %d %f”, concepto, &no_partida, &coste); ...... }
12
Ejemplo: #include <stdio.h> main() { char linea[80]; ..... scanf(“%[ ABCDEFGHIJKLMNOPQRSTUVXYZ]”, linea); ...... } Ejemplo: #include <stdio.h> main() { char linea[80]; ..... scanf(“%[^\n]”, linea); ...... } Es posible limitar el número de caracteres especificando una longitud de campo máxima para cada dato. Ejemplo: #include <stdio.h> main() { int a; float b; char c; ..... scanf(“%3d %5f %c”, &a, &b, &c); ...... } Si introducimos 10 256.875 T se asignarán los valores: a 10, b 256.8 y c 7
13
2.3.5. Salida de datos. Función printf. printf(cadena de control, arg1, arg2, .... argn) Transfiere los datos de los argumentos a la salida estándar con el formato que se indica en la cadena de control. Caracteres de conversión de datos de salida. Carácter
Significado
de conversión c
Carácter
d
Entero decimal con signo
e
Coma flotante con exponente
f
Coma flotante sin exponente
g
Coma flotante
i
Entero con signo
o
Entero octal sin el cero inicial
s
Cadena de caracteres
u
Entero decimal sin signo
x
Entero hexadecimal sin el prefijo 0x
Ejemplo: #include <stdio.h> main() { char concepto[20]; int no_partida; float coste; ..... printf(“%s %d %f”, concepto, no_partida, coste); ...... }
14
Ejemplo: #include <stdio.h> main() /*leer y escribir una línea de texto*/ { char linea[80]; scanf(“ %[^\n]”, linea); printf(“%s”, linea); } Se puede especificar también una longitud de campo mínima: Ejemplo: #include <stdio.h> main() { int i = 12345; float x = 345.678; printf(“%3d %5d %8d\n\n”, i, i, i); printf(“%3f %10f %13f\n\n”, x, x, x); printf(“%3e %13e %16e\n”, x, x, x); } Salida:
12345 12345
12345
345.678000 345.678000 3.456780e+02
345.678000
3.456780e+02
3.456780e+02
15
También es posible especificar la precisión: Ejemplo: #include <stdio.h> main() { float x = 123.456; printf(“%7f %7.3f %7.1f\n\n”, x, x, x); printf(“%12e %12.5e %12.3e”, x, x, x); } Salida:
123.456000 123.456
123.5
1.234560e+02 1.23456e+02
1.235e+02
Además podemos incluir dentro de cada cadena de control un indicador inmediatamente después del %. Indicadores de uso común. Indicador
Significado
-
ajuste a izda. del campo
+
cada dato numérico es precedido de un signo (+ ó -)
0
aparecen ceros en lugar de espacios en blanco
‘‘
cada dato numérico positivo es precedido de un espacio en blanco
#
con las conversiones tipo o y x, hace que los datos octales y hexadecimales sean precedidos por 0 y 0x
#
con las conversiones tipo e, f y g, hace que todos los números en coma flotante aparezcan con punto
16
2.3.6. Funciones gets y puts. Facilitan la transferencia de cadenas de caracteres entre la memoria y los dispositivos de entrada/salida estándares. gets(cadena) puts(cadena) Cada una de estas funciones acepta como argumento una cadena de caracteres que pueden incluir caracteres de espaciado. En el caso de gets, la cadena terminará con un caracter de nueva línea. Ejemplo: #include <stdio.h> main() { char linea[80]; gets(linea); puts(linea); }
17
2.4. Instrucciones de control. 2.4.1 Instrucción if-else. if (expresión) instrucción La instrucción se ejecutará sólo si la expresión tiene un valor no nulo (expresión cierta). La expresión puede ser simple o compuesta. Ejemplo: if ((balance < 1000.)|| (estado == ’R’)) printf(“%f”, balance);
if (expresión) instrucción1 else instrucción2 Si expresión tiene un valor no nulo (expresión cierta) se ejecutará instrucción1, en caso contrario (expresión falsa) se ejecutará instrucción2. Ejemplo: if (estado == ‘S’) tasa = 0.20 * pago; else tasa = 0.14 * pago; También se puede poner como: tasa = (estado == ‘S’) ? (0.20 * pago) : (0.14 * pago); Se pueden anidar estructuras if-else unas dentro de otras.
18
2.4.2. Instrucción while. while (expresion) instrucción la instrucción se ejecutará repetidamente mientras el valor de la expresión sea cierto (no nulo). Ejemplo: #include <stdio.h> main() { int digito = 0; while (digito <= 9) { printf(“%d\n”, digito); ++digito; } } 2.4.3. Instrucción do-while. do instrucción while (expresión) Se ejecuta la instrucción mientras que el valor de la expresión sea cierto (no nulo). Ejemplo: #include <stdio.h> main() { int digito = 0; do printf(“%d\n”, digito++); while (digito <= 9); }
19
2.4.4. Instrucción for. for (expresión1; expresión2; expresión3) instrucción - expresión1 se utiliza para inicializar parámetros, - expresión2 representa la condición que debe cumplirse (no nula) para que continúe la ejecución del bucle - expresión3 modifica el parámetro inicial asignado por expresión1. Pueden aparecer varias expresiones1 o expresiones3 siempre y cuando se separen por comas. El ciclo es equivalente a: expresión1; while (expresión2) { instrucción expresión3; } Ejemplo: #include <stdio.h> main() { int digito = 0; for (digito = 0; digito <= 9; ++digito) printf(“%d\n”, digito); }
20
2.4.5. Instrucción switch. switch (expresion) instrucción - Hace que se seleccione un grupo de instrucciones entre varios grupos disponibles. - Expresión devuelve un valor entero (o char). - Instrucción muestra las distintas opciones a través de etiquetas. En general, cada opción tendrá la siguiente sintaxis: case expresión1: case expresión2: ........... case expresión m: instrucción1 instrucción2 ....... instrucción n Ejemplo: switch (eleccion = getchar()) { case ‘r’: case ‘R’: printf(“ROJO”); break; case ‘b’: case ‘B’: printf(“BLANCO”); break; case ‘a’: case ‘A’: printf(“AZUL”); break; default: printf(“ERROR”); } La instrucción break hace que se transfiera el control fuera de la instrucción switch evitando que se ejecute más de un grupo de instrucciones. Si ninguna de las etiquetas coincide con la expresión se ejecuta la parte default.
21
2.4.6. Instrucción break. Se utiliza para terminar la ejecución de los bucles while, do-while, for, o para salir de la instrucción switch. Ejemplo: scanf(“%f”, &x); while (x <= 100) { if (x < 0) { printf(“ERROR”); break; } /*procesar el valor no negativo de x*/ }
2.4.7. Instrucción continue. Se utiliza para saltarse el resto de las instrucciones en la ejecución actual de un bucle. Ejemplo: for (cont = 1; cont <= n; ++cont) { printf(“x = “); scanf(“%f”, &x); if (x < 0) continue; suma += x; ++nmedia; }
22
2.4.8. Instrucción goto. goto etiqueta; donde etiqueta es un identificador que se utiliza para rotular la instrucción a la que se transferirá el control. La instrucción etiquetada tendrá el formato: etiqueta: instrucción Ejemplo: /*bucle principal*/ scanf(“%f”, &x); while (x<= 100) { ....... if (x < 0) goto error; ...... scanf (“%f”, &x); } /*rutina de detección de error*/ error: { printf(“ERROR – valor negativo de x”); ............. }
23
2.5. Funciones. 2.5.1. Introducción. -
Funciones de librerías de C
-
Funciones definidas por el programador
2.5.2. Definición de una función. Cabecera de la función: tipo nombre(tipo1 arg1, tipo2 arg2, .... tipon argn) Tipo es el tipo de dato que devuelve la función Los argumentos se denominan argumentos o parámetros formales. Cuerpo de la función: instrucción compuesta que debe incluir una o más instrucciones return expresión para devolver el control al punto de llamada. Ejemplo: char minus_mayus (char c1) { char c2; c2 = (c1 >= ’a’ && c1 <= ‘z’) ? (‘A’ + c1 – ‘a’) : c1; return (c2); }
24
2.5.3. Llamada a una función. nombre(arg1, arg2, ..., argn) Los argumentos serán ahora los parámetros reales de la función y pueden ser constantes, variables o expresiones. La función devuelve un valor del tipo que aparece en la definición. Ejemplo: #include <stdio.h> char minus_mayus (char c1) { char c2; c2 = (c1 >=’a’ && c1 <= ‘z’) ? (‘A’ + c1 – ‘a’) : c1; return (c2); } void main(void) { char minusc, mayusc; printf(“Introduce una letra minúscula”); scanf(“%c”, &minusc); mayusc = minus_mayus(minusc); printf(“\nLa mayúscula equivalente es: %c\n\n”, mayusc); }
25
2.5.4. Prototipos de funciones. tipo nombre(tipo1 arg1, tipo2 arg2, .... tipon argn); Se utilizan prototipos de funciones cuando la llamada a una función está antes que la definición de la función. Los argumentos son ficticios y pueden incluso no aparecer. Ejemplo: #include<stdio.h> long int factorial(int n) { int i; long int prod = 1; if (n > 1) for (i = 2; i <= n; ++i) prod *= i; return (prod); } main () { int n; printf(“\nn = “); scanf(“%d”, &n); printf(“\nn! = %ld”, factorial(n)); }
26
Ejemplo: #include <stdio.h> long int factorial(int n); main () { int n; printf(“\nn = “); scanf(“%d”, &n); printf(“\nn! = %ld”, factorial(n)); } long int factorial(int n) { int i; long int prod = 1; if (n > 1) for (i = 2; i <= n; ++i) prod *= i; return (prod); } 2.5.5. Paso de argumentos a una función. Paso por valor Cuando se pasa un parámetro real a una función se pasa una copia de la variable, por tanto la función no cambiará el valor de ese parámetro.
27
2.6. Estructura de un programa. Tipos de variables. 2.6.1. Tipos de variables. Según su tipo de almacenamiento: auto, extern, static, register Ejemplo: auto int a, b, c; extern float raiz1, raiz2; static int cont = 0; extern char asterisco; 2.6.2. Variables automáticas (auto). - Se declaran dentro de una función y son locales a la función. También los parámetros formales son variables automáticas. - Si no se especifica lo contrario, la variable será automática. - Se pueden asignar valores iniciales a las variables automáticas. - Las variables automáticas no mantienen el valor cuando se transfiere el control fuera de la función. 2.6.3. Variables externas (extern). - Su ámbito de validez se extiende desde su definición y hasta el final del programa manteniendo los valores que tomen en las distintas funciones. - Se utilizan para trasmitir valores entre funciones. - Se distingue entre: -
Definición de variable externa: Antes de las funciones en las que aparece. Reserva memoria. Se pueden asignar valores. No es necesario utilizar extern.
-
Declaración de variable externa: Cuando la función que utiliza la variable precede a la definición de la variable o está en otro archivo. Comienza con la palabra extern. No se reserva memoria. No puede haber asignación de valores.
28
2.6.4. Variables estáticas (static). Las variables estáticas mantienen sus valores a lo largo de la vida del programa. En un programa monoarchivo tienen el ámbito de validez de las variables automáticas. Comienza la designación de tipo con la palabra static. Si una variable local (o estática) de una función tiene el mismo nombre que una externa, dentro de la función prevalece la variable local (o estática). Ejemplo: float a, b, c; /*variables externas*/ void ficticio(void); main() { static float a; /*variable estática real a*/ ...... } void ficticio(void) { static int a; /*variable estática entera a*/ int b; }
29
2.6.5. Programas de varios archivos. La definición de una función dentro de un programa multiarchivo puede ser externa (reconocida en todo el programa) o estática (reconocida sólo en el archivo que se defina). La definición será la siguiente: tipo_almac. tipo_datos nombre (tipo1 arg1, tipo1 arg2....tipon argn) Cuando se define una función en un archivo y se accede desde otro, este segundo fichero debe tener una declaración de la función (el especificador extern es opcional). Ejemplo: Primer archivo #include <stdio.h> extern void salida(void); main() { salida(); } segundo archivo extern void salida(void) { printf(“HOLA”); return; }
También pueden existir variables externas asociadas a varios archivos. La definición y posible inicialización estará en un archivo y cada vez que se quiera utilizar se declarará en el archivo correspondiente.
30
2.7. Tablas. 2.7.1. Definición. Colección de datos del mismo tipo, con un nombre común y uno o varios índices (enteros no negativos) que recorren los datos. tipo_almac. tipo_datos nombre[expresion]; Ejemplos: int x[100]; char texto[80]; static char mensaje[25]; static float n[12]; Ejemplo: #include <stdio.h> #include #define TAMANO 80 main() { char letras[TAMANO]; int cont; for (cont = 0; cont < TAMANO; ++cont) letras[cont] = getchar(); for (cont = 0; cont < TAMANO; ++cont) putchar(toupper(letras[cont])); }
31
Las tablas automáticas, a diferencia de las variables automáticas, no pueden ir inicializadas. Para hacerlo, tendremos que definirlas como externas o estáticas. En ese caso, los valores iniciales irán ordenados, separados por comas y entre llaves.
Ejemplo: int digitos[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} static float x[6] = {0, 0.25, 0, -0.5, 0, 0} char color[4] = {‘R’, ‘O’, ‘J’, ‘O’}; x será estática y digitos y color serán externas debido a su colocación en el programa. 2.7.2. Operaciones con tablas. Todas las operaciones (asignación, comparación...) deberán realizarse posición a posición.
32
2.7.3. Tablas como parámetros de funciones. Una tabla se puede pasar como parámetro de una función. Estamos pasando la dirección del primer elemento de la tabla, por tanto, estamos pasando un parámetro por referencia. Ejemplo: float media(int a, float x[]); main() { int n; float med; float lista[100]; ....... med = media(n, lista); ....... } float media(int a, float x[]) { .............. }
33
2.7.4. Tablas multidimensionales. tipo_almac. tipo_dato nombre [expr.1][expr2]...[exprn] Ejemplo: float tabla[50][50]; char pagina[24][80]; static double registros[K][M][N]; int valores[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; int val[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
34
2.7.5. Tablas y cadenas de caracteres. Existen funciones para el tratamiento de cadenas de caracteres en string.h Ejemplo: Ordenar alfabéticamente una lista de cadenas de caracteres. #include <stdio.h> #include <stdlib.h> #include <string.h> void reordenar (int n, char x[][12]); main { int i, n=0; char x[10][12]; printf(“Introducir una cadena. Escribir \’FIN\’ para terminar \n”); do { printf(“cadena %d: “, n +1); scanf(“%s”, x[n]); } while (strcmp(x[n++], “FIN”)); n--; reordenar(n, x); printf(“\n\nLista reordenada de cadenas \n”); for (i= 0; i < n; ++i) printf(“\ncadena %d: %s”, i +1, x[i]); } void reordenar(int n, char x[][12]) {
char temp[12]; int i, elem; for (elem = 0; elem < n –1; ++elem) for (i = elem + 1; i < n; ++i) if (strcmp(x[elem], x[i]) > 0) { strcpy(temp, x[elem]); strcpy(x[elem], x[i]); strcpy(x[i], temp); } return; }
35
2.8. Punteros. 2.8.1. Conceptos básicos. &v dirección de v pv = &v *pv
asignamos a pv la dirección de v
contenido de la dirección de v (valor) Dirección de v
Valor
pv
v
2.8.2. Declaración de punteros. tipo_dato *ptvar; Ejemplo: #include <stdio.h> main() { int v = 3; int *pv; pv = &v; printf(“\n*pv=%d v=%d“, *pv, v); *pv = 0; printf(“\n\n*pv=%d v=%d”, *pv, v); } Salida:
*pv=3
v=3
*pv=0
v=0
36
2.8.3. Paso de punteros a una función. Permite el paso de parámetros por referencia. Ejemplo: #include <stdio.h> void func1(int u, int v); void func2(int *pu, int *pv); main() { int u = 1; int v = 3; printf(“\nAntes de la llamada a func1:
u=%d v=%d”, u, v);
func1(u, v); printf(“\nDespués de la llamada a func1: printf(“\nAntes de la llamada a func2:
u=%d v=%d”, u, v);
u=%d v=%d”, u, v);
func2(&u, &v); printf(“\nDespués de la llamada a func2:
u=%d v=%d”, u, v);
} void func1(int u, int v) { u = 0; v = 0; printf(“\nDentro de func1: u=%d v=%d”, u, v); return; } void func2(int *pu, int *pv) { *pu = 0; *pv = 0; printf(“\nDentro de func2: *pu=%d *pv=%d”, *pu, *pv); return; }
37
Salida: Antes de la llamada a func1: u=1 v=3 Dentro de func1: u=0 v=0 Después de la llamada a func1: u=1 v=3 Antes de la llamada a func2: u=1 v=3 Dentro de func1: *pu=0 *pv=0 Después de la llamada a func2: u=0 v=0
2.8.4. Punteros y tablas unidimensionales. El nombre de una tabla es un puntero al primer elemento de la tabla. int x[10]
x
i-ésimo elemento de la tabla:
&x[0] &x[i]
(x+i)
38
2.8.5. Asignación dinámica de memoria. Podemos definir una tabla como un puntero: int *x
en lugar de int x[10]
Si x es una variable puntero tendremos que asignarle memoria dinámicamente: x = (int *) malloc(10 * sizeof(int)); En este caso no se podrán asignar valores iniciales. Ejemplo: Ordenar crecientemente una lista de números. #include<stdio.h> #include<stdlib.h> void reordenar(int n, int *x); main() { int i, n, *x; printf(“ \n¿Cuántos valores serán introducidos? ”); scanf(“%d”, &n); printf(“\n”); x = (int *) malloc(n * sizeof(int)); for (i = 0; i
39
2.8.6 Punteros y tablas multidimensionales. tipo_dato tabla [expresión1][expresión2] - Podemos definir una tabla bidimensional como un puntero a tablas unidimensionales contiguas: tipo_dato (*ptvar)[expresión2] Ejemplo: La tabla int x[10][20] se puede definir como: int (*x) [20] Ahora x[2][5] se puede expresar como *(*(x + 2) + 5)
- Una tabla bidimensional se puede definir también como una tabla de punteros: tipo_dato *ptvar[expresión1] Ejemplo: int *x [10] Ahora x[2][5] se puede expresar como *(x[2] + 5).
- Además una tabla bidimensional se puede definir a través de un doble puntero:
int ** m
de forma que asignaremos dinámicamente espacio de la siguiente forma: m = (int **) malloc (NUMFILAS*sizeof(int *)); for (j=0; j
40
2.9. Estructuras y uniones. 2.9.1 Definición de una estructura. struct marca { miembro1; miembro2; ..... miembrom; }; los miembros pueden ser variables, punteros, tablas u otras estructuras. Ejemplo: struct cuenta { int no_cuenta; char tipo_cuenta; char nombre[80]; float saldo; }; struct cuenta antiguocliente, nuevocliente; Esto equivale a la siguiente definición: struct cuenta { int no_cuenta; char tipo_cuenta; char nombre[80]; float saldo; } antiguocliente, nuevocliente; También se les pueden asignar valores iniciales: static struct cuenta cliente = {12345, ‘R’, “José García”, 586.30}; 2.9.2 Procesamiento de una estructura. variable. miembro Ejemplo: cliente.no_cuenta
41
2.9.3 Tipos de datos definidos por el usuario. typedef tipo nuevo_tipo; Permite definir nuevos tipos de datos que sean equivalentes a los tipos de datos existentes. Ejemplo: typedef int edad; edad varon, hembra; Este esquema se utiliza habitualmente con estructuras para simplificar la notación: typedef struct { miembro1; miembro2; .... miembrom; } nuevo_tipo; Ejemplo: typedef struct { int no_cuenta; char tipo_cuenta; char nombre[80]; float saldo; } registro; registro anteriorcliente, nuevocliente;
42
2.9.4 Estructuras y punteros. Se pueden definir variables puntero que apunten a estructuras: Ejemplo: struct { int no_cuenta; char tipo_cuenta; char nombre[80]; float saldo; } cliente, *pc; pc = &cliente; Para acceder a un miembro en términos de su correspondiente variable puntero pc->no_cuenta que equivale en este caso a: cliente.no_cuenta
(*pc).no_cuenta
2.9.5 Paso de estructuras a una función. Se pueden pasar miembros individuales o estructuras completas. Una estructura completa se puede transferir pasando un puntero a la estructura como argumento (paso por referencia).
43
Ejemplo: #include <stdio.h> typedef struct { char *nombre; int no_cuenta; char tipo_cuenta; float saldo; } registro; void ajustar(registro *pt); main() { static registro cliente = {“Lázaro”, 3333, ‘A’, 33.33}; printf(“%s
%d
%c
%.2f\n”,
cliente.nombre,
cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo); ajustar(&cliente); printf(“%s %d %c %.2f\n”, cliente.nombre, cliente.no_cuenta, cliente.tipo_cuenta, cliente.saldo); } void ajustar(registro *pt) { pt->nombre = “José”; pt->no_cuenta = 9999; pt->tipo_cuenta = ‘R’; pt->saldo = 99.99; return; } Salida:
Lázaro 3333 A 33.33 José 9999 R 99.99
44
2.9.6 Estructuras autorreferenciadoras. struct marca { miembro1; miembro2; ..... struct marca *nombre; }; Ejemplo: struct lista_elementos { char elem[40]; struct lista_elementos *sig; };
45
2.9.7 Uniones. Las uniones se componen de miembros que comparten el mismo área de almacenamiento. union marca { miembro1; miembro2; ...... miembrom; }; Ejemplo: union id { char color[12]; int talla; } camisa, blusa;
46
2.10. Enumeraciones. enum marca {miembro1, miembro2, ... miembrom}; tipo_almacenamiento enum marca variable1, variable2, ...variablen; o lo que es lo mismo: tipo_almacenamiento enum marca {miembro1, miembro2, ... miembrom} variable1, variable2,....variablen; Ejemplo: enum colores {negro, azul, cian, verde, magenta, rojo, blanco, amarillo} color; Las constantes de enumeración representarán ahora los siguientes valores enteros: negro-0, azul-1, cian-2,.....,amarillo7 (Se pueden cambiar). Ejemplo: enum boolean {FALSE, TRUE}; FALSE-0 y TRUE-1.
47