Fundamentos de C - Lección 3

Gramática del compilador

editar

Estas normas son de uso requerido por los compiladores

  • Las instrucciones se separan con el uso del punto y coma ;
  • Las llaves { } delimitan trozos de código
  • Los paréntesis ( ) identifican funciones y los parámetros de las mismas
  • Las comas , separan parámetros en las funciones

Estructura de una función

editar
  • Declaración de variables locales
  • Iniciamos las variables cuando vayan a ser usadas
  • Las funciones tendrán un punto de salida único
  • No habrá partes de código inaccesibles

Funciones básicas de entrada/salida - printf y scanf

editar

Estas dos funciones básicas de C nos ayudarán a introducir datos a nuestros programas y a mostrarlos por pantalla.

Todo el apartado de secuencias de escape y los modificadores serán de gran utilidad para el manejo de estas dos funciones.

Hay algunos caracteres que según el compilador que se use, no aparecerán bien, como son las tildes o la letra 'ñ'. Para incluirlas debemos usar una secuencia de escape para un carácter (%c) y una representación decimal del mismo, o la secuencia de escape que utiliza números octales e introduce su representación en carácter (\nnn)

Carácter Representación decimal Representación octal
á 160 240
Á 181 265
é 130 202
É 144 220
í 161 241
Í 214 326
ó 162 242
Ó 224 340
ú 163 243
Ú 233 351
ñ 164 244
Ñ 165 245

Nota: Estos son los caracteres más usados y para los que hay que utilizar notación especial, pero si se quiere averiguar el resto, es tan sencillo como compilar esto y ejecutarlo:

#include<stdio.h>
int main(void) { int i; printf("Caracter Decimal Octal\n");
    for(i=0;i<255;i++) printf("%c ------- %d -----%o\n",i,i,i);
    getchar();}


printf

editar

Uso de la función:

printf("Qu%c f%ccil es C",130,160);

Resultado en la terminal

Que fácil es C

El primer parámetro es siempre una secuencia de caracteres y tras él, según el número de secuencias de escape que usen % (excepto el propio carácter % que se pone %%), debe ir el mismo número de variables y en el orden en el que aparecen: si en el ejemplo, el 130 y el 160 estuviesen cambiados de sitio la salida sería así:

Quá fécil es C

Utilizando la secuencia de escape octal, produciría el mismo resultado

 
printf("Qu\202 f\240cil es C");
Que fácil es C

Esta función realiza el proceso contrario: lo que escribamos en pantalla lo almacena en una variable.

Se usa de la siguiente manera:

scanf("%d",&variableEntera);

Esta es la versión simplificada que es la que usaremos para la primera práctica. En lecciones sucesivas se propondrá un uso más amplio como rechazar caracteres concretos o leer varias variables a la vez.

Al igual que printf, consta de dos partes, una entre comillas, y otra después de la coma. Entre las comillas estableceremos el formato de lectura, es decir, el tipo de dato que vamos a introducir, en este caso un número entero, y después de la coma la variable destino.

Nótese el & a la izquierda del nombre de la variable. Este carácter especial nos indica que lo que estamos usando es un puntero, pero no necesitamos llenarnos la cabeza ahora con eso. Simplemente tener en cuenta que debe llevar & para que funcione.


Estructuras de datos

editar

Una estructura de datos es una agrupación de datos de tipos básicos para tratarlos como si de un solo dato se tratase.

ej de declaración de una estructura de fecha
struct fecha
{
    int ano;
    char mes[]; //cadena de caracteres, esto se explicará más adelante
    char dia; //los días entran de sobra en un char
}

Nótese que las variables son todas de distintos tipos, pero podrían ser perfectamente del mismo tipo

struct complejo
{
    float parteReal;
    float parteImaginaria;
}


Para declarar una variable del tipo "fecha" se realiza poniendo el nombre de la estructura:

struct fecha variable;

