Hay tres elementos en el código de una aplicación PocketC: la línea del título, las variables globales y las funciones.
// 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.
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 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.
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ónEstos 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);
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 2Ahora, ¿cuáles son todos los operadores que pueden usarse en las expresiones, y cuál es su asociatividad? Buena pregunta:
|
|
|
---|---|---|
|
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.
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]
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
"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.
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. |
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.
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
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
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.
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.
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...
#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.