Está en la página 1de 64

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.

7. Ficheros.
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.
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);
}
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;
}
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 cuatro bytes (32
bits) y permite un rango entre -231 y +231-1.

• 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).


#include <stdio.h>

int main() {

int x = 100;

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

return 0;
}

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

short [int] ≤ int ≤ long [int]

• 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.

• 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 usado.
• 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.

Carácter Acción Carácter Acción


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

• 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.

• 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.
• 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).


Conversiones de tipos y operador de moldeado.
• Cuando en una expresión C se combinan operandos de distinto tipo el compilador sigue
unas reglas para efectuar automáticamente conversiones de tipo:

• En cualquier operación en que aparezcan dos tipos diferentes se eleva el rango del que lo
tiene menor para igualarlo al del mayor (promoción).

• El rango o categoría de los tipos de mayor a menor es: double, float, long, int,
short y char. Los tipos unsigned tienen el mismo rango que el tipo al que están
referidos.

• En una sentencia de asignación, el resultado final de los cálculos (expresión) se convierte


al tipo de la variable al que está siendo asignado. Puede tratarse de una promoción o de una
pérdida de rango según la categoría de la variable a la que se efectúa la asignación.

• Mientras que la promoción pasa inadvertida fácilmente, la pérdida de rango puede originar
serios inconvenientes debido a que el tipo de menor rango puede no ser lo bastante amplio
como para albergar el valor de la expresión, cuyo tipo es de mayor rango.
• Para conservar la precisión numérica, en el lenguaje C todas las variables y constantes de
tipo float se convierten en double cuando se efectúan cálculos. El resultado final se
reconvierte en float si ése es el tipo declarado.

• Las conversiones de tipo tratadas anteriormente son automáticas. El moldeado (casting)


permite especificar la conversión de tipo deseada, y se realiza anteponiendo a la expresión
a moldear el nombre del tipo requerido entre paréntesis.

#include <stdio.h>

int main() {

int x;
float y = 1.0, z = 2.0;

x = (int) y + (int) z;
printf("\n%d.", x); /* Resultado: número entero 3 */
return 0;
}
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.

• 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.
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.

<var_1> = <var_2> = ... = <var_n> = <expresión>

• 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>

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.

if (...)
if (...)
<sentencia_1>
else
<sentencia_2>

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>

Ejemplo: x = (y > 0)? y: -y;


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_3: 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.");
}
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>

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 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.
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;
}
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>;
}

• Cualquier sentencia while puede reescribirse como una sentencia for:

for (; <expresión>; )
<sentencia>
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);

for (cont = 10; cont <= 100; cont += 10)


printf("\n%d.", n);

n = 1;
for (; n < 10; n++)
printf("\n%d.", n);

for (; ; ) printf("\nEternidad.");

• Ejercicio: Escribir un programa que sume los primeros m términos de la sucesión


An = 1/2n.
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);
}
Funciones.
* 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.
* Parámetros reales 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.");


}
#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 externos de la sentencia return son opcionales */


/* observar el ahorro de la variable x */

* 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 num = 4, *puntnum;

puntnum = &num;
printf("\nEl entero apuntado por puntnum es %d.", *puntnum);

*puntnum = 10;
printf("\nEl entero num es ahora %d.", num);

return 0;
}
#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;
}
* 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(double, double); /* declaración "forward" */

int main() {

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

z = max(x, y);
printf("El máximo de %lf y %lf es %lf.", x, y, z);

return 0;
}

double max(double a, double b) {


return (a > b)? a: b;
}
* 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).
* 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;
}
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
* La recursividad infinita es un error de programación.

* Funcionamiento de la recursividad:

- Avance hasta alcanzar el caso base.


- Retroceso devolviendo el resultado de cada llamada al módulo.

Ejemplo:

unsigned long factorial(unsigned n) { /* módulo "driver" */


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 foma recursiva.

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


* 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 y resulta
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.


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.


¡Nunca deben declararse arrays demasiado grandes!
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. */
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 };
/* ambas declaraciones son equivalentes */
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: Modificar un programa para que procese una tabla de 5x3 enteros.
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).
Ejemplo:
/* Un array como argumento de la función convierte() */
void convierte(int [], int);

int main() {

int edad[5] = {1, 2, 3, 4, 5}, i = 0;

convierte(edad, 5); /* pasamos la dirección del primer elemento */


for (; i < 5; i++) printf("%d ", tam[i]);
return 0;
}

void convierte(int primavera[], int tam) {


int i;
for (i = 0; i < tam; i++) primavera[i] = tam - i;
/* se modifica el array edad declarado en la función llamadora */
}

/* uso de la notación de punteros */


void convierte(int *primavera, int tam) {
int i;
for (i = 0; i < tam; i++) *(primavera + i) = tam - i;
}
Cadenas de caracteres
* 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 */
* 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" };

/* Las filas de este array 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'.
Ejemplo:

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


gets(nombre);

- 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:

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


... /* 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);
* 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() acepta como único argumento un puntero a una cadena de caracteres.
Esta función sustituye el carácter nulo final por '\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.

Ejemplo:
#include <stdio.h>
#define MSJ "Fantástico"

int main() {
char *nombre = "Juan";
printf("Bien, %s, %s.\n", nombre, MSJ);
return 0;
}
Funciones de cadenas de caracteres.

* Prototipos en <string.h>.

* Longitud de una cadena: La función strlen() (string length) calcula la


longitud de una cadena de caracteres, excluyendo el carácter de fin de cadena.

int strlen(char *);

* Concatenación de cadenas: 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() Esta función 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.

char *strcat(char *, char *);


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.

Esta función 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 */


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;
}
Ficheros.
* Un fichero puede definirse como un almacenamiento de datos, generalmente en
disco, con un nombre.

* 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).
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").

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;
}
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.

* 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.

int putc(int, FILE *);

* 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 */
}
* 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 cualquier caso, 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() actúa de modo similar a puts(), pero no copia el carácter


'\0' al final de la cadena ni añade un carácter '\n' en el fichero de salida.

int fputs(char *, FILE *);


* Acceso aleatorio.

La función fseek() permite tratar los ficheros como arrays y acceder


directamente a un byte concreto 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.

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); /* 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;
}
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.

También podría gustarte