El acceso a los distintos campos (que es como llamaremos a cada variable interna de la estructura) se realiza mediante el uso de un punto '.'

variable.ano = 2008;
variable.mes = "Febrero"; //atención, esta asignación no puede hacerse, es sólo didáctica
variable.dia = 21;

Las estructuras pueden definirse de manera global o local, es decir, que pueden definirse para que todo el programa la use, o de manera local si sólo la usa una función en concreto. La definición de la estructura no es lo mismo que la declaración de las variables, es decir, al declarar la estructura como en los ejemplos, no estamos realizando ninguna reserva de memoria. Para reservar un espacio en memoria donde guardar los datos, tenemos que definir una variable del tipo de la estructura como el ejemplo de la fecha

Las estructuras serán muy útiles en capítulos sucesivos, como las listas, pilas o colas (?)

Palabra reservada typedef

editar

La palabra reservada typedef sirve para definir tipos de datos (type = tipo, def = definir).

En el apartado de las estructuras nos será útil para no tener que escribir struct nombre cada vez que declaremos una variable, y podamos poner sólo T_NOMBRE

typedef struct nombre
{
    ''tipo'' var;
    //aquí irían los campos necesarios
}T_NOMBRE;

A partir de este punto podremos definir una variables usando tanto struct nombre como T_NOMBRE.

Nota: el nombre puede ser omitido ya que usaremos T_NOMBRE pero para cuestiones de listas que veremos más adelantes, nos acostumbraremos a poner el nombre igualmente.

Nota: cuando usamos el typedef suele querer decir que es una estructura global, por lo que generalmente irá antes del main()

Entre ambos puede haber un número que indique a funciones como printf formas concretas de escribir los datos por pantalla.

El formato es el siguiente y puede tener todas las partes descritas o ninguna

%+06.02f (?)

Esto mostraría un número real como el 3.1 de esta manera

+03.10
  • El signo + hace que utilice uno de los dígitos para mostrar el signo, o en caso de usar cadenas de caracteres, los alinea a la derecha
  • El primer cero, indica que se rellenará por la izquierda con ceros hasta llegar al número de dígitos indicado por el entero antes del punto.
  • Este entero indica el número total de cifras que tendrá el número, incluidas las decimales y el propio punto decimal, y el signo, o en el caso de cadenas, el número total de caracteres. En cualquier caso, si el número o la cadena excede esos valores, los mostrará completos, excepto las cifras decimales que las redondeará.
  • El cero tras el punto hará que rellene con ceros hasta completar el número de decimales
  • La parte de después del punto decimal indica el número exacto de decimales que tendrá

Estructuras de Control

editar

Estas estructuras son la base de cualquier programa. Determinan qué debe de hacer el programa atendiendo a las circunstancias actuales.

Existen dos tipos

Estructuras condicionales

editar

Tienen una condición y según el resultado de la misma realizan unas acciones u otras.

Condicional simple

editar

Es la estructura conocida como if-then-else (si-entonces-sino) o simplemente if

if(a==3)
    printf("a vale 3");
else
    printf("a no vale 3");

Si tuviesemos más de una instrucción o quisiésemos darle más claridad a las instrucciones condicionadas pondríamos llaves

if( condiciones )
{
    acciones si se da la condición;
}
else
{
    acciones en caso contrario;
}

La parte del else es optativa, es decir, no siempre tiene que haber una parte del sino, lo que siempre, y reitero siempre, tiene que existir es la parte afirmativa. Si nuestra intención es mostrar algún tipo de información en caso de no cumplirse la condición, utilizaremos el operador lógico !

Condicional múltiple

editar

Es un caso especial de condicional simple ya que toda condicional múltiple se puede expresar como un conjunto de condicionales simples anidadas

Las palabras reservadas en C para la condicional múltiple son:

  • switch
  • case
  • default

Y necesita de una instrucción más, no perteneciente a la programación estructurada en sí, pero que en este caso es necesaria para esta estructura:

  • break
