Sei sulla pagina 1di 9

Programación en C

Un programa en C se define a través de una función especial denominada main. La definición


de main constituye el cuerpo del programa. Debajo se muestran dos formas de definir la
función main. Ambos ejemplos constituyen los programas más simples que se pueden escribir
en C.

Ejemplo 1

1: void main()
2: {
3: // Aquí escribimos nuestro programa!!
4: }

Ejemplo 2

1: int main()
2: {
3: // Aquí también podemos escribir nuestro programa!!
4: return 0;
5: }

El ejemplo 1 declara y define la función main vacía (no hace nada). En el segundo ejemplo,
main incluye solo una línea (4) return 0; que indica terminar la función devolviendo el valor
0.

Sintaxis

Tipos básicos

char (8 bits, 1 byte)


int (16 bits, 2 bytes)
float (32 bits, 4 bytes)

Identificador: Cualquier nombre que comience con un caracter alfanumérico y no contenga


caracteres especiales.

Ejemplos:

1: char c;
2: int x;
En el ejemplo se declaran dos variables, la variable c de tipo char (puede almacenar 1 byte) y
la variable x, de tipo entero o int (puede almacenar un entero de 16 bits en el rango de -215
hasta 215-1).
El tipo float depende de la arquitectura del sistema. En un 8086/88 en modo mínimo no
existe coprocesador matemático, luego, se debe evitar utilizar el tipo float. No obstante, las
operaciones con flotantes siempre se pueden definir por software (no sin esfuerzo), lo que
penalizara al sistema en términos de rendimiento.

Calificadores
unsigned : Positivo
signed : Con signo (por defecto, se usa poco)
short : El menor tamaño (posible) asignado al tipo de dato
long : El mayor tamaño (posible) asignado al tipo de dato
const : Nunca cambia
volatile : Nunca se sabe cuándo va a cambiar

Los calificadores modifican el significado del tipo de diversas formas. Ejemplo

1: unsigned int ux;

Declara la variable ux de tipo sin signo (positivo), por lo que el rango de valores cambia a 0
hasta 216-1.

En los calificadores long y short sus significados dependen del compilador y son aplicables a
enteros y flotantes (sólo long para este último caso). En el caso del compilador Digital Mars
(DMC) para 8086/88, el calificador short se ignora, mientras que long es aplicable a int y
extiende a 32 bits o doble palabra. Ejemplos

1: unsigned long d;
2: long int c;

En el primer caso se han combinado dos calificadores (unsigned y long), obsérvese además
que se ha omitido la palabra int que se asume por defecto. El rango de la variable d
corresponde a los enteros entre 0 y 2 32-1. El segundo ejemplo declara la variable c, también de
32 bits en el rango de -231 hasta 231-1.

Tipos de almacenamiento

auto : Variables automáticas (por defecto, no se usa)


static : Persistentes en todo el ámbito de la función o archivo en la que se declaran
extern : Persistentes en todo el ámbito del programa en el que se declaran
register : Indicación para colocar en un registro (no obligatoria)

Operadores

Aritméticos

+ Suma y positivo
- Resta y negativo
* Multiplicación
/ División
% Modulo (Resto de la división entera)

En DMC para 8086/88 de los operadores aritméticos, sólo suma y positivo junto con resta y
negativo se definen para todos los tipos enteros de 8 (char), 16 y 32 bits. La multiplicación,
división y módulo son aplicables en las siguientes condiciones.

Multiplicación

Ambos operandos pueden ser 16 bits, pero deben ser tales que su multiplicación no cause
desbordamiento para 16 bits.

División

No puede haber desbordamiento de ningún tipo y sólo se aceptan operandos de 8 ó 16 bits.

Módulo

Lo mismo que para la división.

Operadores lógicos a nivel de bits

| Suma lógica (OR)


& Multiplicación lógica (AND)
~ Negación lógica (NOT)
^ OR lógico exclusivo (XOR)

Relacionales

== Igualdad
!= Desigualdad
< Menor que
> Mayor que
<= Menor igual
>= Mayor igual

