Lenguaje PocketC

Primero, PocketC es un lenguaje sensible a mayúsculas, lo que significa que tipear word no es lo mismo que Word.

Hay tres elementos en el código de una aplicación PocketC: la línea del título, las variables globales y las funciones.

El Título

El título es por lejos la parte más fácil del lenguaje PocketC. La primera línea del programa es dos barras // seguidas por el nombre del programa. Ejemplo:

// Mi Programa

Esto también es conocido como un comentario. En cualquier lugar que el compilador encuentra '//' en el programa ignorará el resto de la línea. Esto habilita a incluir texto explicativo en el programa. Hay otra forma de poner comentarios en el programa, rodeando el texto con '/* */'. Este método permite repartir el programa a lo largo de varias líneas. Ejemplo:

/* Este es un comentario multi-línea.
Todo el texto entre los
asteriscos será ignorado */
Los comentarios multilíneas no se pueden anidar. En otras palabras:
/* comentario 1 /* comentario 2 */ a=b+c; */
NO es válido.

Las Variables Globales

Las variables son cosas que se emplean para guardar valores en un programa. Hay cinco tipos de variables en PocketC:
 
Tipo Nombre Ejemplo
entero (32-bit, con signo) int 1, 2, 5, -789, 452349
coma flotante (32-bit) float -1.2, 3.141592, 5.7e-4
caracteres (8-bit, con signo) char 'a', 'b', '#'
cadenas string "Bob" "Katie" "Hello"
punteros pointer se discuten más tarde

Nota: Las constantes string pueden contener sólo 255 caracteres. Para guardar cadenas más largas en una variable usar suma: str = "cadena larga 1..." + "cadena larga 2...";

Las variables se declaran así:
tipo-variable nombre[,nombre...];

Estos son algunos ejemplos:

int miEntero, fila, columna;
string nombre;
float pi;
char c, ultimo, primero;
pointer ptr;
También es posible crear matrices de valores. Una matriz es una lista de valores que se guardan en una variable. Las matrices se declaran como variables normales excepto que el nombre es seguido por '[capacidad]' donde capacidad es la cantidad de ítems que la variable puede almacenar. Una declaración puede verse así:
int valores[10];
string nombres[7];
Por supuesto, matrices y variables normales se pueden declarar juntas:
int fila, valores[10], columna;
string nombre, colores[8];
También se pueden asignar valores por defecto para las variables:
int nueve = 9, ocho = 8, cero;
string dias[7] = { "Dom", "Lun", "Mar" };
En el caso de las matrices los valores iniciales deben indicarse entre llaves y separados por comas. Si el número se valores en la lista de inicialización es inferior a la capacidad de la matriz, los valores sin asignar toman los valores por defecto. En la matriz dias de arriba, los últimos 4 miembros son cadenas vacías ("").

Después se discutirán un poco más las variables.

Las Funciones

Las funciones son la parte más importante del programa porque contienen las instrucciones que lo hacen útil. Todas las funciones tienen un nombre y una lista de parámetros (que puede estar vacía) y se declaran de la siguiente manera:
nombre-funcion([tipo-parametro nombre-parametro,...]) { instrucciones }

Las instrucciones se discuten más tarde, pero por ahora estos son algunos ejemplos:

area(int ancho, int altura) {
   return ancho * altura;
}

cuadrado(float x) {
   return x * x;
}

cinco() {
   return 5;
}
Hay una función especial que todos los programas debe tener: main. La función main es la primera función que se ejecuta en un programa. Cuando la se sale de la función main el programa termina. La función main debe declararse sin parámetros:
// Mi Programa
main() {
   puts("Hola Mundo");
}
Las funciones pueden tener variables locales, que son variables que sólo pueden accederse dentro de la función que las declara. Las variables globales, en cambio, pueden accederse desde cualquier lugar del programa. Las variables locales se declaran de la misma forma que las globales, excepto que la declaración debe hacerse inmediatamente después de la llave de apertura de la función:
// Mi Programa
main() {
   string cadenaLocal;

   cadenaLocal = "Hola Mundo";
   puts(cadenaLocal);
}
Nota: Si se están creando matrices grandes, es mejor hacerlas variables globales en lugar de locales, si es posible.

Antes de avanzar es necesario hablar un poco acerca de las expresiones.

Expresiones

Una expresión es un conjunto de constantes, variables y llamadas a funciones vinculadas por operadores y paréntesis.

Una constante es cualquier valor que se ingresa directamente en el programa como: 5 5.4 'a' "String"

