Variables 101: Conociendo tu computadora

Tristemente he visto que no muchos desean aprender a programar este 2018 :( pero aunque solo hubiese leído mi artículo anterior una persona y tras un poco de tiempo esta sea capaz de mandar un commit a algún proyecto de software libre, yo me daría por satisfecho con mi labor :)

Para los amantes de la seguridad, les prometo que el siguiente será un post sobre seguridad :) así todos contentos, si alguno desea aprender otra cosa (como git, administración de servidores, o yo que sé :p ), o que comente algún otro tema que no pueda responderse de manera sencilla en el cuadro de comentarios, avísenme y vemos cómo podemos trabajarlo ;)

Bueno, ahora si vamos a lo nuestro, anteriormente hablamos sobre tipado, y que este tenía que ver con la forma en la que guardamos nuestras variables en un programa, ahora vamos a revisar un poco de qué ocurre en el interior y esperemos que pueda ser lo suficientemente claro.

Bits

Creo que este es un tema que siempre toco cuando escribo sobre programación, ciertamente es algo que me fascina y que me ha ayudado a comprender muchas cosas, ahora intentaré explicar un poco cómo son, cómo se leen, y para qué sirven :)

Piensen en un interuptor de luz, cuando el circuito está cerrado, tenemos un 0 en la pantalla, cuando cambiamos de posición el interruptor, pues un 1:) ¿sencillo no es cierto?

Ahora, un 0 y un 1 pueden significar muchas cosas, todo depende de la creatividad con que lo tomes, supongamos que yo quiero saber si alguien va a ir al Norte o al Sur, 1 puede significar norte y 0, sur :) digamos que yo quiero saber si alguien es hombre o mujer, 1 puede ser hombre y 0, mujer :) . Ahora quiero saber si esta persona es jóven o mayor (>22), 0 puede significar jóven y 1, mayor. Sigamos imaginando… ¿Tiene alguna mascota? 1 diría que sí, mientras que 0 diría que no. Ahora quiero que lean conmigo la siguiente línea:

1001

Esto es la forma breve de decir…

Una jóven mujer de no más de 22 años se dirige al norte acompañada de su mascota.

lo que es muy diferente a:

0110 o Un hombre con más de 22 años de edad se dirige solo hacia el sur.

Bytes

Ahora vamos a ir un paso adelante, vamos a aprender a leer bytes. Un byte es la sucesión de 8 bits, los cuales se leen de derecha a izquierda y cada 1 representa una potencia de 2 elevado a la n donde n es la posición del bit. Como suena a chino, vamos a poner un pequeño ejemplo :)

01001011 Tenemos este byte, ahora vamos a ir de derecha a izquierda ( <- ) yo voy a ponerlos de arriba a abajo para poder escribir su signifiacdo:

1: el bit al estar en la posición 0 nos indica que tenemos lo siguiente 2 elevado a la cero o 2^0. Esto bien sabemos que equivale a 1.

1: el segundo bit, ahora la posición 12^1 que es lo mismo que decir 2

0: tercer bit… aquí debería ser 2^2, mas como no está encendido, vamos a dejarlo en 0

1:cuarto bit, 2^3 u 8 :)

0: lo mismo que 0

0: otro 0

1:ahora estamos en 2^6 o 64

y finalmente 0 , que ya sabemos lo que significa :) ahora vamos a sumar nuestros resultados y compararlos con la siguiente tabla :) Tenemos un 75 por lo que vamos a buscarlo en la columna Decimal y veremos lo que aparece en Char

Resultado de imagen para ascii table

¡¡Tenemos una K!! Felicidades, ya saben leer en binario :) Pero los más sagaces habrán podido notar que también hemos obtenido un número decimal, y que este tiene un límite (cuando todos los valores son 1) Ese límite se encuentra en el número 255.

Word

Ahora más de uno me dirá, pero ¿qué pasa si necesito un número mayor a 255? o ¿dónde puedo encontrar otros caractéres como los japoneces? Pues la respuesta es simple, juntemos 2 bytes. Ahora que tenemos dos, la cantidad posible de combinaciones que tenemos es 2^16 o 65536 posibles resultados, como el 0 es uno de esos, el máximo posible es 65535. ¿A alguno le suena ese número? ¿Recuerdan la cantidad máxima de puertos en un sistema linux? Se los dejo de tarea ;)