|| Disyunción (OR)
&& Conjunción (AND)

Operador ternario

?:

Devuelve una expresión

1: a = cond1 ? expresion_1 : expresion_2;

Si cond1 es verdadera (distinta de cero), entonces a = expresion_1, en otro caso a =


expresion_2.

Estructuras codicionales

if... else if... else

1: if(expresion_1)
2: {
3:
4: }
5: else if (expresion_2)
6: {
7:
8: }
9: else
10: {
11:
12: }

expresion_1 y expresion_2 son expresiones u operaciones validas en el lenguaje y que


producen un resultado. Si este último es igual a cero, la expresión se evalúa como falsa, en
caso contrario se considerara verdadera. Por ejemplo los bloques de código siguientes son
equivalentes:

1: if(Nota < 3)
2: DESAPROBADO = 1;
3: else
4: DESAPROBADO = 0;

1: DESAPROBADO = (Nota < 3) ? 1 : 0;

Estructura de decisión múltiple switch.. case

1: switch(expresión)
2:{
3: case 0:
4: // Sentencias para el caso 0
5: ......
6: break;
7:
8: case 1:
9: // Sentencias para el caso 1
10: ......
11: break;
12:
13: default:
14: // Sentencias para el caso por defecto
15: ......
16: break;
17: }

Estructuras repetitivas o bucles

1: for(inicio; cond; incremento)


2: {
3: // Sentencias a repetir
4: }

En el for todas las partes son opcionales, no obstante se deben mantener los (;). Un for(;;)
indica un ciclo infinito o en otras palabras la condición es evaluada siempre como verdadera.

1: do
2: {
3: // Sentencias a repetir
4: }
5: while(cond)

do..while ejecuta primero el contenido del bucle (código entre llaves) y a continuación evalúa
cond, esto garantiza que el contenido del bucle se ejecute al menos una vez
independientemente a si la condición es falsa desde el inicio.

1: while(cond)
2: {
3: // Sentencias a repetir
4: }

Arreglos y punteros

Un arreglo es una variable que almacena la dirección de una zona de memoria de un tamaño
específico, con la particularidad que dicha zona corresponde a un mismo tipo de dato. Los
arreglos comienzan en 0 y terminan en N-1 donde N es su tamaño.

1: int a[10]; // Un arreglo de 10 enteros de 16 bits con signo


2: char b[20]; // Un arreglo de 20 bytes

Se puede obtener un elemento del arreglo indexando en este

1: A1 = a[1]; // Asigna a la variable A1 el valor del segundo elemento del


arreglo a
2: B15 = b[15]; // Asigna a la variable B15 el valor del byte 16 del arreglo b

El nombre del arreglo corresponde a la dirección inicial de este, o lo que es lo mismo a la


dirección del primer elemento.

Un puntero es una variable que almacena la dirección de otra. Un puntero tiene 3 momentos
fundamentales, (1) declaración, (2) inicialización y (3) uso. Los punteros se declaran
empleando el operador de referencia (*)

1: char *p; // Declara un puntero p de tipo char (puede apuntar a variables de este
tipo)
2: int *v; // Declara un puntero v de tipo int

Las dos primeras etapas se pueden realizar en una misma línea empleando el operador
dirección de (address of, &)

1: char *p = &var; // Declara e inicializa un puntero llamado p que apunta a la


direccion de la variable var que debe ser igualmente un char.

Es así que un puntero es una forma de acceder indirectamente a una variable, se puede
incluso modificar su valor. Por ejemplo, empleando la declaración e inicialización de arriba,

1: *p = 'A'; // Esto es equivalente a decir, var = 'A';

Siempre que se refiera a la dirección se emplea el nombre del puntero, cuando se refiera al
valor al cual apunta se emplea * y el nombre del puntero a continuación. Ejemplos

1: *ptr = 5; // Asigna el valor 5 a la variable a la cual apunta el puntero ptr


2: ptr = 5; // Asigna a ptr el valor 5, o lo que es lo mismo, el puntero apunta a la
dirección con desplazamiento igual a 5

