Está en la página 1de 46

Instituto Tecnológico del Computadoras

Comahue Electrónicas

Lenguaje de Programación C

Ventajas de lenguajes de programación de alto nivel

A pesar de todos los lados buenos, el lenguaje ensamblador tiene algunas desventajas:

• Incluso una sola operación en el programa escrito en ensamblador consiste en muchas


instrucciones, haciéndolo muy largo y difícil de manejar.
• Cada tipo de microcontrolador tiene su propio conjunto de instrucciones que un programador tiene
que conocer para escribir un programa
• Un programador tiene que conocer el hardware del microcontrolador para escribir un programa

Programa escrito en C (El mismo programa compilado al código ensamblador):

Año 2021 1
Instituto Tecnológico del Computadoras
Comahue Electrónicas

Los lenguajes de programación de alto nivel (Basic, Pascal, C etc.) fueron creados con el propósito de

superar las desventajas del ensamblador. En lenguajes de programación de alto nivel varias instrucciones en

ensamblador se sustituyen por una sentencia. El programador ya no tiene que conocer el conjunto de

instrucciones o características del hardware del microcontrolador utilizado. Ya no es posible conocer

exactamente cómo se ejecuta cada sentencia, de todas formas ya no importa. Aunque siempre se puede

insertar en el programa una secuencia escrita en ensamblador.

Si alguna vez ha escrito un programa para un microcontrolador PIC en lenguaje ensamblador, probablemente

sepa que la arquitectura RISC carece de algunas instrucciones. Por ejemplo, no hay instrucción apropiada
para multiplicar dos números. Por supuesto, para cada problema hay una solución y éste no es una

excepción gracias a la aritmética que permite realizar las operaciones complejas al descomponerlas en un

gran número operaciones más simples. En este caso, la multiplicación se puede sustituir con facilidad por
adición sucesiva (a x b = a + a + a + ... + a). Ya estamos en el comienzo de una historia muy larga... No hay

que preocuparse al utilizar uno de estos lenguajes de programación de alto nivel como es C, porque el

compilador encontrará automáticamente la solución a éste problema y otros similares. Para multiplicar los

números a y b, basta con escribir a*b.

Lenguaje C

El lenguaje C dispone de todas las ventajas de un lenguaje de programación de alto nivel (anteriormente

descriptas) y le permite realizar algunas operaciones tanto sobre los bytes como sobre los bits (operaciones

lógicas, desplazamiento etc.). Las características de C pueden ser muy útiles al programar los
microcontroladores. Además, C está estandarizado (el estándar ANSI), es muy portable, así que el mismo

código se puede utilizar muchas veces en diferentes proyectos. Lo que lo hace accesible para cualquiera que

conozca este lenguaje sin reparar en el propósito de uso del microcontrolador. C es un lenguaje compilado, lo

que significa que los archivos fuentes que contienen el código C se traducen a lenguaje máquina por el

compilador. Todas estas características hicieron a C uno de los lenguajes de programación más populares.

Año 2021 2
Instituto Tecnológico del Computadoras
Comahue Electrónicas

La figura anterior es un ejemplo general de lo que sucede durante la compilación de programa de un lenguaje

de programación de alto nivel a bajo nivel.

Este libro describe una aplicación muy concreta del lenguaje de programación C utilizado en el compilador

XC8 para PIC. En este caso, el compilador se utiliza para la programación de los microcontroladores PIC.

Año 2021 3
Instituto Tecnológico del Computadoras
Comahue Electrónicas

FASES DE COMPILACIÓN

El proceso de compilación consiste en varios pasos y se ejecuta automáticamente por el compilador. Por
consiguiente, un conocimiento básico del funcionamiento puede ser útil para entender el concepto del
lenguaje C.

El archivo fuente contiene el código en C que usted escribe para programar el microcontrolador. El

preprocesador se utiliza automáticamente por el compilador al iniciarse el proceso de la compilación. El