Un valor almacenado en una variable puede ser accedido desde simplemente tipeando su nombre: miEntero
Sin embargo, cuando esa variable es una matriz cada valor debe ser accedido individualmente por medio de su índice. Los índices válidos de una matriz son de 0 a n-1 donde n es la cantidad de valores de la matriz. Por lo tanto, una matriz declarada:

string nombres[4]
puede ser accedida:
nombres[0] = "primer nombre";
nombres[1] = "segundo nombre";
nombres[2] = "tercer nombre";
nombres[3] = "cuarto nombre";
Una llamada a función consiste en el nombre de la función, seguida por un paréntesis abierto, la lista de parámetros y el paréntesis de cierre:
area(5, 7);
cuadrado(8.9);
clear();
text(30, 55, "Game Over");
Una función sólo puede ser invocada después de su definición. Si se quiere llamar a una función antes de su definición se puede usar un prototipo de la función. Un prototipo de una función es una línea global (que no se encuentra dentro de otra función) que presenta el nombre y los parámetros de la función, y termina en punto y coma:
area(int x, int y);
cuadrado(float); // el uso de nombres de variables es opcional en la declaración
Estos tres elementos básicos de las expresiones pueden combinarse con los operadores:
5 + 7 - area(12, 34);
cuadrado(5) * pi;
"Hola, " + "Mundo";
Por supuesto, las llamadas a funciones pueden tener expresiones en ellas:
area(6+3, 8*9);
area(8 * cuadrado(4), 7);

Asignación

La asignación de variables es también otra forma de expresión. La asignación se hace en una de dos formas. Para una variable normal:
nombre = expresión

y para una matriz:
nombre[expresión-índice] = expresión

Estos son algunos ejemplos:

int miEntero, numeros[3];
string miCadena;
...
miEntero = 8;
miCadena = "Animaniacs";
numeros[0] = miEntero + 5;
numeros[2] = numeros[0] * 8;
Sin embargo, desde que PocketC no es estricto en los tipos, cualquier tipo de valor se puede asignar a cualquier tipo de variable, y el valor será convertido automáticamente:
miCadena = 95; // El valor de miCadena ahora es "95"
numeros[1] = "78"; // El valor de numeros[1] ahora es 78;
numeros["2"] = "2"; // Otro truco interesante. numeros[2] ahora es 2
Ahora, ¿cuáles son todos los operadores que pueden usarse en las expresiones, y cuál es su asociatividad? Buena pregunta:

Operadores

La tabla siguiente es en orden de precedencia, la menor primero.
 
Operador
Asociatividad
Descripción
=
derecha asigna el valor de la expresión de la derecha a la variable de la izquierda. Evaluado como la expresión de la derecha
||
izquierda 'or' ('o') lógico, 0 si falso, 1 si verdadero true
&&
izquierda 'and' ('y') lógico
|
izquierda 'or' por bits 
^
izquierda 'xor' por bits
&
izquierda 'and' por bits
== != < <= > >=
izquierda operadores relacionales. == (igual), != (no es igual), <= (menor o igual), >= (mayor o igual). Estos son evaluados como 1 si la expresión es verdadera o 0 en otro caso
<< >> izquierda operadores de rotación de bits. Los operandos deben ser char o int.
+ -
izquierda suma, resta (resta no puede ser usado con argumentos string)
* / %
izquierda multiplicación, división, módulo (no se pueden usar con string, y módulo tampoco con float)
- ! ++ - - ~ * [] () & @[]
izquierda - (negación), ! ('not' lógico), ++ (incremento), -- (decremento), ~ (negación por bits), [] (subíndice de matriz), () (derreferencia de puntero a función), & (dirección de ), @[] (acceso a caracteres de cadenas) De éstos, sólo 'not' lógico y @[] pueden ser usados en strings

Notas: No se ejecutan atajos lógicos en los operandos de || y &&
La asignación compuestas de operadores (+=, -=, *=, etc.) no es soportada.
La coma y el operador condicional (?:) no están soportados.

Acceso a caracteres de cadenas

Para obtener o cambiar un carácter individual dentro de una variable string, usar stringVariable@[indice]. El índice del primer carácter es 0. Se producirá un error en tiempo de ejecución si se intenta acceder a una posición posterior al final de la cadena. Ejemplo:
string str = "bob";
...
puts(str@[1]); // Imprime la segunda letra de
str@[1] = 'X'; // Cambia str de "bob" a "bXb"
Nota: esta función no puede usarse con punteros, tampoco se puede obtener la dirección de la cadena resultante. En otras palabras, ;as siguientes expresiones son inválidas: &str@[i], *pstr@[i], (*pstr)@[i]

