• No se han encontrado resultados

Introducción al Lenguaje C

N/A
N/A
Protected

Academic year: 2021

Share "Introducción al Lenguaje C"

Copied!
51
0
0

Texto completo

(1)

Introducción al Lenguaje C

1. Una Visión General del Lenguaje C.

2. Variables, Constantes y Operadores.

3. Sentencias de Selección e Iteración.

4. Funciones.

5. Arrays y Punteros.

6. Cadenas de Caracteres.

(2)

Una Visión General del Lenguaje C

Fue creado por Dennis Ritchie y Ken Thompson en 1972 como herramienta para los programadores. Su principal objetivo consiste en ser un lenguaje útil.

Características

Lenguaje moderno (programación estructurada y diseño modular).

Lenguaje eficiente. Su diseño aprovecha las posibilidades de los ordenadores actuales.

Lenguaje portátil.

Lenguaje potente y flexible. Resolución de problemas físicos y de ingeniería. Posee control sobre aspectos asociados con los lenguajes ensambladores.

Lenguaje compilado. - Editor de texto. - Compilador. - Ejecución.

(3)

Un programa sencillo

#include <stdio.h>

main() { /* Un programa sencillo */

int num;

num = 1;

printf("\n¡Maravilla, colega!");

printf("\nMi número es el %d por ser el primero.", num); }

(4)

Variables, Constantes y Operadores.

Un programa necesita trabajar con datos.

Algunos están preseleccionados antes de la ejecución del programa y mantienen sus valores inalterados durante la misma. Se denominan

constantes.

Otros pueden recibir nuevas asignaciones de valor durante la ejecución del programa. Se trata de

variables.

#include <stdio.h> #define DOCE 12

int main() { /* pasa docenas a huevos */ int huevos;

int docenas = 4;

huevos = DOCE * docenas;

printf("\nHay %d huevos en %d

docenas.", huevos, docenas); return 0;

(5)

El tipo

int

.

Es el tipo de dato entero básico y normalmente usa una palabra del procesador para su almacenamiento.

• En los ordenadores personales ocupa dos bytes (16 bits) y permite un rango entre -32768 y +32767.

• Declaración de variables: Se emplea la palabra clave int seguida de los identificadores de las variables separados por comas. La sentencia acaba en punto y coma.

Las variables se pueden inicializar (asignarles un valor inicial) en la propia sentencia de declaración.

• Cualquier número entero, escrito sin punto decimal y sin exponente, es reconocido por el compilador como una constante entera.

• Especificadores de formato: %d (decimal), %o (octal) y %x (hexadecimal).

• Modificadores del tipo entero básico: unsigned, long y short.

(6)

#include <stdio.h> int main() {

int x = 100;

print("\nDec: %d; Oct: %o; Hex: %x", x, x, x);

return 0; }

• Declaración de constantes de tipo entero largo sin signo: Se usan los sufijos L o l y U o u respectivamente.

#define DOS_MIL_LARGO_SIN_SIGNO 2000UL

El tipo

char

.

• Se utiliza para almacenar caracteres, tal como letras y signos de puntuación, aunque desde un punto de vista técnico se trata de un entero de un byte (8 bits).

• Para manejar caracteres muchos ordenadores emplean el codigo ASCII, mediante el cual asocian números enteros a caracteres.

(7)

• Declaración de variables: Se emplea la palabra clave char seguida de los identificadores de las variables separados por comas. La sentencia acaba en punto y coma.

• En algunos desarrollos de C más recientes se permite el uso de las palabras claves unsigned y signed asociadas al tipo char.

• Cuando se encierra un único carácter entre comillas simples, el compilador lo identifica como una

constante de tipo carácter y almacena en el

ordenador en formato binario el entero correspondiente según el código de entrada/salida utilizado.

• Cuando se usa un código ASCII, se debe tener en cuenta la diferencia entre un número y un carácter numérico representativo de una cifra.

char num = 4;

char cifra = '4'; /* char cifra = 52; */

Existen ciertos caracteres no imprimibles, pues representan acciones a ejecutar por la máquina, tales como el retroceso, el salto de línea, el pitido del altavoz del ordenador, etc. Para representarlos podemos usar las secuencias de escape.

(8)

Carácter Acción \n nueva línea \t tabulador \b retroceso \r retorno de carro \f salto de página \\ barra atrás \' comilla simple \" comillas \a pitido

• Para imprimir un dato de tipo char como carácter debe utilizarse el especificador de formato %c.

#include <stdio.h> int main() { char ch; printf("Introduce un carácter.\n"); scanf("%c", &ch); printf("El código de %c es %d.", ch, ch); return 0; }

Los tipos

float

y

double

.

• Los programas que implican un mayor número de cálculos matemáticos y necesitan mayor precisión, a menudo utilizan números de punto flotante.

(9)

• Para almacenar un dato de tipo float, se usan 32 bits: 8 bits para expresar el valor del exponente y su signo, y 24 bits para representar mantisa. Este sistema permite una precisión de siete cifras decimales y un exponente entre 10-37 y 10+38.

• Muchos sistemas aceptan también el tipo double, que usa el doble de bits que el tipo anterior.

• Declaración de variables: Se emplea la palabra clave float o double, según corresponda, seguida de los identificadores de las variables separados por comas. La sentencia acaba en punto y coma.

• Para representar una constante en punto flotante se escribe una serie de cifras con signo incluyendo un punto decimal, a continuación la letra e o E seguida de un exponente con signo, que indica la potencia de diez a usar.

float num1 = -1.56E+12, num2 = 2.87e-3; double num3 = 35e2; /* 3500 */

• El compilador supone que todas las constantes de punto flotante que aparecen en el programa son de tipo double, a fin de asegurar la máxima precisión en los cálculos.

(10)

• Los especificadores de formato a usar son %f para notación decimal y %e para notación exponencial.

#include <stdio.h> int main() {

float valor = 32000.0;

printf("%f equivale a %e\n.", valor, valor);

return 0; }