Double word & quad word

Para los más matemáticos también existen formatos específicos, los double word contienen, como muchos ya habrán imaginado 2 word o 4 bytes (o 32 bits) de información, lo mismo que decir:

11111111111111111111111111111111 o de 0 a 4 294 967 295

A estas alturas muchos se preguntarán qué sucede con los números negativos, es decir, en algún lugar deben estar contemplados ¿no es cierto? Para poder almacenar un número negativo, los desarrolladores de procesadores eligieron ocupar el primer bit de la izquierda como un valor de signo. Esto quiere decir que si el primero bit es 0 hablamos de un número positivo, pero si es 1 tenemos un negativo. Ahora ven por qué los bits son tan especiales, pueden ser lo que tu quieras :D

¡Pero esto evidentemente nos deja con una posición menos para realizar la multiplicación! Por lo que nuestro0 a 4 294 967 295 se convierte en:

-2,147,483,648 a +2,147,483,647

Ahora bien, muchos ya tenemos procesadores de 64 bits, y este es el valor de un quad word, podemos tener valores que van desde el 0 a 18 446 744 073 709 551 615. Ese sí es un número grande :)

¿Por qué 8 bits?

Esto es algo que ya más de uno se estará preguntando, y la respuesta está en el hardware. Desde su origen, los procesadores necesitaban data para poder realizar operaciones. La data se almacena en la memoria de la computadora y cada vez que el procesador la requiere utiliza los buses de data para conseguirla. En la antigüedad, estos buses podían comunicar un máximo de 8 bits por ciclo, esto quiere decir que el máximo y más eficiente modo de mover data, era agrupando 8 bits y mandando estos al procesador.

Con el paso del tiempo, hasta el día de hoy, los procesadores han desarrollado la hablidad de mover 16 bits, 32bits y… 64 bits.

¿Qué tiene que ver con el tipado?

Ya llegamos a la parte donde todo cobra sentido :) El tipado es una propiedad que utilizan los lenguajes de programación para denominar estos espacios de memoria. Todas las variables tienen su contraparte en alguno de estos tipos de data, sin importar cómo sean llamadas. Estos se conocen como tipos de dato primitivos, cada lenguaje fuertemente tipado tiene su concepción de estos valores, y la cantidad que representan. Por ejemplo en C tenemos la biblioteca limits.h que nos muestra la cantidad máxima y mínima de los valores primitivos.

Veamos lo que sucede si intentamos romper uno de los valores:

Diseño propio. Christopher Díaz Riveros

A la derecha tenemos los valores del archivo limits.h y a la izquierda hemos tomado uno de estos valores (unsigned short int) y le hemos asignado un número mayor al correspondiente. Como resultado el compilador nos advierte que estamos utilizando mal la memoria porque la forma binaria de 66666 no puede caber en la forma binaria de 65535. Esto nos lleva a una lección de performance cuando programamos, si tu valor no va a crecer mucho a lo largo del tiempo, o si no requieres valores tan grandes como los de un double o quad word, utilizar el tipo correcto reduce la cantidad de memoria solicitada por el CPU, lo que implica una mayor velocidad de obtención de data si está bien calculada.

En el lado de los intérpretes esto es más sencillo debido a las conversiones implícitas. Cuando definimos una variable en lenguajes como javascript o Python, el intérprete se encarga de entender qué tipo es, y asignar el espacio suficiente de memoria para realizar las operaciones. Veamos un ejemplo sencillo :)

Diseño propio. Christopher Díaz Riveros

Como pueden ver, no tenemos que explicar al intérprete de python el tipo de nuestra variable, porque él mismo se encarga de asignar un tipo y almacenarlo en memoria :)

Conoce tus variables

Esto depende del lenguaje y tipo de implementación que vayas a utilizar, pero el primer paso para poder programar es aprender las variables que puedes utilizar :) Una vez comprendas las variables, estarás en situación de poder utilizarlas de manera eficiente y lógica para almacenar información (provista por un usuario o por el sistema). Este es el primer paso en la escalera de la programación y con suerte, tras leer este artículo, habrás comprendido un poco mejor cómo es que funciona tu computadora y cómo almacena la información. Conmigo será hasta el siguiente artículo, recuerden dejar sus comentarios para saber si hay que reforzar o comentar algún punto en específico. Saludos