Incremento / Decremento

Los operadores ++ y -- son especiales en que deben ser ubicados antes o después de una variable, y modifican el valor de la variable. ++ incrementa el valor de la variable en uno, mientras que -- lo disminuye en uno. Importante, notar que si ++/-- se ubica delante de la variable la expresión evalúa al valor de la variable antes de su incremento/decremento. Si se ubica detrás de la variable la expresión es evaluada al valor previo de la variable. Ejemplo:
int miEntero;
...
miEntero = 8;
puts(++miEntero); // Imprime "9" en el formulario de salida

miEntero = 8;
puts(miEntero++); // Imprime "8" en el formulario de salida, pero ahora miEntero es 9

Conversión y Tipificación automática

Así como en las sentencias de asignación, la conversión automática tiene lugar en toda parte de la expresión. Si dos argumentos de uno operador son de tipos diferentes, uno de los argumentos será promovido al tipo menos estricto. El orden de promoción es char a int a float a string. Entonces en la expresión:
"Resultado es: " + 5;
La constante 5 es primero promovida a string, y luego las dos cadenas son concatenadas. Esto puede tener algunos efectos indeseables. Por ejemplo, si se desea escribir una expresión y su resultado al formulario de salida, se podría hacer algo como:
puts("5 + 7 = " + 5 + 7); // Imprime "5 + 7 = 57"
Esto probablemente no es la salida deseada. En su lugar, se quiere la expresión evaluada primero y luego concatenada a la cadena. Los paréntesis pueden usarse para lograr esto:
puts("5 + 7 = " + (5 + 7)); // Imprime "5 + 7 = 12"
Resta un problema. Supongamos que se desea encontrar el valor en coma flotante de una fracción de dos enteros.
puts("7 / 5 = " + (7 / 5)); // Imprime "7 / 5 = 1"
Esta salida se debe a que ambos argumentos son enteros, entonces el resultado es entero. Para resolverlo, se puede tipificar uno de ellos a float:
puts("7 / 5 = " + ((float)7 / 5)); // Imprime " 7 / 5 = 1.4"
Esto fuerza al entero 7 a coma flotante antes de dividirlo por 5.

Instrucciones

Las instrucciones son las partes individuales que forman el cuerpo de una función. Estas son las instrucciones disponibles:
 
Instrucción Descripción
return; Vuelve inmediatamente de la función en ejecución (con un resultado por defecto 0 entero)
return expr; Vuelve inmediatamente de la función en ejecución, devolviendo el valor de la expresión expr
if (expr) instr Evalúa la expresión expr, si su resultado es verdadero (no cero o no cadena vacía), la instrucción instr se ejecuta, en otro caso instr se saltea, y la ejecución continúa
if (expr) instrA
else instrB
Evalúa la expresión expr, si su resultado es verdadero (no cero o no cadena vacía), la instrucción instrA se ejecuta, caso contrario instrB se ejecuta
while (expr) instr La expresión expr es evaluada. Si es verdadera (no cero o no cadena vacía), se ejecuta instr. El bucle comienza nuevamente, evaluando expr y ejecutando instr hasta que expr no es verdadera. Esto significa que instr nunca será ejecutada si expr es inicialmente falsa
do instr
while (expr)
Lo mismo que while excepto que la instrucción instr es ejecutada antes de que se evalúe expr. Esto garantiza que instr se ejecutará por lo menos una vez
for (inicio;cond;iter)
instr
La expresión de inicialización init se evalúa primero. La condición cond es evaluada. Si es verdadera, se ejecuta instr y la expresión de iteración iter es evaluada para continuar el bucle, en casi contrario, el bucle for finaliza. Nota: init se evalúa una sola vez.
break; Sale inmediatamente de las instrucciones de bucles while/do/for loop o instrucción switch que la contienen.
continue; Reinicia inmediatamente los bucles while/do/for que la contienen. En un bucle for, se evalúa la expresión de iter, seguida por la expresión cond y posiblemente por la instrucción instr.
switch (expr)
{ instrs }
Evalúa la expresión expr. Si instrs contienen una instrucción case con un valor coincidente, el código que sigue inmediatamente a la instrucción case es ejecutada hasta una instrucción  break o hasta que se alcance el final de la instrucción switch. Si no hay ninguna instrucción case coincidente, y existe una instrucción default en el switch, el código que sigue inmediatamente a la instrucción default se ejecuta hasta un break o hasta alcanzar el final del switch. Si no hay case coincidente con expr, y no hay instrucción default presente, todo lo que está dentro de la instrucción switch se saltea, y la ejecución continúa después de la llave de cierre. expr no debe evaluarse como un valor float.
case constante: Marcador dentro de instrucción switch. La constante debe ser char (case 'a':), int (case 3:), o string (case "manzana":) Si la constante coincide con la expresión expr de la instrucción switch, entonces el código que sigue inmediatamente a este marcador se ejecuta, hasta que se encuentra un break o hasta alcanzar el final de la instrucción switch.
default: Marcador opcional dentro de una instrucción switch. Si ninguno de los cases en el switch coincide con la switch expr, el código que sigue inmediatamente a este marcador es ejecutado, hasta encontrar un break o hasta alcanzar el final de la instrucción switch.
{ instrucciones } Una llave abierta seguida por una lista de instrucciones, terminada con una llave cerrada se considera como una instrucción simple.
expresión; Una expresión seguida por punto y coma se considera como una instrucción.