int a;
switch(a)
{
case 0:
    //operaciones a realizar en caso de que a valga 0;
    break;
case 1:
    //mismo proceso
    break;
...
case n:
    //n tiene que ser una constante, o numérica {0,1,2 ....} o una definida #define n 2
    break;
default:
    //en caso de que no se de ninguna de las anteriores hacer...
}

Y esto sería equivalente a:

int a;
if(a==0)
    //operaciones para 0
else if(a==1)
    //operaciones para 1
...
else if(a==n)
    //operaciones para n
else
    //cualquier otro caso

Si los anidásemos quedaría algo así:

if(a==0)
    //op para 0
else
{
    if(a==1)
        //opr para 1
    else
        if(...)
    ...
}

Aunque toda la estructura del if junto con el else correspondiente equivale a una sola instrucción, por lo que las llaves podrían ser omitidas en algunos casos, es aconsejable colocarlas por claridad.


  • Justificación del uso de break;

La estructura switch-case, una vez encontradao el caso en el que se encuentra, ejecuta todas las instrucciones que se encuentran de ahí para adelante, es decir, si no insertásemos el break, el resultado del case 0: sería el resultado de todos los cases seguidos. Esto se puede aprovechar:

int nota;
switch(nota)
{
case 1:
case 2:
case 3: //nótese que pueden estar desordenados
case 0:
case 4:
    printf("Suspenso");
    break;
case 5:
case 6:
    printf("Aprobado");
    break;
case 9:
case 10:
    printf("Sobresaliente");
    break;
case 7:
case 8:
    printf("Notable");
}

Estructuras cíclicas

editar

Las estructuras cíclicas nos permiten repetir una serie de instrucciones hasta que se cumpla una condición.

Existen tres tipos de estructuras:

  • while
  • do while
  • for

Estas estructuras nos permiten repetir unas instrucciones concretas un número indeterminado (o no) de veces, hasta que se cumpla una condición.

Declaración y uso

int i = 0;
while(i<3)
{
    printf("i vale %d\n",i);
    i++;
}
int i =0;
do
{
    printf("i vale %d\n",i);
    i++;
}while(i<3);
int i;
for(i=0;i<3;i++)
{
    printf("i vale %d\n",i);
}

Estos tres ejemplos producen la misma salida por terminal, pero su uso general es muy distinto.

i vale 0
i vale 1
i vale 2

Explicación

La estructura while (mientras) repite la serie de instrucciones mientras se de la condición entre paréntesis, que sigue las mismas normas que las condiciones de la estructura if.

while ( condiciones ) { instrucciones; }

La estructura do{}while (hacer mientras) es una variación del estándar do{}until que es el que suelen tener los lenguajes de programación, pero su funcionamiento es prácticamente igual, y muy parecido a while. Esta estructura repite una serie de instrucciones hasta que deje de cumplirse una condición. Esta estructura siempre se ejecuta al menos una vez, aunque la condición de salida se de antes de entrar.

do{ instrucciones; } while ( condiciones ) ;
int i=10;
do{
    printf("i vale %d",i);
}while(i<0);

Esto mostraría i vale 10 a pesar de que la condición i<0 no se cumpliese al principio.

Es de obligado cumplimiento que la condición varíe a lo largo de la secuencia de instrucciones en algún momento, porque si no, el programa podría quedarse en un bucle infinito y nunca terminaría de ejecutarse.

Esto no debe hacerse nunca!!

int i = 0;
while(i==0)
    printf("Esto no va a parar nunca.\n");

do{
    printf("Si esto está a continuación, nunca llegará a ejecutarse");
}while(i==0);

El for tiene un uso muy distinto a las otras dos estructuras. Un for siempre puede transformarse en un while, pero el recíproco no siempre es cierto.

for ( vc = vi; cond; incremento ){ instrucciones; }