Otros tipos.

• Tipo void (vacío) y tipo long double (alta precisión).

Operadores y precedencias.

Operador de asignación: =. El operando de la izquierda debe ser una variable y el de la derecha puede ser cualquier expresión.

Operadores aritméticos: +, -, *, /, %, (). El operador de división actúa de manera diferente en tipos flotantes y enteros. El operador menos unario se usa para cambiar el signo de un valor.

(11)

Operadores de actualización de variables: +=, -=, *=, /=, %=.

El operador incremento ++ aumenta el valor de su operando en una unidad y el decremento -- lo disminuye en una unidad.

Operadores relacionales. Se emplean para realizar

comparaciones, y son: <, <=, ==, !=, >, >=.

En C toda expresión siempre tiene un valor.

#include <stdio.h> int main() {

int cierto, falso;

cierto = (10 > 2); /* expresión cierta */ falso = (10 == 2); /* expresión falsa */ printf("cierto: %d, falso: %d.\n",

cierto, falso); return 0;

}

Operadores lógicos. Se utilizan para combinar dos

o más expresiones de relación y son los siguientes: && (AND), || (OR) y ! (NOT).

El operador sizeof() devuelve el tamaño en bytes del tipo o expresión que se le pase como argumento.

(12)

Sentencias de Selección e Iteración.

Expresiones y sentencias.

Expresión: Combinación de operandos mediante

operadores. La expresión más simple es un operando aislado (una constante o una variable).

La asignación también posee un valor, que es el que adquiere la variable a la izquierda del signo igual. Así se pueden realizar asignaciones múltiples.

Un programa es un conjunto de sentencias. En C

cada sentencia debe finalizar con un punto y coma.

Una sentencia compuesta (bloque) es un conjunto de sentencias encerrado entre llaves.

La sentencia

if

.

• Permite elegir la acción o acciones a realizar en función del valor de verdad de una expresión.

if (<expresión>) <sentencia>

else /* opcional */ <sentencia>

(13)

Ejemplo:

if (y > 0) x = y; else

x = -y;

Pueden escribirse sentencias anidadas, donde cada sentencia else es, a su vez, otra sentencia if-else.

• Si existen sentencias if-else anidadas, cada sentencia else corresponde siempre a la sentencia if más próxima.

El operador condicional.

Es una forma abreviada de expresar la sentencia if-else. Si la primera expresión es cierta la expresión condicional toma el valor de la segunda expresión, y si es falsa el de la tercera.

<expr_1>? <expr_2> : <expr_3>

(14)

Elección múltiple: La sentencia

switch

.

• Si un programa debe elegir una opción entre varias, resulta más legible si se usa una sentencia switch en vez de varias sentencias if-else anidadas.

switch (<expresión_entera>) { case const_1: sentencia/s

break; /* opcional */ case const_2: sentencia/s

break; /* opcional */ case const_n: sentencia/s

break; /* opcional */ default: sentencia/s /* opcional */ }

Ejemplo:

switch(nota) { /* la variable es de tipo int */ case 0: printf("\nNi idea."); break;

case 1: case 2:

case 3: printf("\nCasi ni idea."); break; case 4: printf("\nCasi apruebas."); break; case 5: printf("\nPor los pelos.");

printf("\nHay que estudiar más."); break;

case 6: printf("\nBien aprobado."); break; case 7:

case 8: printf("\nNotable."); break; case 9:

case 10: printf("\nSobresaliente."); break; default: printf("\nNota errónea.");

(15)

El bucle

while

.

Pertenece a la categoría de bucles condicionales. La sentencia (simple o compuesta) se ejecuta de cero a n veces según el valor de verdad de la expresión (condición de entrada).

while (<expresión>) <sentencia>

Ejemplo:

/* programa sencillo de encriptación */ #include <stdio.h>

#define K 10 /* clave de encriptación */ int main() {

char ch;

while((ch = getchar()) != EOF) if (ch == '\n') putchar(ch); /* '\n' no varía */ else putchar(ch + K); /* encripta */ return 0; }

• La función getchar() lee un carácter de la entrada estándar, y la función putchar() lo escribe en la salida estándar.

(16)

El bucle

do-while

.

• La sentencia (simple o compuesta) se ejecuta de una a n veces según el valor de verdad de la expresión (condición de salida), la cual se comprueba tras cada iteración.

do <sentencia>

while (<expresión>);

Ejemplo:

/* programa para adivinar un número */ #include <stdio.h>

#define NUM 1000 /* número a adivinar */

int main() { int elegido; do { printf("\nDame un número: "); scanf("%d", &elegido); } while(elegido != NUM); printf("\n¡¡¡Acertaste!!!"); return 0; }

(17)

El bucle

for

.

• Permite aumentar la legibilidad de los bucles en algunos casos.

for (<inic.>; <test>; <act.>) <sentencia>

• El formato anterior puede reescribirse del siguiente modo: <inicialización>; while (<test>) { <sentencia> <actualización>; } Ejemplos: for (n = 10; n > 0; n--) printf("\n%d.", n); for (ch = 'a'; ch <= 'z'; ch++) printf("\nCódigo de %c: %d.", ch, ch); n = 1; for (; n < 10; n++) printf("\n%d.", n); for (; ; ) printf("\nEternidad.");

(18)

• Ejercicio: Escribir un programa que sume los primeros m términos de la sucesión An = 1/2

n .

Las sentencias

break;

y

continue;

.

• La sentencia break; puede usarse con los tres bucles anteriores. Cuando el programa llega a esta sentencia dentro de un bucle, su flujo abandona éste último y pasa a ejecutar la siguiente sentencia del programa.

Ejemplo:

while ((ch = getchar()) != EOF) { if (ch == '\n') break;

putchar(ch); }

• La sentencia continue; dentro de un bucle evita el resto de la iteración y desvía el flujo del programa de nuevo hacia la expresión de test. Puede usarse en los tres bucles anteriores, pero no en una sentencia switch.

Ejemplo:

while ((ch = getchar()) != EOF) { if (ch == '\n') continue; putchar(ch);

(19)

Funciones.

Definición de función:

Unidad de código diseñada para llevar a cabo una tarea determinada.

* El tipo de una función coincide con el valor devuelto por ésta (el tipo por omisión es int).

* En C todas las funciones deben devolver un valor excepto aquéllas cuyo tipo es void.

* Una función puede considerarse como una caja

negra, definida exclusivamente por la información

suministrada (parámetros de entrada) y el producto recibido (resultado de salida).

Ejemplo: printf()

* Parámetros formales: variables que se ponen en la definición de la función, entre los paréntesis, separados por comas e indicando su tipo, como cualquier variable.

(20)

#include <stdio.h>

int max(int, int); /* cabecera o prototipo */

int main() {

int x = 4, y = 10; int z;

z = max(x, y); /* parámetros reales: x e y */ printf ("El máximo de %d y %d es %d. \n",

x, y, z) ; return 0;

}

int max(int a, int b) {

int x; /* variable local a max() */ x = (a > b)? a: b;

return x; }

Declaración de parámetros obsoleta:

max(a, b) /* el tipo por omisión es int */ int a, b; {

return ((a > b)? a: b); }

/* los paréntesis exteriores de la sentencia

return son opcionales */

(21)

* Paso de parámetros:

- Por valor: Los valores de los parámetros reales se copian en los parámetros formales.

- Por referencia o por variable: Los valores de los parámetros reales (cuando son variables) se modifican dentro de la función y permanecen alterados al abandonarla.

* Operador de dirección (&): Devuelve la dirección en la que se ha almacenado la variable afectada por dicho operador.

* Operador de indirección (*): Accede al valor que se encuentra en una dirección.

* Una variable de tipo puntero almacena direcciones de otras variables.

tipo *variable;

Ejemplo:

int main () {

int entero = 4; *puntent; puntent = &entero;

printf("\nEl entero apuntado por puntent es %d.", *puntent);

return 0; }

(22)

* Parámetros reales, actuales o efectivos: valores particulares que se asignan a los parámetros formales durante la llamada a una función concreta. - constante.

- variable.

- expresión (válida en C).

* Devolución de valores:

return[expresión];

El tipo de una función se antepone a su identificador.

Ejemplo:

int abs(int x) {

if (x < 0) return –x; else return x;

puts("Esta sentencia NUNCA se ejecuta."); }

(23)

#include <stdio.h>

void intercambia(int, int); /* versión 1 */ int main() { int x = 5, y = 10; printf("Antes x = %d e y = %d.\n", x, y); intercambia(x, y); printf("Después x = %d e y = %d.\n", x, y); return 0; }

void intercambia(int a, int b) { int temp = a;

a = b; b = temp; }

#include <stdio.h>

void intercambia(int *, int *); /* versión 2 */ int main() { int x = 5, y = 10; printf("Antes x = %d e y = %d.\n", x, y); intercambia(&x, &y); printf("Después x = %d e y = %d.\n", x, y); return 0; }

void intercambia(int *a, int *b) { int temp = *a;

*a = *b; *b = temp; }

(24)

* Tipo de una función: En C las funciones se suponen de tipo int por omisión. Si una función no es de tipo int, además de indicarse, debe realizarse una declaración de la misma por adelantado (forward). Los encabezamientos o prototipos de las funciones deben colocarse al principio del programa.

Ejemplo:

#include <stdio.h>

double max(int, int); /* declaración "forward" */ int main() { double x = 4, y = 10; double z; z = max(x, y); printf("El máximo de %d y %d es %f.", x, y, z); return 0; }

double max(double a, double b) { return (a > b)? a: b;

(25)

* El tipo void (vacío) se usa cuando una función carece de valor de retorno. En versiones más antiguas de C se omitía el tipo de la función, haciendo que ésta adquiriese el tipo int por defecto.

Ejemplo:

void mensaje(unsigned dato) {

printf("Dato: %u.\n\n", dato); puts("No hay valor de retorno.");

return; /* sentencia opcional */ }

* Compilación separada (bajo Ms-Dos ):

- Cada fichero fuente (*.C) se compila por separado (*.OBJ).

- Todos los ficheros resultantes del proceso de compilación (ficheros objeto) se enlazan en un único fichero ejecutable (*.EXE). Para ello debe utilizarse el editor de enlaces (en Ms-Dos es el programa LINK.EXE)

(26)

* Alcance de una variable:

- Local (auto): Su ámbito se reduce a la función donde ha sido declarada y fuera de ella la variable es desconocida. Pueden existir dos variables locales declaradas en dos funciones distintas pero con el mismo identificador .

- Global (extern): Se define fuera de cualquier función y es conocida por todas las funciones del programa. No se recomienda usar una variable global debido a los efectos laterales, excepto cuando ésta sea utilizada en muchas funciones de nuestro programa.

Ejemplo:

#include <stdio.h>

int a = 4; /* variable global */ int main() {

int a = 1; /* local a main() */ printf("El valor de a es %d.", a); return 0;

(27)

Recursividad

* Es una técnica muy potente en la que un módulo (función o procedimiento) está parcialmente definido en términos

de si mismo. Ejemplos: búsqueda de una palabra en un

diccionario, adivinación de un número (búsquedas dicotómicas).

* Principio de inducción:

- El teorema se cumple para el primer valor posible. - Si el teorema se cumple para N (hipótesis inductiva)

se cumple también para N + 1.

Ejemplo: Suma de los N primeros números naturales. S = (N * (N + 1)) / 2.

* Conceptos básicos:

- Caso base (uno o varios): No se realiza ninguna

llamada recursiva.

- Caso general o inductivo: Realiza llamadas a

versiones del propio módulo, con parámetros más simples que los utilizados en la versión actual.

a) Todas las llamadas recursivas deben llevar hacia el caso base.

b) Hay que incluir al menos un caso base. El módulo debe comprobar si debe hacerse una nueva llamada recursiva o si ya se ha alcanzado el caso base

(28)

Ejemplo:

/* módulo "driver" */

unsigned long factorial(unsigned n) { if (n >= 0) return fact(n);

else puts("Error: Número negativo."); }

unsigned long fact(unsigned n) { if (n == 0) return 1;

else return n * fact(n-1); }

* Ejercicio: Escribir un programa que calcule la suma de los n primeros números naturales de forma recursiva.

* Funcionamiento de la recursividad:

- Avance hasta alcanzar el caso base.

- Retroceso devolviendo el resultado de cada llamada

al módulo.

* Métodos de traza: representación de la pila de llamadas y árbol de activación.

Ejemplo:

unsigned long fibo(unsigned n) { if (n == 0 || n == 1) return n; return fibo(n - 1) + fibo(n - 2); }

(29)

* Diseño de un algoritmo recursivo:

- Identificar y resolver el caso base del problema. Debe devolver el resultado correcto para el valor más pequeño.

- Expresar el problema para un tamaño dado en función del mismo problema para un tamaño menor. * Recursividad indirecta:

- Un módulo llama a otro, éste a otro, etc., acabando en que el módulo original es llamado de nuevo.

- Admite distintos niveles de profundidad. - Más difícil de seguir y depurar.

* Recursividad frente a iteración:

- Semejanzas: repetición, prueba de terminación, aproximación a la terminación (iteración por contador), posible continuación indefinida (por errores de programación).

- Diferencias: Mayor coste (tiempo y memoria) de la recursividad frente a la iteración. Existen problemas con soluciones naturalmente recursivas, cortas y elegantes, más fáciles de entender y depurar.

- Conclusión: La decisión de usar la recursividad o la iteración depende de las características del problema concreto.

* Aplicaciones: divide y vencerás, backtracking (vuelta atrás y estructuras dinámicas de datos.

(30)

Arrays. Conceptos Básicos.

Definición de array:

* Estructura de Datos. * Serie de elementos. * Todos del mismo tipo.

Acceso a un elemento:

- Nombre del array.

- Posición del elemento dentro de éste (uso de

índices).

Tipos de arrays:

- Unidimensionales (vectores). - Bidimensionales (tablas). - Tridimensionales (cubos).

- Multidimensionales (en general).

Los arrays deben tener la longitud mínima necesaria.

(31)

Manipulación de Arrays en C.

En C el primer elemento tiene como índice el cero.

Declaración de un Array:

Tipo_Array Nombre_Array[Tam_l]...[Tam_N];

Ejemplos:

int indice[365]; /* vector de 365 enteros */

float matriz[l0][5]; /* matriz de l0x5 reales */

char cubo[70][7][5]; /* array de 70x7x5 caracteres */

/* matriz[0][0]: primera fila, primera columna.

matriz[9][4]: última fila, última columna. */

(32)

int temp[65]; /* array global de 65 enteros */

int main() {

float lluvia[200]; /* array local de 200 float */ extern temp[]; /* array externo: el

tamaño se conoce de la declaración anterior */ . . . return 0; } Inicialización de un Array:

Al igual que las variables, los arrays también se pueden inicializar al declararlos.

int vocales[] = {'A', 'E', 'I', 'O', 'U'}; /* el compilador cuenta el número de

elementos del array */

int nums[4] = {1, 2, 3, 4, 5}; /* esta declaración es errónea */

int cuad[2][3] = { {1, 2, 3}, {4, 5, 6} }; int cuad[2][3] = { 1, 2, 3, 4, 5, 6 };

(33)

Ejemplo: #include <stdio.h> int main() { int i, nums[5]; for (i = 0; i < 5; i++) { printf("Num: "); scanf("%d", &nums[i]); putchar('\n'); } putchar('\n'); for (i = 0; i < 5; i++) printf("Num: %d.", nums[i]); return 0; } Ejercicio: #include <stdio.h> int main() { int i, j, nums[5][3]; . . . return 0; }

(34)

Punteros.

- Proporcionan un método simbólico para utilizar

direcciones de memoria.

- Favorecen la construcción de programas potentes y

eficientes por la aproximación a la forma de trabajo del ordenador, cuyas instrucciones máquina emplean abundantemente las direcciones de memoria.

- En el lenguaje C el identificador de un array es la

dirección del primer elemento de dicho array.

Ejemplo:

int antifaz[4] = { 5, 6, 55, 44 }; /* antifaz == &antifaz[0]

"&" es el operador de dirección */

* Paso de arrays como parámetros.

- Cuando se pasa un array como argumento, se

pasa realmente su dirección, por lo que a partir de ella se puede leer o modificar cualquier elemento del array (los arrays siempre se pasan a través de sus direcciones).

(35)

Ejemplo:

/* Un array como argumento de la función

convierte() */

void convierte(int [], int); int main() {

int edad[50]; /* un array de 50 eltos. */ convierte(edad, 50);

/* pasamos la dirección del primer elto. */ return 0;

}

void convierte(int primavera[], int tam) { int i = 0;

for (; i < tam; i++) primavera[i] = 0;

/* con esta sentencia modificamos el array

edad declarado en la función llamadora */

}

/* el identificador del array es un puntero al primer elemento, por lo que también puede usarse la notación de punteros */ void convierte(int *primavera, int tam) {

int i = 0;

for (; i < tam; i++)

*(primavera + i) = 0; }

(36)

Cadenas de caracteres

Definición

Una cadena de caracteres es un array unidimensional de tipo char terminado con un carácter nulo ('\0').

* Cualquier cadena de caracteres entre comillas dobles es considerada por el compilador como una constante tipo array de caracteres. El compilador cuenta el número de caracteres para saber cuanta memoria va a necesitar para su almacenamiento. Ejemplo.

char m1[] = "Cosa";

char m1[] = { 'C', 'o', 's', 'a', '\0' }; /* ambas declaraciones son equivalentes */ /* m1 es un puntero constante al primer

elemento del array */

char *m2 = "Cosa"; /* m2 es una variable de tipo puntero */ char m2[10] = "Lindo pez";

char m3[50] = "Bocineta";

/* los caracteres restantes se ponen a cero */

(37)

* Arrays de cadenas de caracteres: Se utiliza un subíndice para acceder a las distintas cadenas del array.

char *cadenas[5] = { "Sumo con precisión", "Almaceno datos",

"Dibujo gráficos", "Ejecuto programas", "Domino el C" };

/* este array no es rectangular, ya que sus filas no son todas de la misma longitud */

char cadenas[5][19]; /* este array sí es rectangular */

* Entrada de cadenas de caracteres.

- Preparación de espacio para su almacenamiento (declaración).

- Empleo de una función de entrada para capturar la cadena. Se suelen utilizar las funciones scanf() y gets().

La función gets() captura una cadena introducida por el dispositivo de entrada estándar del sistema. Esta función lee caracteres hasta que encuentra el carácter '\n'. Entonces el ordenador toma todos los caracteres menos el carácter de nueva-línea, que se reemplaza por el carácter '\0'.

(38)

Ejemplo:

char nombre[20]; /* reservamos espacio para 20 caracteres */ gets(nombre);

La función gets() tiene dos retornos posibles:

- Si todo va bien devuelve la dirección de la cadena leída.

- Si hay algo equivocado o la función encuentra un carácter EOF, devuelve una dirección cero o NULL, definida en <stdio.h> como cero.

Ejemplo:

/* mientras la lectura sea correcta */ while (gets(nombre) != NULL) {

.

. /* se pueden crear este tipo de . sentencias */

}

La función scanf() permite leer una cadena de caracteres mediante el especificador %s. Esta función devuelve un valor entero igual al número de items leídos. Si hay un error o se encuentra un carácter EOF, scanf() devuelve éste último.

Ejemplo:

char nombre[15]; /* reservamos espacio */ scanf("%s", nombre);

(39)

* Salida de cadenas de caracteres.

Las dos funciones básicas de salida de cadenas son puts() y printf(), cuyos prototipos también están incluidos en <stdio.h>.

La función puts() sólo necesita un argumento, que es un puntero a una cadena de caracteres. Esta función sustituye el carácter nulo final por el carácter '\n' y lo envía a la salida estándar.

Ejemplo

char mensaje[] = "Hola mundo"; puts(mensaje);

La función printf() también toma un puntero a una cadena como argumento, pero no añade el carácter de nueva-línea al final de la cadena automáticamente. Esta función permite combinar varias cadenas en una sola línea de ejecución.

Ejemplo:

#include <stdio.h>

#define MSJ "Fantástico" int main() {

char *nombre = "Juan";

printf("Bien, %s, %s.\n", nombre, MSJ); return 0;

(40)

Funciones de cadenas de caracteres. * Prototipos en <string.h>.

* Longitud de una cadena.

int strlen(char *);

La función strlen() (string length) calcula la longitud de una cadena de caracteres, excluyendo el carácter de fin de cadena.

* Concatenación de cadenas.

char *strcat(char *, char *);

La función strcat() (string concatenation) toma dos cadenas como argumentos, añade una copia de la segunda cadena al final de la primera y hace que esta versión combinada sea la nueva cadena primera. La segunda cadena no se altera. strcat() devuelve la dirección de la primera cadena.

Esta función no comprueba si la primera cadena dispone de espacio suficiente para almacenar la concatenación de ambas cadenas.

(41)

Ejemplo: #include <stdio.h> #include <string.h> #define MAX_LONG 80 int main() { char flor[MAX];

char apendice[] = " es una flor bonita"; puts("¿Cuál es tu flor favorita?");

gets(flor); if ((strlen(apendice) + strlen(flor) + 1) <= MAX_LONG) { strcat(flor, apendice); puts(flor); } else

puts("No hay espacio suficiente."); return 0;

}

* Comparación de cadenas.

int strcmp(char *, char *);

La función strcmp() (string comparison) compara dos cadenas carácter a carácter. Si las dos cadenas son iguales la función devuelve cero, pero si son distintas strcmp() devuelve la diferencia entre los dos primeros caracteres que son distintos.

(42)

strcmp() compara cadenas almacenadas en arrays de diferente tamaño.

char *cad1 = "Melocomotón"; char *cad2 = "Melocomotón";

if (cad1 == cad2) /* ¡¡¡Error!!! */

... /* estamos comparando punteros */ if (strcmp(cad1, cad2) == 0) /* correcto */

... Ejemplo: strcmp("HOLA", "HOLB") /* -1 */ strcmp("HOLB", "HOLA") /* 1 */ strcmp("HOLA", "HOL") /* 65 */ strcmp("HOL", "HOLA") /* -65 */ * Copia de cadenas. strcpy(char, char);

La función strcpy() (string copy) copia los caracteres de la cadena que se pasa como segundo argumento en la cadena que se pasa como primer argumento.

char *pt1 = "Hola, campeón"; char *pt2;

pt2 = pt1; /* sólo copia la dirección de la cadena en pt2 */

(43)

En esta función no es incumbencia del ordenador la preparación de espacio para la copia en el array de destino; este detalle queda bajo la completa responsabilidad del programador.

Ejemplo:

#include <stdio.h> #include <string.h>

int main() {

char *pt1 = "Hola, campeón";

char pt2[20]; /* siempre hay que

reservar memoria */ strcpy(pt2, pt1); /* se copian los

caracteres de la cadena apuntada por

pt1 en pt2 */

printf("Cadena origen: %s.\n", pt1); printf("Cadena destino: %s.\n", pt2); return 0;

(44)

Ficheros.

Definición.

Un fichero puede definirse como un almacenamiento de datos, generalmente en disco, con un nombre.

Funciones básicas * Apertura de un fichero.

Antes de empezar a trabajar con un fichero es necesario abrirlo. La función fopen() utiliza dos parámetros.

FILE *fopen(char *, char *);

El primer parámetro es el nombre del fichero a abrir, en formato del S.O. El segundo parámetro es una cadena de caracteres que describe el uso al que se va a destinar el fichero:

"r" Lectura (read).

"w" Escritura (write). "a" Apéndice (append).

(45)

Algunos sistemas ofrecen posibilidades adicionales, como añadir la letra t si el fichero es de texto ("rt", "wt", "at") o la letra b si el fichero es binario ("rb", "wb", "ab").

Valor de retorno de fopen():

- Puntero a la estructura del fichero, que debe asignarse a una variable de este tipo para poder acceder al fichero a través de esa variable.

- NULL si se ha producido un error al intentar abrir el fichero. Ejemplo: #include <stdio.h> int main() { FILE *in; int ch; if ((in = fopen("TEST.TXT", "r")) != NULL) {

while ((ch = getc(in)) != EOF) putc(ch, stdout);

/* equivale a putchar(ch); */ fclose(in);

} else

puts("No se puede abrir \"test\"."); return 0;

(46)

* Cierre de un fichero.

La función fclose() acepta como argumento el

puntero al fichero. También realiza el vaciado del buffer, que podría haber quedado parcialmente

lleno en el momento de cerrar el fichero. int fclose(FILE *);

Valor de retorno de fclose():

- NULL si el cierre se ha realizado satisfactoriamente.

- EOF si se ha producido un error al intentar cerrar el fichero.

* Entrada/salida básica.

La función getc() captura un carácter de un fichero, cuyo puntero se especifica como parámetro de la función.

int getc(FILE *);

La función putc() envía el carácter especificado en el primer parámetro al fichero cuyo puntero se especifica en el segundo parámetro.

(47)

Entrada/salida con formato.

Las funciones fprintf() y fscanf() se comportan exactamente igual que printf() y scanf(), excepto que requieren un argumento adicional para apuntar al fichero correspondiente.

Ejemplo:

FILE *ent, *sal; int entero;

float real;

if ((ent = fopen("ENTRADA.TXT", "rt")) == NULL)

puts("Error al abrir ENTRADA.TXT."); else

if ((sal = fopen("SALIDA.TXT", "at")) == NULL) {

puts("Error al abrir SALIDA.TXT."); fclose(ent);

/* los ficheros siempre deben cerrarse */

} else {

fscanf(ent, "%d %f", &entero, &real); fprintf(sal, "Num1: %d, Num2: %f.\n",

entero, real); fclose(ent);

fclose(sal);

/* fcloseall() cierra todos los ficheros */

(48)

Entrada/salida de cadenas de caracteres. La función fgets() utiliza tres argumentos:

- Puntero al lugar de destino de la cadena a leer. - Longitud máxima de la cadena.

- Puntero al fichero.

char *fgets(char *, int, FILE *); Esta función se detiene cuando lee el carácter '\n' o ha leído el máximo de caracteres indicado en el segundo argumento menos uno. En cualesquiera de los casos, se añade un '\0' al final de la cadena. Además, fgets() mantiene el carácter de nueva-línea.

Al igual que gets(), fgets() devuelve NULL cuando encuentra un carácter EOF, lo cual permite comprobar si se ha alcanzado el final del fichero.

La función fputs() funciona de modo similar a puts(). Esta función no copia el carácter '\0' al final de la cadena y tampoco añade un carácter '\n' en el fichero de salida.

(49)

Acceso aleatorio.

* La función fseek() permite tratar los ficheros como arrays, moviéndose directamente a un byte determinado del fichero abierto previamente por fopen().

Argumentos de fseek(): - Puntero al fichero.

- Desplazamiento desde el punto de referencia. - Modo que indica el punto de referencia.

int fseek(FILE *, long, int);

Modo Número Origen del desplazamiento

SEEK_SET 0 Comienzo del fichero.

SEEK_CUR 1 Posición actual.

SEEK_END 2 Fin del fichero.

Valor de retorno de fseek(): - Si no ha habido error 0.

- En caso de error -1. Un error común consiste en intentar avanzar más allá de los límites del fichero.

(50)

Ejemplo:

#include <stdio.h> #include <stdlib.h>

int main() { /* imprime un fichero de texto al revés */

FILE *fp;

long despl = 0L; /* cero "largo" */ char ch;

if ((fp = fopen("texto.txt", "r")) == NULL) {

puts("El fichero no puede abrirse."); exit(1); /* se aborta el programa con

un código de error */ }

fseek(fp, --despl, SEEK_END);

/* sitúa el puntero justo antes del fin de fichero (EOF) */

while ((ch = getc(fp)) != EOF) { putchar(ch);

fseek(fp, --despl, SEEK_END);

/* mueve el puntero un byte hacia atrás */

}

fclose(fp);

/* los ficheros siempre deben cerrarse */ return 0;

(51)

Bibliografía

Aprendiendo C. Tercera Edición Revisada y Ampliada. J.M. Rodríguez, J. Galindo. Servicio de

Publicaciones de la UCA, 2006.

Ejercicios Resueltos de Programación C. P.J.

Sánchez, J. Galindo, I. Turias, I. Lloret. Servicio de Publicaciones de la UCA, 1997.

Turbo C/C++. Manual de Referencia. H. Schildt.

McGraw-Hill, 1993.

C. Guía de Autoenseñanza. H. Schildt.

McGraw-Hill, 1994.

El lenguaje de Programación C. Segunda Edición.

B.W. Kernighan, D.M. Ritchie. Prentice Hall, 1991.

Ejercicios de Fundamentos de Informática: Tests y Ejercicios Resueltos. J.M. Rodríguez, J. Galindo,

M.J. Ferreiro y otros. Servicio de Publicaciones de la UCA, 1997.

Referencias

Documento similar

You may wish to take a note of your Organisation ID, which, in addition to the organisation name, can be used to search for an organisation you will need to affiliate with when you

Where possible, the EU IG and more specifically the data fields and associated business rules present in Chapter 2 –Data elements for the electronic submission of information

The 'On-boarding of users to Substance, Product, Organisation and Referentials (SPOR) data services' document must be considered the reference guidance, as this document includes the

In medicinal products containing more than one manufactured item (e.g., contraceptive having different strengths and fixed dose combination as part of the same medicinal

Products Management Services (PMS) - Implementation of International Organization for Standardization (ISO) standards for the identification of medicinal products (IDMP) in

Products Management Services (PMS) - Implementation of International Organization for Standardization (ISO) standards for the identification of medicinal products (IDMP) in

This section provides guidance with examples on encoding medicinal product packaging information, together with the relationship between Pack Size, Package Item (container)

Package Item (Container) Type : Vial (100000073563) Quantity Operator: equal to (100000000049) Package Item (Container) Quantity : 1 Material : Glass type I (200000003204)