Ejemplos de Instrucciones

return
Visitemos un ejemplo previo de función para ver como funciona return.
cinco() {
   return 5;
}
Desde que el valor devuelto por la función cinco es siempre 5, se puede usar la función en cualquier lugar donde normalmente se pone la constante 5.
puts("Cinco es " + cinco()); // Imprime "Cinco es 5"
También, como return causa la salida inmediata de la función, se puede hacer esto:
cinco() {
   return 5;
   puts("Esto no se imprimirá");
}
y se obtendrá el mismo efecto.

if

menorA5(int x) {
   if (x < 5)
      puts("Menor a cinco");
   puts("Hola");
}
Si la función se invoca con un número inferior a 5, se imprimirá "Menor a cinco" seguido por la palabra "Hola". En otro caso, sólo la palabra "Hola" se imprime.

if ... else

menorA5(int x) {
   if (x < 5)
      puts("Menor a cinco");
   else
      puts("Mayor o igual a cinco");
}
Si esta función se invoca con un número inferior a 5, "menor a cinco" se imprime, en otro caso se imprime "Mayor o igual a cinco".

while

cuenta() {
   int x;

   x = 5;
   while (x > 0) {
      puts(x);
      x = x - 1;
   }
}
Este fragmento de código imprimirá los números de 5 a 1. Notar que las llaves fueron ubicadas encerrando dos líneas de código en el bucle while para hacerlas funcionar como una sola instrucción.

do ... while

cuenta() {
   int x;

   x = 6;
   do {
      x = x - 1; // también podría ser x--
      puts(x);
   } while (x > 0);
}
Este código (similar al ejemplo anterior) imprimirá los números de 5 a 0. El cero se imprime en esta ocasión porque la expresión x < 0 no es evaluada hasta después de cumplir el bucle.

for

salida() {
   string lista[4];
   int indice;

   lista[0] = "Cero";
   lista[1] = "Uno";
   lista[2] = "Dos";
   lista[3] = "Tres";

   for (indice = 0 ; indice < 4 ; indice++)
      puts(lista[indice]);
}
Este ejemplo imprimirá "CeroUnoDosTres". Cuando lo analizamos se ve que la matriz lista es primero inicializada. Luego se llega al bucle for. Primero, se evalúa el inicializador, poniendo el índice a 0. Después, se evalúa la condición indice < 4, que es verdadera, entonces se ejecuta el cuerpo del bucle, imprimiendo "Cero". La expresión de iteración es entonces evaluada, incrementando indice en uno. Esto continua hasta que el indice es igual a 4, punto en el cual se sale del el bucle sin ejecutar el cuerpo otra vez.

break

cuenta() {
   int x;

   x = 5;
   while (x > 0) {
      if (x == 1)
         break;
      puts(x);
      x = x - 1;
   }
}
En esta pieza de código levemente más compleja, la cuenta avanza como lo haría normalmente, imprimiendo "5432". Sin embargo, cuando x llega a 1, se ejecuta break que corta el bucle while tempranamente, antes que se imprima el 1.

continue

cuenta() {
   int x;

   x = 6;
   while (x > 1) {
      x--; // Primero hace la resta
      if (x == 3)
         continue;
      puts(x);
   }
}
En este ejemplo, la salida es "5421". Cuando x alcanza 3, se ejecuta continue, pasando la acción al inicio del bucle, saltando la instrucción puts.

switch, case, default