Eligiendo tu primer lenguaje de programación

Ciertamente esta es una de las preguntas que más llega a mi bandeja de entrada al momento de hablar de programación. Si vamos a comenzar una serie de artículos que les permitan aprender a programar y devolver el conocimiento gratuito en forma de contribuciones a comunidades de software libre/open source en el mundo, es necesario responder a esta básica aunque un poco difícil pregunta. ¿Qué lenguaje de programación debo aprender?

Un poco de historia

Para poder empezar a comprender y elegir un lenguaje de programación, primero debemos conocer un poco sobre la historia de los mismos, sus usos y funciones, y cómo resuelven distintas necesidades a lo largo del tiempo.

Lenguajes de máquina (bajo nivel)

Conocidos comunmente como Assembly, son lenguajes de programación que podríamos definir como dialectos de un idioma más general… Esto suena un poco complicado pero lo voy a ejemplificar… Sabemos que el lenguaje universal de la computación es la electricidad, esto quiere decir que en última instancia lo que una computadora lee son 0s y 1s, vamos a denominar esto como español de computadora. En este ejemplo, el español es la regla básica, pero como bien sabemos, no es lo mismo el español que hablan los latinos al hablado en España, e incluso así, no es lo mismo el español de Perú con el español de Argentina. Evidentemente todos tenemos casi las mismas palabras (0s y 1s), mas el uso y significado pueden variar de acuerdo al contexto.

Esto sucede a nivel de procesador. Cuando hablamos de arquitecturas de computación, (amd64, intel, arm,…) nos referimos al dialecto de ese español de computadora. Esto se debe a que diversas empresas entienden el orden y significado a su manera, por lo que algunos varian en detalles como el flujo de la corriente, o el orden con el que se van a guardar los 0s y 1s.

Estos lenguajes de programación son sumamente veloces, puesto que trabajan al nivel más bajo posible de programación, pero son sumamente dependientes de la arquitectura y ciertamente son un poco más complicados de aprender que el resto. Estos suelen requerir de una base más amplia de conceptos para poder transformar la data y poder ejecutar cosas útiles en ellas. Para los amantes de los videojuegos, un ejemplo serían las consolas SEGA, las cuales utilizaban Assembly para programar sus juegos. Evidentemente en esa época la cantidad de memoria era mínima comparada con hoy, y era necesario dominar un lenguaje que pudiera ser veloz y producir programas ligeros.

Lenguajes de alto nivel

Este gran grupo contempla aquellos lenguajes que vinieron después de Assembly. La necesidad de obtener codigo portable hizo que surgiera un grupo de lenguajes denominados compilados. Entre estos el primero en tomar ventaja fue C, el cual ha tenido predominancia en la programación a nivel de sistema operativo desde los 70s.

Lenguajes compilados

Vamos a ver un ejemplo práctico de lo que comento. Veamos un programa muy simple en lenguaje C que imprime una línea de código.

Diseño propio. Christopher Díaz Riveros

Tras compilarlo tenemos lo siguiente:

Diseño propio.Christopher Díaz Riveros

Pero ahora veamos lo que tendríamos que escribir para replicar el mismo resultado en código Assembly:

Diseño propio. Christopher Díaz Riveros

Esta es la traducción de nuestras 3 líneas de código de simple.c, el archivo simple.s es creado mediante el comando gcc -S simple.c y es lo que entendería nuestro procesador en un dialecto Assembly. Evidentemente para poder crear un ejecutable que conste de 0s y 1s es necesario procesar el archivo simple.s y conectarlo con las bibliotecas compartidas de nuestro sistema. Esto se hace mediante un ensamblador (as) y un conector (ld).

Los lenguajes compilados brindan una gran ventaja sobre los de bajo nivel, son portables. La portabilidad entrega código que puede ser ejecutado en distintos procesadores sin la necesidad de generar código específico para cada arquitectura. Otra ventaja evidente es la simplicidad que emplea al momento de leer y escribir código. Dentro de sus principales desventajas tenemos una elevada complejidad, puesto que comparado con el siguiente tipo de lenguajes que veremos, la libertad que brinda C puede ser perjudicial si no se sabe controlar, ciertamente es como entregar una pistola, podría suceder que en la falta de experiencia una persona termine disparando a su propio pie en el intento de limpiar el arma.