Donde

  • vc es variable de control, es una variable que se usará como índice para las reiteraciones
  • vi es el valor inicial
  • cond es la condición de salida. Debe ser una comparación aritmética de las siguiente: <, >, <=, >=. Generalmente se usará < ó >, salvo que la situación requiera de las otras dos. En caso de querer introducir una condición distinta (ya que también acepta cualquier expresión lógica) nos decantaremos siempre por el uso de la estructura while
  • incremento es la cantidad que le sumamos o quitamos a la variable de control. Lo más común es '++' pero se puede usar cualquiera de las de suma (++, --, +=, -=). Si quisiésemos usar alguna otra, también nos decantaremos por el uso de un while

El for se usa cuando sabemos el número exacto de repeticiones que queremos hacer, ya sea entre dos números (1), un número y una variable (2)(3), o entre dos variables (4)

(1) for(i=0;i<10;i++){ printf("esto se repite 10 veces"); }
(2) for(i=0;i<x;i++){  printf("esto se repite hasta que i sea igual a x. Nótese que puede no repetirse ninguna vez."); }
(3) for(i=x;i<10;i++){ printf("esto se repetirá en función de x, igual que el caso anterior");}
(4) for(i=x;i<y;i++){  printf("esto se repetirá x-y veces");}

En un for, al contrario que en las otras dos estructuras, está totalmente desaconsejado modificar el valor de la variables de control (en este caso i) en el bucle, para eso está el incremento.

Transformación de for a while Es tan sencillo como pasar de

for( vc = vi; vc < lim; vc += incremento) { ... }

a

vc = vi;
while(vc < lim)
{
    ...
    vc += incremento;
}

En esta lección empezaremos a trabajar con funciones y con punteros.

Funciones y procedimientos

editar

Las funciones son secciones o trozos de código agrupados bajo una misma referencia, dicha referencia es el nombre de la función.

Existen diversas razones para crear funciones:

  • Reutilizar código
  • Reducir el tamaño de programas que repiten una misma secuencia
  • Leer mejor el código y poder seguir su estructura en caso de posterior modificación o corrección de errores
  • ...

Nombres válidos

editar

El siguiente texto se aplica tanto a funciones como a procedimientos y nombres de variables en general, cuando mencionamos aquí la palabra "nombre" nos referimos a estos casos en general y sólo a estos puesto que una variable de tipo char*, verbigracia, puede [u]contener[/u] los caracteres ASCII sin problema alguno:

No debe usarse el signo de espacio puesto que el compilador no lo entiende los espacios como partículas significativas dentro de un nombre. No obstante, es válido usar el signo _ para reemplazarlos en un nombre de función e inclusive usar números para distinguir dos funciones o variables con el mismo nombre (lista2, lista3)

Así muchos caracteres que están reservados y significan cosas distintas de lo que el programador novato en C podría esperar. Su uso indebido produce errores de compilador. Ejemplo de ello son los signos #,%,&,$,!,-,+,^, |, entre otros.


Ejemplo de nombres válidos:

  • imprimirdatos3
  • imprimir_datos3
  • imprimir_datos_3


Ejemplo de nombres inválidos:

  • 3imprimirdatos
  • 3_imprimirdatos
  • _imprimir_datos3
  • 3_imprimir_datos
  • %&imprimir_datos
  • imprimir datos
  • imprimir/datos $ 3


Puede usarse letras en mayúscula como es el caso de la "Notación Yugoeslava" pero no es recomendable o, dicho de otra manera, debe ser evitado al máximo.


Nota: La mejor forma para respetar las reglas al nombrar funciones, procedimientos y espacios de datos en general es no emplear caracteres extraños o exóticos y comenzar siempre los nombres con caracteres alfabéticos, es decir, usando letras del abecedario. Irrespetar estas reglas produce errores de compilador a veces bastante difíciles de rastrear, dependiendo de la complejidad de los programas.


Proyecto: Programación en C
Anterior: Fundamentos de C - Lección 2 — Fundamentos de C - Lección 3 — Siguiente: Fundamentos de C - Lección 4