compilador busca las directivas del preprocesador (que siempre empiezan por ‘#’) dentro del código y

modifica el código fuente de acuerdo con las directivas. En esta fase se llevan a cabo inclusión de archivos,

definición de constantes y macros etc, lo que facilita el proceso. Más tarde vamos a describir estas directivas

en detalle. El analizador sintáctico (parser) elimina toda la información inútil del código (comentarios,
espacios en blanco). Luego, el compilador traduce el código a un archivo binario denominado archivo .mcl.
El enlazador (linker) recupera toda la información requerida para ejecutar el programa de los archivos

externos y la agrupa en un solo archivo (.dbg). Además, un proyecto puede contener más de un archivo

fuente y el programador puede utilizar funciones predefinidas y agrupadas dentro de los archivos

denominados librerías. Por último, el generador .hex produce un archivo .hex. Es el archivo que se va a

cargar en el microcontrolador.

Año 2021 4
Instituto Tecnológico del Computadoras
Comahue Electrónicas

El proceso entero de la compilación que incluye todos los pasos anteriormente descritos se le denomina
“building”.

ESTRUCTURA DE PROGRAMA

La idea principal de escribir un programa en C es de “romper” un problema mayor en varios trozos más

pequeños. Supongamos que es necesario escribir un programa para el microcontrolador para medir la

temperatura y visualizar los resultados en un LCD. El proceso de medición se realiza por un sensor que

convierte temperatura en voltaje. El microcontrolador utiliza el convertidor A/D para convertir este voltaje

(valor analógico) en un número (valor digital) que luego se envía al LCD por medio de varios conductores. En

consecuencia, el programa se divide en cuatro partes, de las que cada una corresponde a una acción

específica:

1. Activar y configurar el convertidor A/D incorporado;


2. Medir el valor analógico;
3. Calcular temperatura; y
4. Enviar los datos en el formato apropiado al LCD;

Los lenguajes de programación de alto nivel como es C le permiten solucionar este problema con facilidad al
escribir cuatro funciones que se ejecutarán cíclicamente sin parar.

La idea general es de dividir el problema en varios trozos, de los que cada uno se puede escribir como una

sola función. Todos los programas escritos en XC8 contienen por lo menos una función llamada main() que

encierra entre llaves {} las sentencias a ser ejecutadas. Esta es la primera función a ser ejecutada al iniciarse

la ejecución de programa. Las otras funciones se pueden llamar dentro de la función main(). En otras

palabras, podemos decir que la función main() es obligatoria, mientras que las demás son opcionales. Si

todavía no ha escrito un programa en C, es probable que todo le resulte confuso. No se preocupe, acéptelo
tal como es por el momento y más tarde entenderá la sintaxis.

Año 2021 5
Instituto Tecnológico del Computadoras
Comahue Electrónicas

¡Y ahora, su primer programa ‘real’! La figura muestra la estructura de programa, señalando las partes en las
que consiste.

Año 2021 6
Instituto Tecnológico del Computadoras
Comahue Electrónicas

La manera de escribir el código en C es muy importante. Por ejemplo, C distingue entre minúsculas y

mayúsculas, así que la función main() no se puede escribir MAIN() o Main(). Además, note que dos líneas del

código dentro de la función terminan con un punto y coma. En C todas las sentencias deben terminar con un

punto y coma ‘;’, así el compilador puede aislarlas y traducirlas a código máquina.

Año 2021 7
Instituto Tecnológico del Computadoras
Comahue Electrónicas

1.2 TIPOS DE DATOS EN C

T AM AÑ O
TIPO DE
DESCRIPCIÓN (NÚMERO DE RANGO DE VALORES
DATO
BITS)

char Texto (caracteres) 8 de 0 a 255


int Valores enteros 16 de -32768 a 32767

de ±1.17549435082—10-38 a
float Valores en punto flotante 32
±6.80564774407—1038
Valores en punto flotante de ±1.17549435082—10-38 a
double de doble precisión 32 ±6.80564774407—1038

Es lógico pensar que si un char debe contener un carácter ASCII entonces el mismo debe poder almacenar
8 bits.
Un float que cumpla con la norma IEEE-754 debe tener 32 bits para simple precisión y 64 bits para doble
precisión.
El int generalmente se refiere a que usa la palabra del sistema entera. Entonces es esperable que en un
microcontrolador de 32 bits, el int ocupe 32 bits.

Al añadir un prefijo (calificador) a cualquier tipo de dato entero o carácter, el rango de sus
posibles valores cambia así como el número de los bytes de memoria necesarios. Por defecto, los datos
de tipo int son con signo, mientras que los de tipo char son sin signo. El calificador signed (con signo)
indica que el dato puede ser positivo o negativo. El prefijo unsigned indica que el dato puede ser sólo
positivo. Note que el prefijo es opcional.

Año 2021 8
Instituto Tecnológico del Computadoras
Comahue Electrónicas

TIPO DE TI PO D E DAT O CO N T AM AÑO ( NÚM ER O RANGO DE


DATO PREFIJO DE BIT S) VALORES

char signed char 8 de -128 a 128

unsigned int 16 de 0 a 65535


short int 8 de 0 a 255

signed short int 8 de -128 a 127


int
long int 32 de 0 a 4294967295

de -2147483648 a
signed long int 32
2147483647

Tipo punto flotante (float)

0. // = 0.0

-1.23 // = -1.23

El tipo punto flotante (float) se utiliza para los números reales con el punto decimal. Los datos de tipo float se

pueden representar de varias maneras. Un dato float es siempre consigno (signed).

23.45e6 // = 23.45 * 10^6

2e-5 // = 2.0 * 10^-5

3E+10 // = 3.0 * 10^10

.09E34 // = 0.09 * 10^34

Tipo carácter (char)

El tipo char es considerado como un entero por el compilador. No obstante, se utiliza normalmente para los
datos de tipo carácter. Un dato de tipo carácter está encerrado entre comillas y codificado en un carácter
ASCII.

59 // entero

'p' // carácter ASCII 'p'

Año 2021 9
Instituto Tecnológico del Computadoras
Comahue Electrónicas

Una secuencia de caracteres es denominada cadena (string). Las cadenas están encerradas entre comillas
dobles, por ejemplo:

"Presione el botón RA0"

1.3 VARIABLES Y CONSTANTES

Definiciones

Una variable es un objeto nombrado capaz de contener un dato que puede ser modificado durante la

ejecución de programa. En XC8, las variables tienen tipo, que significa que es necesario especificar el tipo de

dato que se le asigna a una variable (int, float etc.). Las variables se almacenan en la memoria RAM y el
espacio de memoria que ocupan (en bytes) depende de su tipo.

/* dos líneas de programa consecutivas. En la primera línea del programa

se define el tipo de variable */

int a = 1000; // Variable a es de tipo int y equivale a 1000

a = 15; // a equivale a 15

Una constante tiene las mismas características que una variable excepto el hecho de que su valor asignado

no puede ser cambiado durante la ejecución de programa. A diferencia de las variables, las constantes se

almacenan en la memoria Flash del microcontrolador para guardar el mayor espacio posible de memoria

RAM. El compilador las reconoce por el nombre y el prefijo const. En XC8, el compilador reconoce

automáticamente el tipo de dato de una constante, así que no es necesario especificar el tipo adicionalmente.

/* dos líneas de programa consecutivas */

const A = 1000 // el valor de la constante A está definido

A = 15; // ¡ERROR! no se puede modificar el valor de la constante

Cada variable o constante debe tener un identificador que lo distingue de otras variables y constantes.

Año 2021 10
Instituto Tecnológico del Computadoras
Comahue Electrónicas

Refiérase a los ejemplos anteriores, a y A son identificadores.

Declaración de variables

Cada variable debe ser declarada antes de ser utilizada en el programa. Como las variables se almacenan en

la memoria RAM, es necesario reservar el espacio para ellas (uno, dos o más bytes). Al escribir un programa,

usted sabe qué tipo de datos quiere utilizar y qué tipo de datos espera como resultado de una operación,

mientras que el compilador no lo sabe. No se olvide de que el programa maneja las variables con los

nombres asignados. El compilador las reconoce como números en la memoria RAM sin conocer su tamaño y

formato. Para mejorar la legibilidad de código, las variables se declaran con frecuencia al principio de las
funciones:

<tipo> variable;

Es posible declarar más de una variable de una vez si tienen el mismo tipo.

<tipo> variable1, variable2, variable3;

Aparte del nombre y del tipo, a las variables se les asignan con frecuencia los valores iniciales justamente
enseguida de su declaración. Esto no es un paso obligatorio, sino ‘una cuestión de buenas costumbres’. Se

parece a lo siguiente:

unsigned int peso; // Declarar una variable llamada peso

peso = 20; // Asignar el valor 20 a la variable peso

Un método más rápido se le denomina declaración con inicialización (asignación de los valores iniciales):

unsigned int peso = 20; // peso está declarado y su valor es 20

Si hay varias variables con el mismo valor inicial asignado, el proceso se puede simplificar:

Año 2021 11
Instituto Tecnológico del Computadoras
Comahue Electrónicas

unsigned int peso1 = peso2 = peso3 = 20;

int valor_inicial = un_mínimo_de_petróleo = 0;

• Tenga cuidado de no declarar la misma variable otra vez dentro de la misma función.
• Puede modificar el contenido de una variable al asignarle un nuevo valor tantas veces que quiera
• Al declarar una variable, siempre piense en los valores que la variable tendrá que contener durante
la ejecución de programa. En el ejemplo anterior, peso1 no se puede representar con un número
con punto decimal o un número con valor negativo.

Declaración de constantes

Similar a las variables, las constantes deben ser declaradas antes de ser utilizadas en el programa. En XC8,

no es obligatorio especificar el tipo de constante al declararla. Por otra parte, las constantes deben ser
inicializadas a la vez que se declaran. El compilador reconoce las constantes por su prefijo const utilizado en

la declaración. Dos siguientes declaraciones son equivalentes:

const int MINIMUM = -100; // Declarar constante MINIMUM

const MINIMUM = -100; // Declarar constante MINIMUM

Las constantes pueden ser de cualquier tipo, incluyendo cadenas:

const T_MAX = 3.260E1; // constante de punto flotante T_MAX

const I CLASS = 'A'; // constante carácter I_CLASS

const Mensaje = "Presione el botón IZQUIERDA"; // constante de cadena


Mensaje

Las constantes de enumeración son un tipo especial de constantes enteras que hace un programa más
comprensible al asignar los números ordinales a las constantes. Por defecto, el valor 0 se asigna

automáticamente a la primera constante entre llaves, el valor 1 a la segunda, el valor 2 a la tercera etc.

enum surtidores {AGUA,GASÓLEO,CLORO}; // AGUA = 0; GASÓLEO = 1; CLORO = 2

Es posible introducir directamente el valor de una constante dentro de la lista de enumeraciones. El

incremento se detiene al asignar un valor a un elemento de matriz, después se reinicia a partir del valor

Año 2021 12
Instituto Tecnológico del Computadoras
Comahue Electrónicas

asignado. Vea el siguiente ejemplo:

enum surtidores {AGUA,GASÓLEO=0,CLORO}; // AGUA = 0; GÁSOLEO = 0; CLORO = 1

Las constantes de enumeración se utilizan de la siguiente manera:

int Velocidad_de_ascensor

enum motor_de_ascensor {PARADA,INICIO,NORMAL,MÁXIMO};

Velocidad_de_ascensor = NORMAL; // Velocidad_de_ascensor = 2

Definir los nuevos tipos de datos

La palabra clave typedef le permite crear con facilidad los nuevos tipos de datos.

typedef unsigned int positivo; // positivo es un sinónimo para el tipo sin


signo int

positivo a,b; // Variables a y b son de tipo positivo

a = 10; // Variable a equivale a 10

b = 5; // Variable b equivale a 5

Ámbito de variables y constantes

Una variable o una constante son reconocidas por el compilador en base de su identificador. Un identificador

tiene significado si el compilador lo puede reconocer. El ámbito de una variable o una constante es el rango

de programa en el que su identificador tiene significado. El ámbito es determinado por el lugar en el que se
declara una variable o una constante. Intentar acceder a una variable o una constante fuera de su ámbito

resulta en un error. Una variable o una constante son invisibles fuera de su ámbito. Todas las variables y

constantes que pensamos utilizar en un programa deben ser declaradas anteriormente en el código. Las

variables y constantes pueden ser globales o locales. Una variable global se declara en el código fuente,

fuera de todas las funciones, mientras que una variable local se declara dentro del cuerpo de la función o

dentro de un bloque anidado en una función.

Año 2021 13
Instituto Tecnológico del Computadoras
Comahue Electrónicas

A las variables globales se les puede acceder de cualquiera parte en el código, aún dentro de las funciones
con tal de que sean declaradas. El ámbito de una variable global está limitado por el fin del archivo fuente en

el que ha sido declarado.

El ámbito de variables locales está limitado por el bloque encerrado entre llaves {} en el que han sido
declaradas. Por ejemplo, si están declaradas en el principio del cuerpo de función (igual que en la función
main) su ámbito está entre el punto de declaración y el fin de esa función. Refiérase al ejemplo anterior. A las
variables locales declaradas en main() no se les puede acceder desde la Función_1 y al revés.

Un bloque compuesto es un grupo de declaraciones y sentencias (que pueden ser bloques también)

encerradas entre llaves. Un bloque puede ser una función, una estructura de control etc. Una variable

declarada dentro de un bloque se considera local, o sea, ‘existe’ sólo dentro del bloque. Sin embargo, las

variables declaradas fuera del ámbito todavía son visibles.

Aunque las constantes no pueden ser modificadas en el programa, siguen las mismas reglas que las

variables. Esto significa que son visibles dentro de su bloque a excepción de las constantes globales

(declaradas fuera de cualquier función). Las constantes se declaran normalmente en el inicio del código fuera

de cualquier función (como variables globales).

Año 2021 14
Instituto Tecnológico del Computadoras
Comahue Electrónicas

1.3 OPERADORES

Un operador es un símbolo que denota una operación aritmética, lógica u otra operación particular. Dicho de

manera sencilla, varias operaciones aritméticas y lógicas se realizan por medio de los operadores. Hay más

de 40 operaciones disponibles en el lenguaje C, pero se utiliza un máximo de 10-15 de ellas en práctica.


Cada operación se realiza sobre uno o más operandos que pueden ser variables o constantes. Además, cada

operación se caracteriza por la prioridad de ejecución y por la asociatividad.

OPERADORES ARITMÉTICOS

Los operadores aritméticos se utilizan en las operaciones aritméticas y siempre devuelven resultados

numéricos. Hay dos tipos de operadores, los unitarios y los binarios. A diferencia de las operaciones unitarias

que se realizan sobre un operando, las operaciones binarias se realizan sobre dos operandos. En otras
palabras, se requieren dos números para ejecutar una operación binaria. Por ejemplo: a+b o a/b.

OPERADOR OPERACIÓN

+ Adición

- Resta

* Multiplicación

/ División

% Resto de la división

int a,b,c; // Declarar 3 enteros a, b, c

a = 5; // Inicializar a

b = 4; // Inicializar b

c = a + b; // c = 9

c = c%2; // c = 1. Esta operación se utiliza con frecuencia

// para comprobar la paridad. En este caso, el

// resultado es 1 lo que significa que la variable

// es un número imparo

Año 2021 15
Instituto Tecnológico del Computadoras
Comahue Electrónicas

OPERADORES DE ASIGNACIÓN

Hay dos tipos de asignación en el lenguaje C:

• Los operadores simples asignan los valores a las variables utilizando el carácter común '='. Por
ejemplo: a =8
• Las asignaciones compuestas son específicas para el lenguaje C. Consisten en dos caracteres
como se muestra en la tabla a la derecha. Se utilizan para simplificar la sintaxis y habilitar la
ejecución más rápida.

EJEMPLO
OPERADO R
Expresión Equivalente

+= a += 8 a=a+8

-= a -= 8 a = a - 8

*= a *= 8 a=a*8

/= a /= 8 a=a/8

%= a %= 8 a=a%8

int a = 5; // Declarar e inicializar la variable a

a += 10; // a = a + 10 = 15

OPERADORES DE INCREMENTO Y DECREMENTO

Las operaciones de incremento y decremento por 1 se denotan con "++" y "--". Estos caracteres pueden

preceder o seguir a una variable. En primer caso (++x), la variable x será incrementada por 1 antes de ser

utilizada en la expresión. De lo contrario, la variable se utilizará en la expresión antes de ser aumentada por

1. Lo mismo se aplica a la operación de decremento.

OPERADOR EJEMPLO DESCRIPCIÓN

++a
++ Variable "a" es incrementada por 1
a++

--b
-- Variable "a" es decrementada por 1
b--

int a, b, c;

a = b = 5;

c = 1 + a++; // c = 6

b = ++c + a // b = 7 + 6 = 13
Año 2021 16
Instituto Tecnológico del Computadoras
Comahue Electrónicas

OPERADORES RELACIONALES

Los operadores relacionales se utilizan en comparaciones con el propósito de comparar dos valores. En C, si

una expresión es evaluada como falsa (false), el operador devuelve 0, mientras que si una oración es

evaluada como verdadera (true), devuelve 1. Esto se utiliza en expresiones tales como ‘si la expresión es

evaluada como verdadera, entonces...’

OPERADOR DESCRIPCIÓN EJEMPLO CONDICIÓN DE VERACIDAD

> mayor que b>a si b es mayor que a

>= mayor o igual que a >= 5 si a es mayor o igual que 5

< menor que a<b si a es menor que b

<= menor o igual que a <= b si a es menor o igual que b

== igual que a == 6 si a es igual que 6

!= desigual que a != b si a es desigual que b

int prop;

int var = 5;

prop = var < 10; // Expresión es evaluada como verdadera, prop = 1

Año 2021 17
Instituto Tecnológico del Computadoras
Comahue Electrónicas

OPERADORES LÓGICOS

Hay tres tipos de operaciones lógicas en el lenguaje C: Y (AND) lógico, O (OR) lógico y negación - NO (NOT)

lógico. Los operadores lógicos devuelven verdadero (1 lógico) si la expresión evaluada es distinta de cero. En

caso contrario, devuelve falso (0 lógico) si la expresión evaluada equivale a cero. Esto es muy importante

porque las operaciones lógicas se realizan generalmente sobre las expresiones, y no sobre las variables

(números) particulares en el programa. Por lo tanto, las operaciones lógicas se refieren a la veracidad de toda

la expresión.

Por ejemplo: 1 && 0 es igual a (expresión verdadera) && (expresión falsa)

El resultado 0, o sea - Falso en ambos casos.

OPERADOR FUNCIÓN

&& Y

|| O

! NO

OPERADORES DE MANEJO DE BITS

A diferencia de las operaciones lógicas que se realizan sobre los valores o expresiones, las operaciones de

manejo de bits se realizan sobre los bits de un operando. Se enumeran en la siguiente tabla:

OPERADOR DESCRIPCIÓN EJEMPLO R ESU LT AD O

~ Complemento a uno a = ~b b=5 a = -5

<< Desplazamiento a la izquierda a = b << 2 b = 11110011 a = 11001100

>> Desplazamiento a la derecha a = b >> 2 b = 11110011 a = 00011110

a = 11100011
& Y lógico para manejo de bits c=a&b c = 11000000
b = 11001100

a = 11100011
| O lógico para manejo de bits c=a|b c = 11101111
b = 11001100

a = 11100011
^ EXOR lógico para manejo de bits c=a^b c = 00101111
b = 11001100

Note que el resultado de la operación de desplazamiento a la derecha depende del signo de la variable. En

caso de que el operando se aplique a una variable sin signo o positiva, se introducirán los ceros en el espacio
vacío creado por desplazamiento. Si se aplica a un entero con signo negativo, se introducirá un 1 para

mantener el signo correcto de la variable.

Año 2021 18
Instituto Tecnológico del Computadoras
Comahue Electrónicas

¿CÓMO UTILIZAR LOS OPERADORES?

• Aparte de los operadores de asignación, dos operadores no deben estar escritos uno junto al otro.

x*%12; // esta expresión generará un error

• Cada operador tiene su prioridad y asociatividad como se muestra en la tabla:


• Similar a las expresiones aritméticas, los operadores se agrupan juntos por medio de paréntesis.
Primero se calculan las expresiones encerradas entre paréntesis. Si es necesario, se pueden
utilizar los paréntesis múltiples (anidados).

PRIORIDAD OPERADORES ASO C I AT I VI D AD

() [] -> . de izquierda a
Alta
derecha

! ~ ++ -- +(unitario) -(unitario) *Puntero de derecha a


&Puntero izquierda

* / % de izquierda a
derecha

+ - de izquierda a
derecha

< > de izquierda a


derecha

de izquierda a
< <= > >=
derecha

== != de izquierda a
derecha

& de izquierda a
derecha

^ de izquierda a
derecha

| de izquierda a
derecha

de izquierda a
&&
derecha

de derecha a
||
izquierda

?: de derecha a
izquierda

= += -= *= /= /= &= ^= |= <= >= de izquierda a


Baja
derecha

int a, b, res;

Año 2021 19
Instituto Tecnológico del Computadoras
Comahue Electrónicas

a = 10;

b = 100;

res = a*(a + b); // resultado = 1100

res = a*a + b; // resultado = 200

CONVERSIÓN DE TIPOS DE DATOS

Algunas operaciones implican conversión de datos. Por ejemplo, si divide dos valores enteros, hay una alta

posibilidad de que el resultado no sea un entero. El XC8 realiza una conversión automática cuando se
requiera.

Si dos operandos de tipo diferente se utilizan en una operación aritmética, el tipo de operando de la prioridad

más baja se convierte automáticamente en el tipo de operando de la prioridad más alta. Los tipos de datos

principales se colocan según el siguiente orden jerárquico:

La autoconversión se realiza asimismo en las operaciones de asignación. El resultado de la expresión de la


derecha del operador de la asignación siempre se convierte en el tipo de la variable de la izquierda del

operador. Si el resultado es de tipo de la prioridad más alta, se descarta o se redondea para coincidir con el

tipo de la variable. Al convertir un dato real en un entero, siempre se descartan los números que siguen al

punto decimal.

int x; // A la variable x se le asigna el tipo integer (un entero)

x = 3; // A la variable x se le asigna el valor 3

x+ = 3.14; // El valor 3.14 se agrega a la variable x al realizar la


operación de asignación

Año 2021 20
Instituto Tecnológico del Computadoras
Comahue Electrónicas

/* El resultado de la adición es 6 en vez de 6.14, como era de esperar.

Para obtener el resultado esperado sin descartar los números que siguen al

punto decimal, se debe declarar x como un punto flotante. */

Para realizar una conversión explícita, antes de escribir una expresión o una variable hay que especificar el
tipo de resultado de operación entre paréntesis.

double distancia, tiempo, velocidad;

distancia = 0.89;

tiempo = 0.1;

velocidad = (int)(a/b); // c = (int)8.9 = 8.0

velocidad = ((int)a)/b; // c = 0/0.1 = 0.0

1.4 ESTRUCTURAS DE CONTROL

ESTRUCTURAS CONDICIONALES

Las condiciones son ingredientes comunes de un programa. Las condiciones permiten ejecutar una o varias
sentencias dependiendo de validez de una expresión. En otras palabras, ‘Si se cumple la condición (...), se

debe hacer (...). De lo contrario, si la condición no se cumple, se debe hacer (...)’. Los operandos

condicionales if-else y switch se utilizan en las operaciones condicionales. Una sentencia condicional puede

ser seguida por una sola sentencia o por un bloque de sentencias a ser ejecutadas.

OPERADOR CONDICIONAL if-else

El operador if se puede utilizar solo o asociado al operador else (if-else).

Ejemplo del operador if:

if(expresión) operación;

Si el resultado de la expresión encerrada entre paréntesis es verdadero (distinto de 0) la operación se realiza

Año 2021 21
Instituto Tecnológico del Computadoras
Comahue Electrónicas

y el programa continúa con la ejecución. Si el resultado de la expresión es falso (0), la operación no se realiza

y el programa continúa inmediatamente con la ejecución.

Como hemos mencionado, la otra forma combina tanto el operador if como el else:

if(expresión) operación1 else operación2;

Si el resultado de la expresión es verdadero (distinto de 0), se realiza operación1, de lo contrario se realiza la


operación2. Después de realizar una de las operaciones, el programa continúa con la ejecución.

La sentencia if-else se parece a lo siguiente:

if(expresión)

operación1

else

operación2

Si operación1 u operación2 está compuesta, escriba una lista de sentencias encerradas entre llaves. Por
ejemplo:

if(expresión) {

... //

... // operación1

...} //

else

operación2

El operador if-else se puede sustituir por el operador condicional '?:':

Año 2021 22
Instituto Tecnológico del Computadoras
Comahue Electrónicas

(expresión1)? expresión2 : expresión3

Si el valor de la expresión1 es distinto de 0 (verdadero), el resultado de la expresión entera será equivalente

al resultado obtenido de la expresión2. De lo contrario, si la expresión1 es 0 (falso), el resultado de la


expresión entera será equivalente al resultado obtenido de la expresión3. Por ejemplo:

maximum = (a>b)? a : b // A la variable maximum se le asigna el

// valor de la variable mayor(a o b)

Operador Switch

A diferencia de la sentencia if-else que selecciona entre dos opciones en el programa, el operador switch

permite elegir entre varias opciones. La sintaxis de la sentencia switch es:

switch (selector) // Selector es de tipo char o int

case constante1:

operación1 // El grupo de operadores que se ejecutan si

... // el selector y la constante1 son equivalentes

break;

case constante2:

operación2 // El grupo de operadores se ejecuta si

... // el selector y la constante2 son equivalentes

Año 2021 23
Instituto Tecnológico del Computadoras
Comahue Electrónicas

break;

...

default:

operación_esperada // El grupo de operadores que se ejecuta si

... // ninguna constante equivale al selector

break;

La operación switch se ejecuta de la siguiente manera: primero se ejecuta el selector y se compara con la

constante1. Si coinciden, las sentencias que pertenecen a ese bloque se ejecutan hasta llegar a la palabra
clave break o hasta el final de la operación switch. Si no coinciden, el selector se compara con la constante2.

Si coinciden, las sentencias que pertenecen a ese bloque se ejecutan hasta llegar a la palabra clave break

etc. Si el selector no coincide con ninguna constante, se ejecutarán las operaciones que siguen al operador

default.

También es posible comparar una expresión con un grupo de constantes. Si coincide con alguna de ellas, se

ejecutarán las operaciones apropiadas:

switch (días) // La variable días representa un día de la semana.

{ // Es necesario determinar si es un día laborable o no lo es

case1:case2:case3:case4:case5: LCD_message = 'Día laborable'; break;

case6:case7: LCD_message = 'Fin de semana'; break;

default:LCD_message_1 = 'Elija un día de la semana'; break;

La palabra clave de C ‘break’ se puede utilizar en cualquier tipo de bloques. Al utilizar ‘break’, es posible salir de

un bloque aunque la condición para su final no se haya cumplido. Se puede utilizar para terminar un bucle infinito,

o para forzar un bucle a terminar antes de lo normal.

Año 2021 24
Instituto Tecnológico del Computadoras
Comahue Electrónicas

BUCLES

A menudo es necesario repetir una cierta operación un par de veces en el programa. Un conjunto de

comandos que se repiten es denominado un bucle de programa. Cuántas veces se ejecutará, es decir cuánto
tiempo el programa se quedará en el bucle, depende de las condiciones de salir del bucle.

Bucle While

El bucle while se parece a lo siguiente:

while(expresión){

comandos

...

Los comandos se ejecutan repetidamente (el programa se queda en el bucle) hasta que la expresión llegue a
ser falsa. Si la expresión es falsa en la entrada del bucle, entonces el bucle no se ejecutará y el programa
continuará desde el fin del bucle while.

Un tipo especial del bucle de programa es un bucle infinito. Se forma si la condición sigue sin cambios dentro
del bucle. La ejecución es simple en este caso ya que el resultado entre llaves es siempre verdadero

(1=verdadero), lo que significa que el programa se queda en el mismo bucle:

while(1){ // En vez de "while(1)", se puede escribir "while(true)"

... // Expresiones encerradas entre llaves se ejecutarán

... // repetidamente (bucle infinito)

Año 2021 25
Instituto Tecnológico del Computadoras
Comahue Electrónicas

Bucle For

El bucle for se parece a lo siguiente:

for(expresión_inicial; expresión_de_condición; cambiar_expresión) {

operaciones

...

La ejecución de esta secuencia de programa es similar al bucle while, salvo que en este caso el proceso de

especificar el valor inicial (inicialización) se realice en la declaración. La expresión_ inicial especifica la

variable inicial del bucle, que más tarde se compara con la expresión_ de_condición antes de entrar al bucle.

Las operaciones dentro del bucle se ejecutan repetidamente y después de cada iteración el valor de la
expresión_inicial se incrementa de acuerdo con la regla cambiar_expresión. La iteración continúa hasta que

la expresión_de_condición llegue a ser falsa.

for(k=0; k<5; k++) // La variable k se incrementa 5 veces (de 1 a 4) y

operación // cada vez se repite la expresión operación

...

La operación se ejecutará cinco veces. Luego, al comprobar se valida que la expresión k<5 sea falsa
(después de 5 iteraciones k=5) y el programa saldrá del bucle for.

Bucle Do-while

El bucle do-while se parece a lo siguiente:

do

operación

while (cambiar_condición);

Año 2021 26
Instituto Tecnológico del Computadoras
Comahue Electrónicas

La expresión cambiar_condición se ejecuta al final del bucle, que significa que operación se ejecuta como
mínimo una vez sin reparar en que si la condición es verdadera o falsa. Si el resultado es distinto de 0

(verdadero), el procedimiento se repite.

Todos los siguientes ejemplos son equivalentes. Esta parte del código visualiza "hello" en un LCD 10 veces

con un retardo de un segundo. Note que en este ejemplo se utilizan funciones predefinidas, que se

encuentran en las librerías del compilador XC8 PRO for PIC. No obstante le aconsejamos que no trate de

entenderlas en detalle. Su comportamiento general dentro del bucle se explica por medio de los comentarios.

i = 0; // Inicialización del contador

while (i<10) { // Condición

Lcd_Out(1,3,"hello"); // Visualizar “hello” en el LCD

Delay ms(1000); // Retardo de 1000 ms

Lcd_Cmd(_LCD_CLEAR); // Borrar el LCD

Delay ms(500); // Retardo de 500ms

i++; // Contador se incrementa

for(i=0; i<10; i++) { // Inicialización, condición, incremento

Lcd_Out(1,3,"hello"); // Visualizar “hello” en el LCD

Delay ms(1000); // Retardo de 1000 ms

Lcd_Cmd(_LCD_CLEAR); // Borrar el LCD

Delay ms(500); // Retardo de 500ms

i = 0; // Inicialización del contador

do {

Año 2021 27
Instituto Tecnológico del Computadoras
Comahue Electrónicas

Lcd_Out(1,3,"hello"); // Visualizar “hello” en el LCD

Delay ms(1000); // Retardo de 1000 ms

Lcd_Cmd(_LCD_CLEAR); // Borrar LCD

Delay ms(500); // Retardo de 500ms

i++; // Contador se incrementa

while (i<10); // Condición

SENTENCIAS DE SALTO

SENTENCIA BREAK

A veces es necesario detener y salir de un bucle dentro de su cuerpo. La sentencia break se puede utilizar

dentro de cualquier bucle (while, for, do while) y en las sentencias switch también. En éstas la sentencia
break se utiliza para salir de las sentencias switch si la condición case es verdadera. En este ejemplo,

“Esperar” está parpadeando en la pantalla LCD hasta que el programa detecte un uno lógico en el pin 0 del

puerto PORTA.

while(1){ // Bucle infinito

if(PORTAbits.RA0 == 1) // Probar si el estado lógico del pin 0 del


puerto

break; // Si RB7 es 1; equivale a salir del bucle

Lcd Out(1,3,"Esperar"); // Visualizar “Esperar” en el LCD

Delay_ms(1000); // Retardo de 1000 ms

Lcd Cmd( LCD CLEAR); // Borrar LCD

Delay_ms(500); // Retardo de 500ms

Año 2021 28
Instituto Tecnológico del Computadoras
Comahue Electrónicas

SENTENCIA CONTINUE
La sentencia continue colocada dentro de un bucle se utiliza para saltar una iteración. A diferencia de la
sentencia break, el programa se queda dentro del bucle y las iteraciones continúan.

// Si x=7, puede ocurrir una división por 0.

// continue se utiliza aquí para evitar esta situación.

x=1;

while (x<=10) {

if (x == 7) { // saltar x=7 para evitar división por 0

Lcd_Cmd(_LCD_CLEAR);

Lcd_Out(1,3,"Division by 0");

Delay_ms(1000);

x++;

continue; // Después de esta línea, saltar a la sentencia while con x=8

a = 1/(x-7); // Esta división generará un error si x=7

/* Muchas operaciones pueden ocurrir aquí */

Lcd_Out(1,3,"Division is OK"); // Poner este mensaje en el LCD

Delay_ms(1000);

x++;

Año 2021 29
Instituto Tecnológico del Computadoras
Comahue Electrónicas

2.7 TIPOS DE DATOS AVANZADOS

MATRICES (ARREGLOS O ARRAYS)

Una matriz es una lista de elementos del mismo tipo colocados en localidades de memoria contiguas. Cada
elemento es referenciado por un índice. Para declarar una matriz, es necesario especificar el tipo de sus

elementos (denominado tipo de matriz), su nombre y el número de sus elementos encerrados entre

corchetes. Todos los elementos de una matriz tienen el mismo tipo.

tipo_de_matriz nombre_de_matriz [nº_de_elementos];

Los elementos de una matriz se identifican por su posición. En C, el índice va desde 0 (el primer elemento de

una matriz) a N-1 (N es el número de elementos contenidos en una matriz). El compilador tiene que “saber”

Año 2021 30
Instituto Tecnológico del Computadoras
Comahue Electrónicas

cuántas localidades de memoria debe alojar al declarar una matriz. El tamaño de una matiz no puede ser una
variable. Por eso, se pueden utilizar dos métodos:

// método 1

int display [3]; // Declaración de la matriz display capaz de contener 3


enteros

// método 2

const DÍGITOS = 5;

char Matriz_nueva[DÍGITOS]; // Declaración de la matriz Matriz_nueva

// capaz de contener 5 enteros

Una matriz se puede inicializar a la vez que se declara, o más tarde en el programa. En ambos casos, este
paso se realiza al utilizar llaves:

int array_1[3] = {10,1,100};

Para leer o modificar un elemento de matriz del ejemplo anterior, basta con introducir su índice encerrado
entre corchetes:

/* Se supone que a ha sido declarado anteriormente como un entero */

a = array_1[0]; // A la variable a se le asigna el valor del miembro de


matriz

Año 2021 31
Instituto Tecnológico del Computadoras
Comahue Electrónicas

// con índice 0 (a = 10)

array_1[2] = 20; // Miembro de matriz array_1[2] es modificado (nuevo valor


es 20)

El siguiente programa cambia el orden de los elementos de una matriz. Note que el índice se puede expresar
mediante variables y operaciones básicas.

void main() {

const MUESTRAS_DE_AGUA = 4; // Valor de la constante MUESTRAS_DE_AGUA es


4

int i, temp; // Variables i y temp son de tipo int

int profunidad_de_sonda [MUESTRAS_DE_AGUA] = {24,25,1,1987};// Todos

// los miembros de la matriz profundidad

// de sonda son de tipo int

for(i=0;i<(MUESTRAS DE AGUA/2);i++){ // Bucle se ejecuta 2 veces

temp = profundiad_de_sonda [i]; // temp se utiliza para guardar un


valor

// temporalmente

profundiad_de_sonda [i] = profundiad_de_sonda [MUESTRAS_DE_AGUA-1-i];

profundiad_de_sonda [MUESTRAS_DE_AGUA-1-i] = temp;

// Aquí tenemos: profundidad_de_sonda [MUESTRAS_DE_AGUA] = {1987,1,25,24}

Año 2021 32
Instituto Tecnológico del Computadoras
Comahue Electrónicas

MATRICES BIDIMENSIONALES

Aparte de las matrices unidimensionales que se pueden interpretar como una lista de valores, el lenguaje C le

permite declarar matrices multidimensionales. En esta parte vamos a describir sólo las matrices

bidimensionales, también denominadas tablas o matrices. Una matriz bidimensional se declara al especificar
el tipo de dato de matriz, el nombre de matriz y el tamaño de cada dimensión.

tipo_de_matriz nombre_de_matriz [número_de_filas] [número_de_columnas];

En la declaración de esta matriz número_de_filas y número_de_columnas representan el número de filas y


columnas en las que consiste una tabla, respectivamente. Vea la siguiente matriz bidimensional:

int Tabla [3][4]; // Tabla se define de modo que tenga 3 filas y 4 columnas

Esta matriz se puede representar en la forma de una tabla.

tabla[0][0] tabla[0][1] tabla[0][2] tabla[0][3]

tabla[1][0] tabla[1][1] tabla[1][2] tabla[1][3]

tabla[2][0] tabla[2][1] tabla[2][2] tabla[2][3]

Similar a las matrices unidimesionales, es posible asignar los valores a los elementos de una tabla en la línea
de declaración. La asignación debe ser realizada línea a línea como en el siguiente ejemplo. Como hemos
visto anteriormente, esta matriz tiene dos filas y tres columnas:

int Tabla [2][3]= { {3,42,1},{7,7,19} };

La matriz anterior se puede representar también en la forma de una tabla de valores:

3 42 1

7 7 19

Año 2021 33
Instituto Tecnológico del Computadoras
Comahue Electrónicas

PUNTEROS

Un puntero es una variable destinada a recibir una dirección. Un puntero “apunta” a una localidad de
memoria, referenciada por una dirección. En C, la dirección de un objeto se puede obtener por medio un

operador unitario &. Para acceder al contenido de la memoria en una dirección específica (también llamado

objeto apuntado), se utiliza un operador de indirección (*).

'&n' es la dirección de la localidad de memoria 'n'.

'*(&n)' es el contenido de la dirección '(&n)', o sea de 'n'.

Para declarar un puntero, se debe que especificar el tipo de la variable apuntada:

tipo_de_variable *puntero;

En esta etapa, el puntero mi_puntero apunta al valor almacenado en esta localidad de memoria, o sea, a un
valor desconocido. Así que, una inicialización es muy recomendable:

puntero = &variable;

Ahora, puntero contiene la dirección de variable.

Para acceder al contenido de la variable apuntada, debe utilizar ‘*’. El siguiente ejemplo muestra el contenido

de memoria dependiendo de la acción realizada por medio del puntero.

Año 2021 34
Instituto Tecnológico del Computadoras
Comahue Electrónicas

Los punteros son muy útiles para manejar las matrices. En este caso, un puntero se utilizará para apuntar al

primer elemento de una matriz. Debido al hecho de que es posible realizar operaciones básicas sobre los

punteros (aritmética de punteros), es fácil manejar los elementos de una matriz.

Fíjese en la diferencia entre ‘*v+1’ y ‘*(v+1)’ en el siguiente ejemplo:

short int voltio[3] = {0,5,10};

short int *v;

v = &(voltio[0]); // v contiene la dirección de voltio[0]

*(v+1) = 2; // voltio[1] = 2

voltio[2] = *v+1; // tab[2] = 1 (tab[0] + 1)

*(v+2) = *(v+1); // voltio[2] = 2

v++; // v contiene la dirección de voltio[1]

*v = 1; // voltio[1] = 1

• Los punteros también pueden ser declarados con el prefijo ‘const’. En este caso, su valor no puede
ser modificado después de la inicialización, similar a una constante.
• A diferencia de C, el XC8 no admite alojamiento dinámico.

Año 2021 35
Instituto Tecnológico del Computadoras
Comahue Electrónicas

ESTRUCTURAS

Ya hemos visto cómo agrupar los elementos dentro de matrices. No obstante, al utilizar este método todos los

elementos deben ser del mismo tipo. Al utilizar estructuras, es posible agrupar diferentes tipos de variables

bajo el mismo nombre. Las variables dentro de una estructura se le denominan los miembros de la estructura.
Las estructuras de datos se declaran al utilizar la siguiente sintaxis:

struct nombre_de_estructura {

tipo1_de_miembro1 miembro1;

tipo2_de_miembro2 miembro2;

tipo3_de_miembro3 miembro3;

..

};

No es posible inicializar variables dentro de la declaración de la estructura de datos:

struct generador {

int voltaje;

char corriente;

};

Entonces, podrá definir los objetos denominados ‘turbina’ en el código. A cada uno de estos tres objetos
(turbinas) se le asignan las variables ‘corriente’ y ‘voltaje’.

struct generadores turbina_1, turbina_2, turbina_3;

Para acceder a los miembros de estructuras y uniones se utilizan los dos siguientes operadores de
selección: . (punto); y -> (flecha a la derecha).

El operador ‘.’ se llama selector directo de miembro. Se utiliza para acceder directamente a uno de
miembros de estructura.

Año 2021 36
Instituto Tecnológico del Computadoras
Comahue Electrónicas

turbina_3.voltaje = 150;

turbina_3.corriente = 12;

El operador -> se llama el selector indirecto (de puntero) de miembro.

pturbina_3.voltaje = 150; //Acceso indirecto, igual que

//( *pturbina_3).voltaje

Tipos de uniones
Los tipos de uniones son los tipos derivados, con cierta similitud sintáctica y funcional con los tipos de
estructura. La diferencia principal es que los miembros de uniones comparten el mismo espacio de memoria

Por supuesto, igual que al utilizar los punteros, todavía se le permite realizar operaciones por medio de

operadores y sentencias definidos en las partes anteriores.

Si está familiarizado con el lenguaje C, recuerde que XC8 no admite la inicialización de los miembros de

estructura por medio de las llaves. Por ejemplo, ‘conjunto_1 ={15,‘m’};’ devuelve un error en XC8.

1.6 FUNCIONES

Una función es una subrutina que contiene una lista de sentencias a realizar. La idea principal es dividir un

programa en varias partes utilizando estas funciones para resolver el problema inicial con más facilidad.

Además, las funciones nos permiten utilizar las destrezas y el conocimiento de otros programadores. Una

función se ejecuta cada vez que se llame dentro de otra función. En C, un programa contiene como mínimo

una función, la función main(), aunque el número de funciones es normalmente mayor. Al utilizar funciones el

código se hace más corto ya que es posible llamar una función tantas veces como se necesite. En C, el
código normalmente consiste en muchas funciones. No obstante, en caso de que su programa sea muy corto

y simple, puede escribir todas las sentencias dentro de la función principal.

FUNCIÓN PRINCIPAL

La función principal main() es una función particular puesto que es la que se ejecuta al iniciar el programa.
Además, el programa termina una vez completada la ejecución de esta función. El compilador reconoce

automáticamente esta función y no es posible llamarla por otra función. La sintaxis de esta función es la

siguiente:

Año 2021 37
Instituto Tecnológico del Computadoras
Comahue Electrónicas

void main (void) {

/* el primer 'void' significa que main no devuelve ningún valor. El segundo

'void' significa que no recibe ningún valor. Note que el compilador

también admite la siguiente sintaxis: 'main()' o 'void main()' o

'main(void)' */

..

/* --- Introduzca su programa aquí --- */

};

Esto significa que f es una función que recibe un número real x como parámetro y devuelve 2*x-y.

La misma función en C se parece a lo siguiente:

float f (float x, float y) // variables flotantes x y y se pueden utilizar


en f

float r; // declarar r para almacenar el resultado

r = 2*x - y; // almacenar el resultado del cálculo en r

return r; // devolver el valor de r

Cada función debe ser declarada apropiadamente para poder interpretarla correctamente durante el proceso
de compilación. La declaración contiene los siguientes elementos:

• Tipo de resultado (valor devuelto): tipo de dato del valor devuelto


• Nombre de función: es un identificador que hace posible llamar a una función.
• Declaración de parámetros se parece a la declaración de variable regular (por ejemplo: float x).

Año 2021 38
Instituto Tecnológico del Computadoras
Comahue Electrónicas

• Cada parámetro consiste en una variable, constante, puntero o matriz, precedidos por la etiqueta
de tipo de dato. Se utilizan para pasar la información a la función al llamarla. Los parámetros
diferentes están delimitados por comas.
• Cuerpo de función: bloque de sentencias dentro de llaves

Una función se parece a lo siguiente:

tipo_de_resultado nombre_de_función (tipo argumento1, tipo argumento2,...)

Sentencia;

Sentencia;

...

return ...

Note que una función no necesita parámetros (función main() por ejemplo), pero debe estar entre paréntesis.
En caso contrario, el compilador malinterpretaría la función. Para hacerlo más claro, puede sustituir el espacio

en blanco encerrado entre paréntesis por la palabra clave void: main (void).

VALOR DEVUELTO

Una función puede devolver un valor (esto no es obligatorio) por medio de la palabra clave return. Al llegar a
return, la función evalúa un valor (puede ser una expresión) y lo devuelve a la línea de programa desde la
que fue llamada.

return r; // Devolver el valor contenido en r

return (2*x - y); // Devolver el valor de la expresión 2*x-y

Una función no puede devolver más de un valor, pero puede devolver un puntero o una estructura. Tenga
cuidado al utilizar matrices y punteros. El siguiente ejemplo es un error típico:

int *reverse(int *tab) // Esta función debe devolver una matriz r

Año 2021 39
Instituto Tecnológico del Computadoras
Comahue Electrónicas

{ // cuyo contenido está en orden inverso con

// respecto a la matriz tab

int r[DIM]; // Declaración de una nueva matriz denominada r

int i;

for(i=0;i<DIM;i++) // Bucle que copia el contenido de tab en r

r[i] = tab[DIM-1-i]; // al invertir el orden

return r; // Devolver el valor r

En realidad, el compilador reserva memoria para el almacenamiento de variables de la función reverse sólo
durante su ejecución. Una vez completada la ejecución de reverse, la localidad de memoria para la variable i

o para la matriz r ya no está reservada. Esto significa que la dirección que contiene los valores de i o r[] está

libre para introducir datos nuevos. Concretamente, la función devuelve sólo el valor &r[0], así que sólo el

primer elemento de la matriz tab será almacenado en la memoria. Las demás localidades de memoria, tales

como &tab[1], &tab[2], etc. serán consideradas por el compilador como espacios en blanco, o sea, estarán

listas para recibir los nuevos valores.

Para escribir esta función es necesario pasar la matriz r [] como parámetro (vea la subsección Pasar los
parámetros).

La función puede contener más de una sentencia return. En este caso, al ejecutar la primera sentencia return,
la función devuelve el valor correspondiente y se detiene la ejecución de la función.

float abs (float x, float y) // Devolver el valor absoluto de 2*x-y

if ((2*x - y) >= 0)

return (2*x - y);

Año 2021 40
Instituto Tecnológico del Computadoras
Comahue Electrónicas

else

return (-2*x + y);

Si la función no devuelve ningún valor, la palabra void debe ser utilizada como un tipo de resultado en la
declaración. En este caso, la sentencia return no debe ser seguida por ninguna expresión. Puede ser omitida
como en el siguiente ejemplo:

void wait_1 (unsigned int a)

cnt ++; // Incremento de una variable global cnt

Delay ms(a) ; // Ejecución de la función Delay ms

} // Note que Delay_ms no devuelve nada

DECLARAR PROTOTIPOS DE FUNCIONES

Para utilizar una función, el compilador debe ser consciente de su presencia en el programa. En la

programación en C, los programadores normalmente primero escriben la función main() y luego las funciones

adicionales. Para avisar al compilador de la presencia de las funciones adicionales, se requiere declarar los

prototipos de funciones en el principio de programa antes de la función main(). Un prototipo de función está
compuesto por:

• tipo de resultado
• nombre de función
• tipos de parámetros
• un punto y coma (;)

El prototipo de la función main no necesita ser declarado.

float f (float, float);

Año 2021 41
Instituto Tecnológico del Computadoras
Comahue Electrónicas

/* no es obligatorio escribir los nombres de los parámetros. Este prototipo

informa al compilador: en el programa se utilizará la función f,

que utiliza dos parámetros de tipo float y devuelve el resultado del tipo

float. */

LLAMAR UNA FUNCIÓN

Mientras una función es definida y su prototipo declarado, se puede utilizar en culquier parte de programa.

Sin embargo, como la función main es 'raiz' del programa, no puede ser llamada de ninguna parte de

programa. Para ejecutar una función, es necesario escribir su nombre y los parámetros asociados. Vea los

siguientes ejemplos:

float resultado,a,b; // resultado,a,b,time deben coincidir con los tipos

// definidos

int time = 100; // en la declaración de las funciones f y wait 1

a = 10.54;

b = 5.2;

resultado = f(a,b); // Ejecutar la función f por medio de los parámetros a


y b

// El valor devuelto se le asigna a la variable


resultado

pausa_1(tiempo); // Ejecutar la función pausa_1 por medio de la


variable tiempo

funciónX(); // Ejecutar la función funciónX (sin parámetros)

Cuando se llama una función, el programa salta a la función llamada, la ejecuta, después vuelve a la línea
desde la que fue llamada.

Año 2021 42
Instituto Tecnológico del Computadoras
Comahue Electrónicas

1.7 CARACTERÍSTICAS PRINCIPALES DEL PREPROCESADOR

El preprocesador es un programa que procesa el código antes de que pase por el compilador. Funciona bajo

el control de las líneas de comando del preprocesador denominadas directivas. Las directivas del

preprocesador se colocan en el código fuente, normalmente en el principio del archivo. Antes de pasar por el

compilador, el código fuente se examina por el preprocesador que detecta y ejecuta todas las directivas del

preprocesador. Las directivas del preprocesador siguen a una regla de la sintaxis especial, empiezan por un

símbolo ‘#’ y no requieren ningún punto y coma al final (;).

DIRECTIVAS DEL PREPROCESADOR

La siguiente tabla contiene un conjunto de directivas del preprocesador frecuentemente utilizadas:

Las directivas del preprocesador se pueden dividir en tres categorías:

DIRECTIVAS FUNCIONES

#define Define una sustitución de macro

#undef Quita una definición de nombre de macro

#define Especifica un archivo a ser incluido

#ifdef Prueba para definición de macro

#endif Especificar el final de #if

#ifndef Prueba si una macro no está definida


#if Prueba las condiciones de compilar

#else Especifica alternativas cuando la prueba de #if falla

#elif Especifica alternativas cuando más de dos condiciones se necesitan

• Definiciones de macro
• Inclusiones de archivos
• Control de compilación

Ahora, vamos a presentar sólo las directivas del preprocesador utilizadas con más frecuencia. Sin embargo,

no es necesario saber todas ellas para programar microcontroladores. Sólo tenga en cuenta que el

preprocesador es una herramienta muy poderosa para los programadores avanzados en C, especialmente

para el control de compilación.

DIRECTIVAS DEL PREPROCESADOR PARA DEFINIR MACROS

Por medio de los macros es posible definir las constantes y ejecutar funciones básicas. Una sustitución de
macro es un proceso en el que un identificador del programa se sustituye por una cadena predefinida. El

Año 2021 43
Instituto Tecnológico del Computadoras
Comahue Electrónicas
preprocesador sustituye cada ocurrencia del identificador en el código fuente por una cadena. Después de la

sustitución, el código será compilado normalmente.

Esto significa que el código sustituido debe respetar la sintaxis del XC8. La acción se realiza por medio de la

directiva '#define'.

#define PI 3.14159 // Sustitución simple, PI será sustituido por

// el valor 3.14159 en todas las partes del programa

También puede utilizar los parámetros para realizar substituciones más complejas:

#define VOLUMEN (D,H) (((D/2)*(D/2)*PI))*H // Macro con parámetros

Entonces, en el código, la siguiente sentencia:

Tanque_1 = VOLUMEN (Diámetro,altura);

será sustituida por:

Tanque_1 = (((Diámetro/2)*(Diámetro/2)*PI)*altura;

Por medio de la directiva #undef es posible quitar una definición de nombre de macro. Así se especifica que
la substitución que se ha definido anteriormente ya no va ocurrir en el siguiente código. Esto es útil cuando
usted quiere restringir la definición sólo a una parte particular del programa.

#undef TANQUE // Quitar la definición del macro VOLUMEN

INCLUSIÓN DE ARCHIVOS

La directiva de preprocesador #include copia un archivo específico en el código fuente. El código incluido

debe observar la sintaxis de C para ser compilado correctamente.

Hay dos formas de escribir estas directivas. En el primer ejemplo, sólo el nombre de archivo se especifica, así

que el preprocesador lo buscará dentro del archivo include. En el segundo ejemplo, se especifica la ruta

entera, así que el archivo estará directamente incluido (este método es más rápido).

Año 2021 44
Instituto Tecnológico del Computadoras
Comahue Electrónicas

#include <nombre_de_archivo> // Se especifica sólo el nombre


del archivo

#include "C:\Ruta\nombre de archivo.h" // Se especifica la localidad

// exacta del archivo

Compilación condicional
Las directivas de compilación condicional se utilizan generalmente para facilitar las modificaciones y
compilaciones de programas fuente en diferentes entornos de ejecución.

Directivas #if, #elif, #else, y #endif

Las directivas condicionales #if, #elif, #else, y #endif se comportan igual que las sentencias condicionales
comunes en C. Si la expresión escrita después de #if es cierta (tiene un valor distinto de cero), las líneas de
código <sección_1> que siguen inmediatamente a la directiva #if se retienen en la unidad de compilación.
La sintaxis es:

#if constante_expresión_1

<seccion_1>

#elif constante_expresión_2

<seccion_2>

........

#elif constante_expresión_n

<seccion_n>

#else

<Seccion_final>

#endif

Cada directiva #if en el código fuente debe terminar por una directiva de cierre #endif. Puede haber
cualquier número de #elif entre las directivas #if y #endif, pero sólo se permite una directiva #else. La
directiva #else, si está presente, precede a #endif.

Año 2021 45
Instituto Tecnológico del Computadoras
Comahue Electrónicas

Directivas #ifdef y #ifndef

Las directivas #ifdef y #ifndef son condicionales especializadas para comprobar si un identificador está
definido o no. La siguiente línea:

#ifdef identificador

tiene exactamente el mismo efecto que #if 1 si el identificador está actualmente definido, o #if 0 si el
identificador está actualmente indefinido. La directiva #ifndef comprueba la condición opuesta
comprobada por #ifdef.

Año 2021 46

También podría gustarte