Lenguajes interpretados

Dentro de este grupo tenemos una gran variedad de lenguajes, entre los más importantes contamos Python, Ruby, Javascript, PHP, etc… La idea básica de estos lenguajes es brindar una forma rápida de creación y ejecución de programas, esto se debe a que muchos de los procesos difíciles son llevados a cabo en el intérprete, y la programación de la lógica es la que se implementa en el código. Veamos el mismo ejemplo anterior pero esta vez escrito en Python:

Diseño propio. Christopher Díaz Riveros

Dentro de las cosas más resaltantes podemos ver que la primer línea se encarga de llamar al intérprete ( el programa que va a ejecutar nuestra aplicación) y el subsiguiente código es más “simple” que su versión en C, puesto que todo el trabajo pesado se realiza en el intérprete.

Diseño propio. Christopher Díaz Riveros

Los lenguajes interpretados brindan al desarrollador una capa de seguridad mayor, puesto  cuentan con controles de seguridad más rigurosos (OJO que no son perfectas, puesto que hasta los mejores pueden cometer errores) y ya no sufrimos el riesgo de disparar un arma sin darnos cuenta, puesto que al primer intento, el intérprete soltaría una alerta y se cancelaría la ejecución. La principal desventaja se hace evidente al momento de ejecutar el programa, puesto que este es más lento que su contraparte binaria, esto precisamente debido a la mayor cantidad de procesamiento para poder asegurar que el código funciona. Si el programa no requiere de plazos extremadamente cortos, la diferencia puede pasar desapercibida, pero si hablamos de miles o millones de datos por segundo, la diferencia se hace exponencialmente notable en los lenguajes compilados.

Tipado

Esta es una caracteríscia de los lenguajes de programación, estos pueden ser fuertemente débilmente tipados. Este tema lo voy a dejar para otro post, puesto que es necesario y curioso entender cómo se almacena la memoria en un programa, pero por ahora solo necesitamos hacer la distincion: Los lenguajes fuertemente tipados son aquellos que requieren conocer el tipo de dato que va a trabajarse en una variable o constante, mientras que los débilmente tipados pueden realizar conversiones de manera implícita y todo dependerá de una jerarquía de conversión seguida por el lenguaje. (si no se entiende ahora, no hay problema, lo dejaremos para después)

Paradigmas

Al igual que todo en el mundo GNU/Linux, los lenguajes de programación se basan de acuerdo a paradigmas, y se generan comunidades en torno a estos. Por ejemplo tenemos la Fundación Python o Ruby o PHP o Bash (en cuyo caso es la comunidad GNU). A lo que quiero llegar con esto es que no puedo expresar la gran cantidad de pros y contras que tiene cada uno, pero si puedo decirles que donde existe un lenguaje de programación libre, existe una comunidad donde aprender y participar. Vale la pena mencionar que muchos si es que no son todos los intérpretes de lenguajes están escritos en C, o algún derivado cercano, y el desarrollo de los mismos suele llevarse a cabo por un grupo más reducido de la comunidad, quienes se encargan de tomar decisiones que afectarán a todos los usuarios del lenguaje. Pueden incluso formarse instituciones que velen por el desarrollo correcto del lenguaje, como es el caso de C.

¿Cuál elegir?

Ya hemos hablado bastante sobre los lenguajes y todavía no respondo a lo más importante :P . Pero espero que tras haber revisado este pequeño artículo no sea necesario que sea yo mismo quien te diga qué lenguaje elegir, puesto que con esta información estás en toda la capacidad de buscar uno que te genere curiosidad. Evidentemente si deseas aprender a programar en un lenguaje Assembly requerirás de bastante tiempo antes de poder tener algo funcional, el tiempo se reducirá bastante si optas por un lenguaje compilado, donde además de contar con la portabilidad en sistemas *NIX, podrás aprender información referente a el funcionamiento del mismo sistema, puesto que estar en contacto con C o derivados te hace de una manera u otra aprender cómo funciona de manera general un sistema operativo. Por último, si lo que quieres es aprender algo ligero y que te permita hacer mucho sin la necesidad de comprender mucho, los lenguajes interpretados son una manera entretenida de aprender y desarrollar habilidades de programación.

