[Tip] Simple serializador para C
El otro día necesitaba serializar unos mensajes para una aplicación cliente/servidor que estaba desarrollando.
En un primer momento intenté buscar alguna librería que me diera lo que estaba buscando. Probé con GLib pero no tiene nada para serializar mensajes (se pueden serializar objectos con GObject).
Decidí hacer una implementación sencilla de lo que tenia mente.
Así fue como desarrolle el siguiente código:
// PUSH/POP Simple net serializer
#define PUSH_BEGIN(buff,l) { int i = 0; int lm = (l); char* b = (char*) (buff);
#define PUSH_BYTE(b) assert(i+1 <= lm); b[i++] = (b);
#define PUSH_SHORT(s) assert(i+sizeof(short) <= lm); { short* ps = (short*) &b[i]; *ps = htons((s)); i += sizeof(short); }
#define PUSH_INT(s) assert(i+sizeof(int) <= lm); { int* ps = (int*) &b[i]; *ps = htonl((s)); i += sizeof(int);}
#define PUSH_SZ(s) { int l = strlen(s); assert(i+l+sizeof(int) <= lm); PUSH_INT(l); memcpy(&b[i],s,l); i += l; }
#define PUSH_END(l) (l) = i; }
#define POP_BEGIN(buff,l) { int i = 0; int lm = (l); char* b = (char*) (buff);
#define POP_BYTE(b) assert(i < lm); (b) = b[i]; i++;
#define POP_SHORT(s) assert(i+sizeof(short) <= lm); (s) = ntohs( *((short*) &b[i]) ); i += sizeof(short);
#define POP_INT(s) assert(i+sizeof(int) <= lm); (s) = ntohl( *((int*) &b[i]) ); i += sizeof(int); printf("val: %i\n",(s));
#define POP_SZ(s) { int l = 0; POP_INT(l); assert(i+l <= lm); (s) = (char*) malloc(l+1); memcpy(s,&b[i],l); s[l] = '\0'; i += l; }
#define POP_END() }
A poco que examinéis el código comprobareis que se trata en realidad de una macro.
Esta implementación cumple mis requisitos:
- Eficiente. Las macros se resuelven en el código y es equivalente a las funciones inline.
- Sencillo. La nomenclatura de las macros auto-describe su propósito, PUSH_x para agregar y POP_x para extraer.
- Poca sobrecarga. La mayoría de los serializadores incluyen el tipo de dato del dato agregado. En este caso el tipo está bien determinado e identifica unívocamente el tipo de mensaje.
- Depuración. Gracias a los
assert()puede comprobar que el tamaño de los bufferes para el envio/recepción de los mensajes es correcto durante la fase de pruebas. - Portable. El uso de
ntoh/htonposibilita la independencia de arquitectura big/little endian.
Así por ejemplo si queremos mandar el siguiente mensaje: “bote de champú”,3,”suave”, éste seria el código resultante:
#define MAXBUFFER 1024
char buff[1024];
int len;
PUSH_BEGIN(buff,MAXBUFFER)
PUSH_SZ("bote de champu")
PUSH_INT(3)
PUSH_SZ("suave")
len= PUSH_END()
Seguro que no es la mejor implementación y es probable que tenga algun bug. Si ves algo que no te gusta o bugs, te animo a que lo modifiques y comentes. Libero el código bajo licencia BSD.
Translate to:English
Menéame
Twitter
Sicur ‘10
El pasado martes estuve en SICUR 2010 en el Ifema. No es una feria de visita obligada para mi, pero asistí para acompañar a unos buenos amigos.
No voy a hablar mucho sobre SICUR porque no es un terreno donde me sienta cómodo. Me voy a limitar a una pequeña descripción y dar mi opinión sobre un aspecto que creo que es denominador común de los expositores y feriantes. Creo que si queréis conocer mejor SICUR, su página oficial hará una mejor presentación que la mía.
SICUR es una feria destinada al sector de seguridad. Pregunta rápida, ¿qué encuentro en SICUR? Cámaras de vigilancia (diurna, nocturna, FLIR, vía IP), cajas de seguridad, control de accesos, armas (semiautomáticas, largo alcance, cuerpo a cuerpo), detectores de metales, etc.
El aspecto común que me llamo la atención lo llamo “mi secreto“. La mayoría de las empresas que vi optan por fabricar productos cerrados (tanto hardware como software). Alguna que otra, sólo se limitan a soltar un tímido “open framework” sin entrar en más detalles.
El principal argumento que dan estos fabricantes para defender sus arquitecturas cerradas es la seguridad. Todos sabemos que el verdadero motivo es que tú no te puedas ahorrar algún dinero sustituyendo alguna pieza por otra un poco más económica de otro fabricante. Lo cual es harina de otro costal.
Voy a reflexionar sobre el mito “cerrado = mayor seguridad“.
Poco (o nada) me acuerdo del esquema de AES para el cifrado de datos que aprendí en la asignatura de criptografía. No obstante me gusta quedarme con los fundamentos y las reflexiones sobre éstos. Mi profesor decía que: “la seguridad NO se encuentra en el algoritmo criptográfico, sino en la clave“. Esa es la clave. En la antigüedad reciente y pasada, el algoritmo o el diseño del “cacharro” era protegido, ya que su conocimiento significaba una posible rotura de la seguridad. Cuando comprendieron esto, optaron por realizar concursos donde se presentaban algoritmos que eran estudiados por todos (a la vez que depurados) y donde la clave encerraba la seguridad.
La mayoría de los fabricantes usan cifrado AES de 128 bits. Este algoritmo con esta longitud de clave sigue sin poder romperse a día de hoy (ya veremos que ocurre si el computador cuántico sale a la luz algún día). Para mi esto es garantía de seguridad (siempre que la clave generada no sea leak). Creo que aumentaría sus ventas si dieran posibilidad de incluir hardware/software de terceros en su arquitectura. Como comenté en un párrafo anterior, ya están empezando a hacer algunos sistemas abiertos que cumplen este anhelo.
Volviendo a la feria, poco I+D+i encontré. En el campo más cercano a mi. la vigilancia mediante cámaras locales o IP, “poca nueva chicha”. Me recorrí la zona más propensa a la innovación, la periferia del pabellón. En esta zona suele encontrarse stands de poco más de 15 m2 donde empresas pequeñas se arriesgan apostando por la innovación. Fue en un de estos sitios donde vi una plataforma que sí hace uso de las doctrinas del Computer Vision. El sistema era capaz de reconocer a individuos sospechosos, enumerar a dichos sujetos y hacerles un tracking automático (moviendo la cámara y haciendo zoom). Esto no es nada nuevo pero fue de lo poco que vi en ejecución en SICUR. Espero que con los nuevos chipsets de GPU, CUDA/OpenCL tenga mucho que decir en este campo.
Concluyo animando al lector a que visite la feria (si así lo estima conveniente) y dé su opinión.
Translate to:English
Menéame
Twitter
[Tip] Si se cae tu servidor DNS
Seguramente más de una vez has tecleado alguna página web y no ha entrado. Sin embargo, programas como el Messenger siguen funcionando.
¿qué está pasando? Muy posiblemente tu servidor DNS se ha caído.
No voy a entrar en detalles sobre los servidores DNS, simplemente voy a decir que son como una guía de teléfonos, resuelven nombres a direcciones IPs.
La manera más fácil de comprobar si tu servidor DNS sigue en pie es hacerle un ping, y ver si responde. Si sale un error tipo timeout o host/net unreacheable, efectivamente, está caído.
La solución más sencilla para volver a tener “Internet” (Internet sigue habiendo pero sin “agenda”), es especificar un nuevo servidor DNS mientras el nuestro se recupera (o recuperan). No hace más de unos cuantos meses era necesario memorizar algún DNS público, como el de Telefónica, Orange, ya.com, etc o utilizar otro equipo con Internet para buscar otra dirección DNS (o la propia del equipo).
Un día los chicos de Google pensaron que tener una dirección IP fácil de recordar, podría ayudar y mucho. Crearon el servidor DNS 8.8.8.8. Bastante sencillo de recordar, ¿no? (también disponen del 8.8.4.4).
Ahora sólo nos basta cambiar la dirección IP del servidor DNS caído por el de Google (u otro). En GNU/Linux (independientemente de nuestra distribución favorita), para la resolución de nombres públicos se usa el fichero /etc/resolv.conf. Normalmente este fichero es generado en el arranque por los scripts de red, luego podemos sobreescribirlo sin problema (si no es tu caso, ya sabes, backup). Con la siguiente linea (en Ubuntu) ya tendríamos nuestro servidor DNS de emergencia.
sudo echo "nameserver 8.8.8.8" > /etc/resolv.conf
Bueno espero que os sirva y no os quedéis sin “Internet”.
Translate to:English
Menéame
Twitter
Exportar e importar firmas digitales en GNU/Linux
Ando liado con trámites tributarios.
La firma digital es una de esas cosas de la tecnología que te hace preguntar: ¿cómo hemos podido vivir sin esto? Puede parecer una exageración, pero el hecho de poder hacer trámites de la Agencia Tributaria o de asuntos relacionados con tu ayuntamiento desde el PC de tu casa, no tiene precio. No tienes que pedir horas en tu trabajo, no haces colas, es muy rápido e indoloro. La pega que tiene es el soporte técnico. Tiene un soporte horrible. A pesar de haber cursado asignaturas en la carrera donde se ven los certificados, firmas y demás engendros digitales, tengo que reconocer que me cuesta un poco tanto “palabroto” técnico (X.509, PKCS12, PEM, etc). No quiero imaginar a mi madre haciendo esto.
El asunto es que ayer probé a usar mi firma digital y comprobé que estaba caducada (tenia que haberla renovado por la propia web meses antes de que caducara). Era necesario volver a solicitar una nueva. El proceso es sencillo:
- Entras en la web de CERES y solicitas un certificado de usuario (no sé como irá el tema de los DNIe)
- Te piden el NIF y te generan un código que tienes que guardar.
- Es necesario que te presentes con el DNI en la oficina más cercana que expida firmas digitales (hay un buscador que calcula la más cercana a tu dirección).
- Te presentas en la oficina, enseñas el código, el DNI y firmas el contrato de adhesión.
- Vuelves a tu casa, te conectas a la web de CERES y especificando tu DNI y el código previamente asignado, ¡voila! el certificado se instala en tu equipo.
El proceso anterior es la teoría. En la práctica hay una letra pequeña que cuando se lee ya es tarde. Cuando vuelves de firmar el contrato y pinchas en la opción de descarga, aparece un párrafo que dice: “El certificado se descargará en el navegador y PC con el que solicitó el certificado“. El gráfico “Uppss” aparece en mi cabeza: “PC el de siempre…, navegador web… Google Chrome Beta para GNU/Linux“. Por supuesto, Chrome todavía no está preparado para gestionar este tipo de certificados personales. La cuestión es que, aún sabiendas de que posiblemente no iba a funcionar, pulsé en “Descargar“. Ni el puntero del ratón se movió. Indagando un poco en cómo gestiona Chrome los certificados, me doy con una utilidad muy potente, certutil.
Resulta que Chrome guarda los certificados bajo la ruta ~/.pki/nssdb/ en varios ficheros que suelen ser bases de datos SQLite3.
Uno de los cometidos de certutil es mostrar los certificados que existen en esa base de datos. Para ser exacto, esta línea me mostró todos los certificados
certutil -L -d sql:~/.pki/nssdb
Qué sorpresa me lleve cuando comprobé que la última línea correspondía con mi certificado personal. Resultó que cuando le di a descargar el certificado, Chrome lo guardo ahí.
Genial. Si el certificado está ahí, seguro que puedo exportarlo e importarlo en Firefox (que sí esta soportado). ¿Cómo?
Después de buscar un poco, encontré una opción para exportar certificados para LDAP que usaba la utilidad pk12util.
El nombre de esta utilidad viene del formato de documento, PKCS12, se trata simplemente de una forma de codificar un certificado digital que contiene tu clave pública y privada mediante una contraseña para que nadie te pueda usurpar. Era necesario por tanto establecer una contraseña y guardarla en un archivo (por ejemplo, /tmp/clave”). La sintaxis que usé para exportar mi certificado fue:
pk12util -d sql:~/.pki/nssdb -n "<nombre-certificado-mostrado-por-certutil>" -o <nombre-fichero-salida>.p12 -w /tmp/clave -k /tmp/clave
El mensaje de salida fue: pk12util: PKCS12 EXPORT SUCCESSFUL
Faltaba la prueba de fuego: ¿funcionará en Firefox?¿cargará por fin la Oficina Virtual?
Podéis imaginar que sí
Firefox, después de solicitarme la clave que especifiqué en “/tmp/clave”, importó sin problemas el certificado y la oficina virtual por fin entró.
No sé si este post servirá para alguien o no. Por lo menos dejó registró en la web por si algún día necesito recurrir a estos comandos (ya sabéis eso de volver a tropezar con la misma piedra).
He obviado algunos detalles sobre la importación y solicitud de firma digital. En la Web hay bastante información al respecto. No obstante, si teneis dudas, preguntad
Translate to:English
Menéame
Twitter
PlayLab ‘10
El pasado domingo 7 de Febrero asistí a la clausura y presentación de proyectos del PlayLab.
PlayLab es un taller de videojuegos experimentales o conceptuales con mucho arte de por medio.
Ha sido posible gracias al esfuerzo de Flavio Escribano, los colaboradores y la financiación de PlayStation.
El desarrollo de los proyectos se ha realizado en MediaLab Prado (al lado del Caixa Forum Madrid).
De los ocho proyectos seleccionados me han gustado como profesional y usuario:
- The Pingus Plasticiline Experience
La experiencia se basa en el clon GPL del clásico título “Lemmings“, Pingus. El gameplay es sencillo. Primero se diseña un mapa sobre una cartulina usando unos patrones que especifica el tipo de material (pared, suelo, agua, etc). Luego empieza la partida. Usando plastilina de colores se definen las diferente acciones a realizar por nuestros pingus. Así por ejemplo se use puede usar un círculo rojo para indicar a los pingus que escalen una pared.
Desde el punto de vista técnico, se requiere de un proyector, un PC y un pequeño software de reconocimiento (intuyo que basado en color segmentation).
Creo que este proyecto puede tener una buena acogida por los centros de día, colegios e institutos, dada su alta interacción y la necesidad de resolver puzzles (grandes estimuladores del cerebro).
- Homeward Journeys
A este proyecto lo llamaría: Teatro 2.0.
¿os acordáis del Monkey Island, Maniac Mansion o algún Indiana Jones? Pues eso en carne y hueso. La idea es hacer un teatro donde se le da opción al espectador de guiar al personaje seleccionando diferentes acciones a realizar. Una correcta escenografía ayuda al espectador mostrando la información relevante sobre fondo (como si de un OSD se tratase).
Sin duda este proyecto puede atraer a los más jóvenes a las butacas de los viejos teatros. - Audiogames
¿alguna vez has intentado salir de un laberinto sin poder ver? Este es el objetivo de Audiogames. El jugador sólo se orienta en el laberinto por medio de sonidos que provienen de unos altavoces distribuidos estratégicamente. El prototipo está desarrollado usando OpenCV para PureData y el Blender Engine.
Este proyecto tiene un gran potencial social, desde la concienciación de los videntes, a la guía por espacios públicos para invidentes.
- The Mexican Standoff
Sin duda el proyecto más heavy. Se trata del típico duelo de los westerns donde el ganador (o el que sobrevive) es aquel que primero dispara usando su mente. Para ello se usa un dispositivo capaz de medir la actividad cerebral. El primer jugador que se “relaja” lo suficiente, gana. No me gustaría competir contra una profesor de Yoga. El escenario virtual es una escena específicamente diseñada del Counter Strike Source.
Creo que este juego causaría furor entre los colegas de veinte/treinta años. Tiene pinta de ser superadictivo.
Espero que estos proyectos que acaban de germinar tengan un largo recorrido y alcancen sus propósitos.
Hay que agradecer el esfuerzo de todos los responsables: ArsGames, MediaLab-Prado y PlayStation por hacer posible esta gran oportunidad.
Espero impaciente la nueva edición del PlayLab
Actualización: ya se encuentran disponibles los vídeos de las presentaciones de los proyectos. Merece la pena dedicarle un vistazo.
Translate to:English
Menéame
Twitter
Android 101
Desarrollando para Android ando.
He empezado a desarrollar para Android por pura necesidad. El origen de dicha necesidad se encuentra en el browser de Android. Os lo explico.
Resulta que haces una semanas empecé el desarrollo web para Android. Estaba haciendo un formulario de registro y es necesario que el usuario pueda subir una fotografía. Qué sorpresa me llevé cuando al previsualizar el formulario, el campo destinado para seleccionar el fichero mostraba: “Uploads disabled“.
¡Qué chasco! Sabía que el Mobile Safari de iPhone/iPod no lo permitía… pero ¿tú?¿Android?
En fin, después de buscar en opciones de seguridad sin éxito y ver los cientos de “WTF!” que circulan por los foros del androide, comprobé que iba a necesitar a empezar a desarollar con el Android SDK. Android me ha obligado ha usar su SDK, desarrollar una aplicación y subirla a su Android Market. Me cuestiono el porqué de la deshabilitacion de los uploads. ¿sera por cuestiones de capacidad? ¿O más bien cuestiones de marketing?
Lo primero que hice fue entrar en la web de Android, bajarme su SDK, bajarme el Eclipse, preparar el entorno, hacer un proyecto “hello world” y ejecutarlo en el emulador.
Para hacer estos pasos no me llevo más de 2 horas y media contando con la descarga. El tamaño aproximado de mi directorio de trabajo ronda el giga (Java+Eclipse+Android).
Para estar más tranquilo psicológicamente, queria comprobar que el hello world se ejecutaba sin problemas en mi HTC Magic. Cogí un cable comercial USB-micro, lo conecté, le dí a “Ejecutar” y de repente apareció un dialogo con el número de serie mi smartphone. Se ejecutó sin problemas.
Antes de empezar a codificar ni una línea si quiera, tenía muchas dudas sobre el proceso de subida y aprobación de aplicaciones al Android Market. Resulta que el proceso no es tan complejo ni largo como el análogo para el App Store. Eso sí, el pagar no te lo quita nadie. Unos $25 USD (aprox. 17€). La primera vez que pago algo a Google en 10 años. La verdad es que no me parece caro, menos si lo comparramos con los $299 del App Store de Apple.
Iba siendo hora de teclear algo.
Hay una cosa que tengo clara en todos los desarrollos (libres) que hago: no reinventar la rueda.
El propósito de mi aplicación es bien sencillo: formulario de registro de usuario con subida de fotos, pantalla de login y ejecución del navegador web con una redirección.
Decidí que la mejor forma de desarrollar la aplicación a la vez de ir aprendiendo Android, era hacer un desarrollo por milestones que se pueden resolver en sesiones de una hora.
Estos fueron los hitos fijados:
- Modelo de programación.
- Botón con manjeador.
- Hacer un Http Post.
- Hacer un Http Post multipart/form-data.
- Acceso a la galeria de fotos.
- Layout de aplicacion.
- Creación de sub-actividades.
- Lanzar el browser.
- Depuración.
- Subida al Android Market.
Hoy voy a tratar sólo el primer hito. Considero que es el más importante.Lo primero que hace el gestor de aplicaciones es examinar el AndroidManifest.xml. Como indica la extensión, se trata de una “Declaración de Intenciones” escrito en XML.
Dos conceptos aparecen en el párrafo anterior:
- Activity: es la clase asociada a una pantalla de la aplicación. Al menos es necesaria una clase Activity. Al ejecutar la aplicación, Android se encargará de instanciar una clase de la activity raiz. Android sigue el modelo MVC, modelo-vista-controlador. Respecto a la Activity, tenemos por un lado un fichero XML donde se especifica la UI (vista) y un fichero Java donde se encuentra la clase de nuestra Activity que hereda de Java. El modelo de desarrollo ideal, consiste en crear un layout con todos los elementos de interfaz que usemos y gestionar su callback en la clase asociada.
- Intent: para mi es la clase estrella, la clase “no reinventar la rueda”. Lo entiendo como un modelo “ONG”. Una aplicación declara una serie de habilidades que puede hacer en favor de otra aplicaciones mediante Intents. Se entiende mejor con un ejemplo. Si mi aplicación necesita obtener la ruta de un imagen dentro de la Galeria, basta con lanzar un Intent, especificando nuestro anhelo “Eh,alguien puede sacar el diálogo de la galeria y devolverme la ruta del fichero que el usuario ha clickeado” (es casi algo así).
Hay otros tipos de clases, como servicios en background, pero creo que estas son las más usadas.
Os dejo el enlace de un ejemplo donde se usan.
En la siguiente entrega, empezaremos a darle forma a la aplicación: “Hello, uploads“.
Translate to:English
Menéame
Twitter
[Tip] Direcciones IPs virtuales
Seguramente sabréis que la dirección IP es una entidad lógica y que la dirección MAC es un identificador hardware único asociado a un dispositivo.
Esto quiere decir que, si tenéis una tarjeta de red, ésta tendrá una dirección MAC puesta por el fabricante (tipo: XX-XX-XX-YY-YY-YY) y una dirección IP asignada a mano o mediante DHCP.
En GNU/Linux es posible asociar múltiples direcciones IPs a una única interfaz de red. A esto se le conoce como interfaces virtuales. Se usa principalmente para poder acceder a diferentes redes IP simultáneamente.
El procedimiento es sencillo, supongamos que actualmente nuestra NIC eth0 tiene la dirección 192.168.1.10/24 y queremos poder comunicarnos con la red 10.10.10.x/24 usando la dirección IP 10.10.10.10. Basta con la siguiente línea:
sudo ifconfig eth0:1 10.10.10.10
Podemos añadir más interfaces virtuales sólo modificando el ethX:Y, donde X es la interfaz e Y el numero de dirección virtual.
Espero que si os encontrais en esta situación, os sirva este tip
P.D. Sé que en Windows es posible tener interfaces virtuales, pero nunca se ha presentado la necesidad.
Translate to:English
Menéame
Twitter
[Tip] Hoard, gestor de memoria dinámica multiprocesador
Si alguna vez habéis programado en C/C++, casi seguro que habréis tenido que reservar y liberar memoria con malloc/free o new/delete.
Estas operaciones internamente gestionan la memoria de proceso asignando slices de memoria libre. Esta gestión tiene un coste u overhead de procesamiento. Si nuestro programa es multihebrado este procesamiento se vuelve más complejo.
Emery Berger, profesor de la Universidad de Massachusetts, se dio cuenta que la implementación de malloc()/free() de la stdlib, se podía mejorar y empezó a trabajar en Hoard.
Hoard ha sido comparado con otros gestores de memoria y ha salido victorioso en varios tests. Estos screenshots arrojan los detalles.
Esta disponible para GNU/Linux, Windows y Solaris bajo licencia GPL.
La gran ventaja de usar Hoard, es que no es necesario recompilar un programa (salvo que sea estático) para aprovechar sus bondades, ya que usa la misma sintaxis que las funciones originales de stdlib.
Para usarlo en GNU/Linux basta con indicar a LD, que precargue la libreria de Hoard:
export LD_PRELOAD=/ruta-a-libreria/libhoard.so
Ahora sólo tenemos que ejecutar nuestro programa con esta variable de contexto.
Es un buen gestor de memoria para aquellos desarrollos de servidores multithreads.
Translate to:English
Menéame
Twitter
ideone.com: Programa, compila y ejecuta in the cloud
La nube está de moda. También entre los desarrolladores. En este caso, más que almacenar nuestros datos, lo que se ofrece es ejecutar y escribir nuestros programas.
Este servicio se llama ideone.
El nombre es auto explicativo de su misión: un solo IDE y vía Web.
La idea es sencilla: desarrollar, compilar y ejecutar programas o scripts. Existe un gran catálogo de lenguajes disponibles: C, C++, Java, awk, php, etc. Hay un total de 43 lenguajes disponibles. Incluso cuenta con GoLang (recordemos que la web oficial de GoLang también cuenta con un compilador/ejecutor online).
Muchas preguntas o dudas se me ocurren acerca de este web service.
Se puede por ejemplo establecer un buffer de texto como entrada del programa. Trata el texto del textarea como el buffer de stdin. No puede por ejemplo tratar varios argumentos en su invocación, pero tampoco tiene mucho sentido.
Os dejo un screenshot con un código C de ejemplo y su ejecución:
La aplicaciones prácticas que yo le veo son:
- Crear code snippets para solucionar problemas de tipo algorítmico (cálculo de fórmulas, planificadores, ciphers, etc) y compartirlos en Internet (tweet de la short URL).
- Desarrollar código desde dispositivos móviles (por ejemplo, hacer un script bash en el metro).
- Experimentar con nuevos lenguajes (como GoLang, Scala, etc).
¿se te ocurre alguna más?
En el apartado FAQ, podemos encontrar las típicas dudas sobre uso. En líneas generales, no se puede ejecutar código que haga uso de la red, ni crear/acceder a ficheros y la codificación es UTF-8.
Si quieres empezar a practicar con código ya creado, podeis mirar los ejemplos de la sección samples.
Curiosidad: por lo visto hay un lenguaje de programación llamado whitespace que también lo compila ideone. Sí, has acertado, se programa a base de espacios… Sin comentarios
Translate to:English
Menéame
Twitter