que_numero(int x) {
   switch (x) {
      case 1:
         puts("x == 1\n");
         break;
      case 2:
      case 3:
         puts("x == 2 ó x == 3\n");
         break;
      case 8:
         puts("x == 8\n");
      case 10:
         puts("x == 8 ó x == 10\n");
         break;
      default:
         puts("x no es 1,2,3,8, ni 10\n");
   }
}
La función que_numero recibe un valor e imprimirá una o dos notas acerca de él. Si el valor es 1, case 1 se ejecuta y break salta al final del switch. Si el valor es 2 ó 3, el código que sigue a case 3: es ejecutado. Si el valor es 8, ambos "x=8" y "x=8 or x=10" se imprimen, porque no hay un break antes de case 10:, esto en inglés se denomina "fall-through". Si ninguno de los casos coincide con el valor recibido por la función, el código que sigue a default: es ejecutado.

Punteros (pointers)

Nota: Los punteros son un tema avanzado, que debería ser tratado una vez que el usuario esté familiarizado con todo el resto de los conceptos de programación.

Todas las variables se alojan en alguna dirección de memoria. Un puntero es una variable que da una referencia a otra variable almacenando su dirección de memoria.

Hay dos operadores primarios que se usan con los punteros, * y &. El operador * derreferencia el puntero. Un puntero derreferenciado actúa exactamente como el dato al que apunta. El operador & devuelve la dirección de una variable dada. Para ilustrar:

pointer p, q;
int i;

main() {
  i = 5;
  p = &i;   // Asigna la dirección de 'i' al puntero 'p'
            // Ahora tipear '*p' es lo mismo que tipear 'i'
  puts(*p); // Imprime el valor de 'i'
  *p = 7;   // Asigna 7 a 'i'
  q = p;    // Asigna el valor de 'p', que es la dirección 'i', a 'q'
            // Ahora, tipear '*q' es también lo mismo que tipear 'i'

  // Cosas que no se deben hacer
  p = 8;    // MAL! No asignar un valor constante a un puntero
  *i = 9;   // MAL! No intentar derreferenciar una variable que no es puntero
}
Un puntero también puede ser usado para obtener la dirección de una función (pero NO de las funciones nativas!). A diferencia de las variables, sin embargo, el operador & NO se usa. Llamar a una función a través de un puntero es un poco complicado. Primero, el código es feo. Segundo, no se puede hacer control de errores sobre los parámetros, por lo tanto no se realizan las conversiones de tipos ni se confirma el número de argumentos. Por ejemplo:
funcion(int x) { return 5*x; }

main() {
  int resultado;
  pointer punt;

  punt = funcion; // Toma la dirección de la función
  resultado = (*punt)(5); // Llamada a la función (feo)

  // Cosas que no hay que hacer
  resultado = (*punt)("5"); // esto no funcionará porque la cadena
                            // no es convertida a entero
  resultado = (*punt)(5,7); // esto se compilará, pero resultará en
                            // corrupción de la pila porque
                            // se usa un número incorrecto de argumentos
}
Punteros y matrices
Los punteros y matrices son claramente similares. Los punteros pueden usar el operador [], y una variable matriz (cuando no se usa con []) es la dirección de memoria del primer elemento. Por ejemplo:
int matriz[5];
pointer p;

main() {
  p = matriz; // Asigna la dirección del primer elemento de 
              // 'matriz' a 'p'
  *p = 7;     // Asigna 7 a matriz[0]
  p[1] = 8;   // Asigna 8 a matriz[1]
}
Esto habilita a los punteros a matrices a ser pasados como parámetros de funciones. Esto también permite al usuario implementar su propia versión de matrices bidimensionales. Por la creación de una matriz de punteros, cada uno de los cuales es puntero a una matriz (o a una parte de ella), se puede simular una matriz bidimensional.
int matriz[100];
pointer dosd[10]; // después de inicio(), este puede ser tratado como
                  // una matriz 10x10

inicio() {
  int i;
  for (i=0;i<10;i++)
    dosd[i]=matriz + i*10; // Aritmética de punteros
}

main() {
  int x, y;
  inicio();
  for (x=0;x<10;x++)
    for (y=0;y<10;y++)
      dosd[x][y]=x * y;  // Hace array[x*10 + y] = x*y
}
Aritmética de punteros
Los valores de los punteros se pueden usar en un limitado número de expresiones. Se puede sumar y restar de un puntero (y así, usar operadores de incremento y decremento también). Cuando se suma 1 a un puntero, el puntero apunta al siguiente valor en la memoria. Similarmente, cuando se resta 1 a un puntero, éste apunta al valor previo de la memoria. Se debe tener precaución al usar aritmética de punteros, porque derreferenciar una dirección de memoria inválida causará un error en el programa.