Aprende con algo emocionante

Este es el mejor consejo que puedo darles, si quieren aprender algo, es necesario encontrar algo apasionante primero, sino será bastante difícil sobrepasar la curva de aprendizaje típica de todo lenguaje de programación. Supongamos que administran sistemas, en ese caso tal vez sea necesario aprender un lenguaje ideal para scripting (interpretado), dentro de estos contamos con Perl, Python, Bash, etc etc… Tal vez lo tuyo son los juegos, existen muchos proyectos en lenguajes como Javascript, Lua, C++, dependiendo del tipo de juego que desees realizar. Tal vez te gustaría crear una herramienta a nivel de sistema, pues tenemos C, Python, Perl, como verás algunos se repiten, y esto es debido a que muchos lenguajes pueden ser utilizados para muchas tareas, por eso la definición de lenguajes multipropósitos en la mayoría de estos.

Comienza un proyecto

Con esto no me refiero a que crees el siguiente compilador, o incluso el siguiente lenguaje de programación, un proyecto puede ser arreglar un pequeño bug en tu programa favorito, tal vez incluso ayudar a mejorar la documentación. ¿Por qué la documentación? porque no hay mejor forma de aprender cómo funciona el software que leyendo y ayudando a escribir su documentación, porque luego del código fuente, es la mayor fuente de información que se va a encontrar sobre el programa. En otro momento veremos cómo leer el código de un proyecto y entender las funciones y valores que adquieren.

Muchas gracias por haber llegado hasta aquí y como siempre, sus comentarios me ayudan a generar mejor contenido y saber dónde enfocar la atención, Saludos.

Comencemos el año programando

Es sorprendente la acogida que ha tenido el buen Mar.io y a decir verdad es el primer artículo que publico que genera más de 10 mil vistas, esto pone la valla algo alta para los siguientes y espero no defraudarlos con este :) Muchas gracias por encontrar mis escritos lo suficientemente interesantes como para compartirlos dicho sea de paso :)

Programación

Este es un tema de moda, todo el mundo quiere programar, o al menos todo el mundo piensa que es una habilidad cada vez más necesaria, y a decir verdad a mi me gustaría escribir todo un libro sobre programación, GNU/Linux, seguridad, y tal vez en algún momento lo pueda hacer, apenas aprenda cómo escribir libros libres y en formato agradable :P .

La tecnología avanza rápidamente

Este es uno de los motivos por los que no he escrito el libro todavía :P puesto que quiero hacer algo que pueda superar la barrera del tiempo en un campo en el que las cosas no suelen durar más de unos días de forma vigente. Es por esto que en este artículo quiero contarles un poco de los conceptos más que las implementaciones, de esta manera podremos volver a leer estas líneas en un tiempo y seguirán siendo vigentes.

Los principios se mantienen más tiempo

A pesar de que existen muchos lenguajes de programación hoy por hoy, muchos de los conpceptos se remontan a los mismos orígenes. Con esto quiero decir que muchas de las cosas que hoy se aprenden, han sido válidas por mucho tiempo, y probablemente lo seguirán siendo, esto debido a que la programación es hecha por personas y mientras sigan siendo ellos los que desarrollen, algunos conceptos se mantendrán.

Conociendo las bases

Ya existen muchos cursos, algunos gratuitos y otros no, que exponen gran parte de la sintaxis de muchos de los lenguajes de programación más populares de hoy en día. Pero no vamos a hacer esto aquí :) yo quiero contarles un poco de lo que todo pogramador debería pensar antes de empezar a programar para poder hacer un trabajo decente.

Entrar en la mente del programador es ciertamente algo necesario, ya en un artículo un poco antiguo tratamos el tema. Ahora vamos a entrar un poco en los conceptos que nos permiten escribir el código.

Variables y funciones

Las variables son espacios de memoria, pensemos en los buzones que tienen los grandes edificios, están diseñados para almacenar cierto tipo de objetos, los hay grandes y pequeños, pueden estar solos o en grupo. Una variable es un valor que tu sabes que se usará a lo largo del tiempo, aunque exactamente no conoces su valor en el principio, si lo conoces y sabes que no va a variar, estamos frente a una constante.