Funciones

Las funciones son la piedra angular de la programación estructurada. Una función es una
fragmento de código reutilizable que puede ejecutarse una o varias veces dentro de un
programa. Estructuralmente las funciones tienen un nombre, un tipo, una lista de argumentos y
un cuerpo. Ejemplo

1: <tipo> nombre(lista de argumentos)


2: {
3: // Cuerpo
4: }

El nombre puede ser cualquier identificador válido, el tipo corresponde al tipo de dato (char, int,
etc.) que devuelve la función. Si no devuelve nada se usa la palabra reservada void. La lista de
argumentos corresponde a los datos de entrada (y sus respectivos tipos) de la función. El
cuerpo es el conjunto de sentencias que ejecuta la función. El cuerpo siempre va entre llaves.
Ejemplos

1: /****************************************************************
2: Función vacía, no lleva argumentos, no devuelve valor
3: ****************************************************************/
4: void foo()
5: {
6: }

1: /****************************************************************
2: Función suma, devuelve la suma de sus argumentos a y b
3: ****************************************************************/
4: int suma(int a, int b)
5: {
6: return (a+b);
7: }

Las funciones tienen 3 momentos fundamentales. La declaración, la definición y la utilización o


llamada. La declaración informa del nombre de la función así como el tipo del valor de retorno y
el orden y tipos de los argumentos o valores de entrada. En la declaración no es obligatorio el
nombre de los argumentos de entrada. Una declaración válida de la función suma anterior
puede ser:

1: int suma(int, int);

Obsérvese el (;) al final de la declaración. Si sólo se declara la función es obligatorio. Otro


momento importante es la definición. Aquí describimos qué hace la función. Los dos ejemplos
de arriba son las respectivas definiciones de las funciones foo y suma. Finalmente la llamada
consiste en emplear la función dentro del programa principal u otra función. Ejemplo

1: /****************************************************************
2: Función suma, devuelve la suma de sus argumentos a y b
3: ****************************************************************/
4: int suma(int a, int b)
5: {
6: return (a+b);
7: }
8:
9: void main()
10:{
11: int a, b, c;
12:
13: a = 5;
14: b = 10;
15:
16: // Llamada a la función suma
17: c = suma(a, b)
18: }

C y ensamblador.

Se puede insertar código en ensamblador y código de máquina en un programa en C. Para


insertar código en ensamblador se emplea la directiva asm.

1: asm
2: {
3: // Sentencias en ensamblador
4: };

La siguiente función realiza un acceso a puerto de bajo nivel. Obsérvese como se puede
acceder directamente a los valores de los argumentos de la función utilizando el nombre dentro
del bloque asm (líneas 8 y 9)

1: void outp8(int addr, char c)


2: {
3: asm
4: {
5: push dx
6: push ax
7:
8: mov dx, addr
9: mov al, c
10: out dx, al
11:
12: pop ax
13: pop dx
14: };
15: }

C para 8086/8088 sin enlace a bibliotecas

Normalmente en las diferentes plataformas los compiladores se enlazan automáticamente a


bibliotecas proporcionadas por el Sistema Operativo (SO) para proveer a los programas de los
servicios básicos de entrada/salida, funcionalidades de alto nivel y otros. Cuando se está
construyendo un nuevo hardware, estos servicios no existen y deben ser creados desde la
base. Así se propone la siguiente estructura básica para organizar un proyecto en C con estas
características.

- Archivo de arranque (rtl.asm)


- Archivo de mapa de direcciones o arquitectura del sistema, también incluye directivas y
definiciones básicas para todo el programa (board.h)
- Programa principal (nombre.c)

Si existen uno o varios periféricos complejos se puede hacer una biblioteca independiente para
cada uno. Asumiendo el periférico que hemos llamado deviceX

- Archivo de declaraciones (deviceX.h)


- Archivo de definiciones (deviceX.c)

Un ejemplo concreto se puede encontrar en el sistema de control de LED vía UART aquí.

Potrebbero piacerti anche