Include

Usando la sentencia include se vuelve posible escribir programas cuyo código supere los actuales 4K máximos que tiene el MemoPad, o crear memos con código de uso frecuente. El contenido del archivo incluido es insertado funcionalmente en la línea que contiene la sentencia include.
Notas: El archivo mencionado en include puede comenzar con '/$' en lugar de '//' para que no se vea en el formulario de compilación. El archivo include puede ser tipo doc, sin embargo, PocketC buscará el memo correspondiente antes de buscar entre los archivos doc. En PocketC Desktop Edition, todos los \ en la ruta deben anteponerse con el carácter de escape.

Ejemplo

/$ MisFunciones
por5(int x) {
   return x*5;
}
Otro memo:
// Mi Programa
include "MisFunciones"

main() {
   int y;
   y = por5(7);
   puts(y); // Imprime 35
}
El compilador ve esto como:
// Mi Programa
por5(int x) {
   return x*5;
}
main() {
   int y;
   y = por5(7);
   puts(y); // Imprime 35
}
Nota: include sólo se puede usar a nivel global, es decir que no se puede usar include dentro de una función.

Uso de librerías nativas

Para usar las funciones definidas por una librería nativa primero hay que decirle al compilador que cargue la librería usando la sentencia library. Las funciones se usan como si fueran funciones normales. (NO HAY que usar library con MathLib. Las funciones de MathLib están disponibles como las funciones propias de PocketC.)

Ejemplo:

// Mi Programa
// PocketCLib define times5(int)
library "PocketCLib"

main() {
   int x;
   x = times5(7);
}
Para información de cómo crear librerías nativas ver Nativas.html.

Caracteres especiales

Hay dos formas de agregar caracteres especiales a una cadena. La primera es agregándola por número, como en:
str = "Este es un cuadrado: " + (char)149;
El otro método es a través de secuencias de escape. Son válidas las siguientes secuencias:
 
Secuencia de escape \\ \' \" \n \t \x
Interpretación \ ' " nueva línea tabulación carácter especificado por los siguientes dígitos hexadecimales. Ejemplo: '\x95' es el carácter block (decimal 149)

Entonces, para crear una cadena que contenga comillas:

str = "Ella dijo \"Lo siento,\" pero fue muy tarde...";
puts(str); // Imprime: Ella dijo "Lo siento," pero fue muy tarde...

Preprocesador

Como en C, PocketC contiene un preprocesador, el cual permite al programador definir macros y compilar condicionalmente una sección de código basándose en la presencia de una determinada macro.

#define macro datos_macro

Una macro es un identificador que cuando es leído por el compilador se reemplaza por los datos de la macro. Los datos de la macro pueden ser cualquier número de instrucciones (incluso ninguna). Los datos de la macro se terminan en el siguiente salto de línea. Por ejemplo:

#define calculo 5 * (x + 7)

main() {
  int x, y;
  x = 9;
  y = calculo;
  puts("y = " + y);
}
El compilador ve esto como:
main() {
  int x, y;
  x = 9;
  y = 5 * (x + 7);
  puts("y = " + y);
}
#ifdef macro

Si la macro fue previamente definida (incluso definida como nada, sin datos)  la sección de código entre ella y el siguiente #endif es compilada. En otro caso, el compilador ignora esa sección de código.

#ifndef macro

Si la macro NO fue previamente definida la sección de código entre ella y el siguiente #endif se compila. En otro caso el compilador ignora esa sección de código.

#endif

Marca el final de una sección de código precedida por un #ifdef o #ifndef.

#else

Ubicado en el medio de un bloque #ifdef o #ifndef, el código entre #else y #endif se compila sí y sólo si el bloque previo no se compiló.

#undef

Elimina la definición de una macro previa, Si la macro nunca se definió no hace nada.

Ejemplo:

#define DEBUG

main() {
#ifdef DEBUG
  puts("En modo de depuración");
#else
  puts("En modo normal");
#endif
}
El compilador predefine estas macros:
#define __PKTC__ 1
#define __PKTC_PALM__ 1
#define __PKTC_PRC__ 1 // sólo si PocketC Desktop Edition está creando un archivo .prc

El siguiente paso es ver las Funciones propietarias para ver cuáles  le pueden resultar útiles.