Las funciones por otra parte, son conjuntos de instrucciones. Una instrucción es lo más básico que puede hacer un procesador, la razón de ser de las funciones es permitir al programador agrupar conjuntos de ordenes para poder repetirlas a lo largo de un programa. veamos un ejemplo sencillo y a la vez lleno de detalles.

Diseño propio. Christopher Díaz Riveros

Este es un pequeño programa escrito en C, tenemos la función main, la variable saludo, y la función printf que proviene de la biblioteca stdio.h. Vamos a modificar un poco el ejemplo y luego compilarlo para ver qué sucede.

Diseño propio. Christopher Díaz Riveros

Hemos agregado una pequeña función llamada saludar la cual toma como argumento una variable llamada saludo y la imprime. Esto no cambia mucho el resultado final del programa pero nos permite mostrar un gran y útil principio de la programación, la abstracción. Veamos el resultado:

Diseño propio. Christopher Díaz Riveros

Un simple programa, que está lleno de conocimiento y trabajo.

Bibliotecas

El motivo por el cual creé la función saludar fue simplemente para mostrar uno de los principios más grandes del desarrollo de software, que ya hemos nombrado: la abstracción. Así como hemos definido saludarprintf() ha sido definido en algún lugar de nuestro sistema operativo (la biblioteca standard de C de GNU), este lugar se conoce comúnmente como biblioteca. Las bibliotecas son conjuntos de funciones que nos permiten agregar funcionalidad a nuestros programas sin tener que volver a inventar la rueda. En este caso, gracias a printf no tenemos que preocuparnos de toda la lógica necesaria para poder mostrar en una terminal el mensaje que deseamos.

Las bibliotecas están presentes en casi todos los lenguajes de programación actuales, puesto que al poder contar con secciones de código para elegir e implementar es más sencillo que crear cada función desde cero.

Abstracción

Imaginemos el sistema de correo, nosotros no necesitamos conocer toda la logística necesaria para poder enviar o recibir una carta, lo mismo sucede con la programación, abstraer es esencial para generar código duradero y elegante. Este proceso permite utilizar nombres generales para definir procesos generales.  En otras palabras, si creamos la función enviarCarta() sabemos de manera general que dicha función se encargará de enviar una carta, pero no necesariamente qué pasos se requieren para hacerlo. Y este es otro punto por el cual la abstracción es tan buena, puesto que nos permite encapsular segmentos de procesos.

Encapsulamiento

Nuestra función saludar es un claro ejemplo de encapsulamiento, nos permite tener un bloque cerrado con instrucciones específicas que podemos usar una o mil veces dentro de un programa. Esto hace que el código sea más fácil de leer y que sea más fácil de depurar puesto que si algún error surge, sabemos exactamente cuáles son los límites de nuestra función, y conocemos cada instrucción en un espacio reducido. Esto nos lleva a un principio de la programación bastante común en UNIX

Haz una cosa, hazla muy bien

Una buena función es aquella que solamente hace una cosa, pero la hace muy bien. Pensemos en esto por unos instantes… enviarCarta() probablemente haría muchas cosas, lo que no puede ser bueno si queremos depurar el proceso, mientras saludar() solo hace una. A lo largo del tiempo, si surgen problemas la segunda será más fácil de reparar que la primera. Una opción para evitar este problema sería generar distintos niveles de abstracción para enviarCarta(), esto quiere decir que dentro de la función existirían otras como verificarSobre() y tal vez dentro de esta una como verificarRemitente(). En definitiva esta última función (verificarRemitente()) es mucho más específica que solo enviarCarta() y de esta manera podemos encapsular partes del código para que hagan lo que es necesario y solo una cosa a la vez.

Practicar

Para aprender el arte de la programación es necesario practicar, y puesto que yo ahora he dado un vistazo muy general al tema, es necesario que practiquen con diversos lenguajes, o diversos problemas. Primero intentando generar funciones específicas, luego aumentando la complejidad. Como siempre, si surgen dudas, o sugerencias o comentarios, me ayudan mucho a saber qué aspectos reforzar. Muchas gracias y que este 2018 esté lleno de éxitos y proyectos asombrosos. Saludos