Apuntadores en C. Recorrer variable int como si fuera un arreglo char

El capítulo 5, del libro “El Lenguaje de programación C” de K&R dice en la página 103:

“Una situación común es que cualquier byte puede ser un char, un par de celdas de un byte pueden tratarse como un entero short, y cuatro bytes adyacentes forman un long.”

Partiendo de esta idea, podemos considerar que si una variable es de tipo int, su tamaño aproximado será de 4 bytes (32 bits). En C los tipos de datos nos ayudan a darle interpretación a los bits (trata estos bits como int, char, float, double, etc).

Una variable de tipo int, de 4 bytes de largo (tamaño variable dependiendo de la implantación) se puede representar por 32 bits:

00000000 00000000 00000000 00000000

Entonces, si de alguna manera pudieramos recorrer esos bits byte por byte e interpretarlos como tipo char, podríamos almacenar letras en ese entero y recorrerlo como un arreglo.

Una variable de tipo char se vería como 8 bits:

0000000

Si recordamos, el código ASCII es una tabla en la cual podemos representar letras, es un estándar. En ASCII necesitamos 8 bits, de los cuales por lo general solo se usan 7 para los caracteres imprimibles.

En ASCII, los valores para las letras empiezan a partir del byte con valor decimal 65 (en Hex equivale a 0x41):

01000001

Entonces, que tal si en nuestra variable int, almacenamos un número como esto:

DECIMAL: 1094861636

HEX: 0x41424344

BINARIO: 01000001 01000010 01000011 01000100

ASCII: A B C D

Teniendo un numero binario de esa forma, podríamos recorrerlo con un apuntador de tipo char, que recorra byte por byte e imprima en pantalla la letra respectiva … después de todo, son solo bits, lo importante es como los interpretamos.

El código quedaría como:

#include <stdio.h>

int main(void)
{
    int c = 0;
    c = 0x41424344;
    printf("%d\n", c);
    char *d = (char *) &c;
    
    printf("%c\n", *(d));
    printf("%c\n", *(d + 1));
    printf("%c\n", *(d + 2));
    printf("%c\n\n", *(d + 3));
    
    printf("%c\n", d[0]);
    printf("%c\n", d[1]);
    printf("%c\n", d[2]);
    printf("%c\n", d[3]);
    
    return 0;
}

 

AudioSense: HTML5 y la Web Audio API

pavarotti

AudioSense mostrando en tiempo real la frecuencia de Nessun Dorma

Bueno, hace tiempo que he tenido oportunidad de llevar varios cursos en línea, del tipo MOOC (Massive Open Online Course). En un principio excéptico, he encontrado en este tipo de plataformas una nueva y muy poderosa herramienta de auto-aprendizaje.

Hay una plataforma llamada edX (www.edx.org), la cual es una plataforma abierta que ofrece cursos en línea de forma gratuita. EdX fue fundada por Harvard y el MIT por lo que pienso que tienen un respaldo bastante sólido.

De los varios cursos disponibles, hace tiempo probé los cursos de HTML5,

HTML5 Part 1: HTML5 Coding Essentials and Best Practices: https://www.edx.org/course/html5-part-1-html5-coding-essentials-w3cx-html5-1x-0

HTML5 Part 2: Advanced Techniques for Designing HTML5 Apps: https://www.edx.org/course/html5-part-2-advanced-techniques-w3cx-html5-2x-0

Bueno, haré una pausa de la publicidad en este punto, por que ciertamente no busco que esta entrada en el blog sea solo hablar de los cursos, si no más bien quisiera presentarles una pequeña aplicación web que pude realizar gracias a los conocimientos que pude obtener de los mismos (gracias al gran profesor Michel Buffa).

AudioSense y Web Audio Api

Bueno, los que hayan jugado un poco con HTML5 habrán visto que hay una gran cantidad de elementos poderosísimos, como la capacidad de manejar video sin necesidad de flash, manipular audio, geolocalización e incluso la posibilidad de crear juegos gracias al Canvas.

Se me ocurrió entonces, unir algunas de estas propiedades en una sola aplicacion:

AUDIOSENSE DEMO: https://s3-us-west-2.amazonaws.com/richiemx.com/index.html

Al entrar a la aplicación, nos solicitará permiso para acceder a la webcam y al micrófono:

audios1

Si nos damos cuenta en la dirección del navegador, estamos en https, esto es requisito si subimos nuestra aplicación a Internet y queremos que pueda solicitar acceso a la Webcam del usuario (o al micrófono) … creo recordar que sin https, en Chrome o Firefox no se puede hacer esta solicitud.

Pero… ¿Qué hace mi aplicación?…

Bueno, si le damos permiso de utilizar el micrófono… la aplicación estará a la escucha de los sonidos que capture el micrófono y mostrará la frecuencia de la señal de audio en dos paneles canvas de la pantalla. También obtiene la imágen de la webcam pero esto es solo con fin decorativo.

En la parte central, se muestra el valor de la frecuencia en Hz … todo esto para qué?

Bueno, he visto en varias páginas de Internet donde hablan que los sonidos representan colores (Ej. http://www.flutopedia.com/sound_color.htm).

Entonces si tenemos la frecuencia del sonido en tiempo real, podemos mostrar en un canvas que varie el color en base a esta frecuencia … el resultado? … podría ser: el color de los sonidos.

Realmente es una aplicación sencilla, aunque creo que un logro si fue poder calcular la frecuencia en Hz … y el resultado es agradable, por decir, he probado el programa escuchando música de Pavarotti y obtengo unos bello tonos en morado.

Tenía la aplicación arrumbada, lo cual no pienso que tenga caso, asi que por eso la quise compartir, si alguien gusta de tener el código puede obtenerlo directamente, pues no es mas que HTML, CSS y Javascript.

Como nota extra quiero decir que fue mi proyecto final en un MOOC que rennovó totalmente mi gusto por la programación, me refiero al curso CS50 de la Universidad de Harvard y que es dirigido por el grandioso Profesor David Malan: https://www.edx.org/course/introduction-computer-science-harvardx-cs50x

En Audiosense, hay otros dos menús … uno que te lleva a un fractal y otro a una gráfica de una parábola … eran mis experimentos con el Canvas y la graficación … no hablaré en este post al respecto, pero si logro perfeccionar mi fractal, haré un post para platicar un poco.

Dejo todo esto solo como curiosidad, a lo mejor a alguien le resulta interesante …

EOF

Hablando de C: Números Binarios

La ciencia de la computación, trata sobre “datos”, como almacenarlos, interpretarlos y operar con ellos. Cuando trabajamos en C, podemos profundizar a varios niveles.

En C, hay 4 tipos de datos básicos:

  • char: De 1 byte, la palabra char viene de character (8 bits)
  • int:  Por lo general es del tamaño natural  de la maquina, nos sirve para trabajar con enteros (4 bytes)
  • float: Utilizamos los numeros de punto flotante float de precisión sencilla para numeros que requieren parte fraccionaria (4 bytes, precisión hasta 8 digitos)
  • double: Numeros de punto flotante con doble precisión (15 a 17 digitos de precisión)

Si vamos a manejar números muy grandes o muy pequeños, es mejor utilizar double, por su ventaja en precisión sobre float. Además, hay un problema inherente a todas las computadoras …

Para una computadora es difícil representar algunos números con fracciones … es un problema que tienen todas las computadoras por diseño al tratar con números de punto flotante, por lo que si por decir, si lo que queremos es un float con valor a .001, nuestra computadora nos regresa una aproximación, como podemos ver en la imágen:

signos3

Más información sobre los problemas con aritmética de punto flotante: http://docs.python.org.ar/tutorial/2/floatingpoint.html

Teniendo esto en cuenta, un programador debería tener en cuenta considerar aproximaciones y redondeos para evitar este problema. (Si sumas .01 en un ciclo, 10 iteraciones, al final tu computadora no almacena 1, si no una aproximación).

Pero volviendo al tema inicial, para una computadora, el valor de los 0’s y 1’s que tiene almacenados, depende de la interpretación, por si mismos no significan nada.

Ejemplo,

Una variable de tipo char son 8 bits: 00000000

Aunque char significa character, char no es sino un entero pequeño, lo importante es como interpretarlo.

Si de estos 8 bits tomamos el último bit para el signo, entonces solo quedan 7 bits para representar un entero: 00000000

Bajo estos términos, qué valor tendría el número? 10000000

Depende, el número binario 10000000 representa el 128 positivo, pero para representar en binario números negativos usamos el complemento a uno y complemento a dos.

En complemento a uno, negamos los bits del número binario original, esto es, 10000000 se vuelve 01111111. Una curiosidad del complemento a uno es que hay dos formas de representar el cero. En una máquina es más común encontrar que utilice complemento a dos para representar negativos. El complemento a dos solo suma 1 al complemento a uno, esto es:

01111111 + 00000001 = 10000000

Entonces, 128 y -128 se representa igual en binario, pero depende de que consideremos o no el último bit como signo, en la imágen pongo la salida de dos programas en C donde se puede ver mejor esto:

signos4

El código para el primer programa lo pueden descargar de aqui: http://www.mediafire.com/view/z9ewo988uj60ccy/signos.c

En este programa, aunque es sencillo ponemos varios detalles, por decir, en el código estamos aplicamos una máscara a una variable char (la máscara es 0x80) con signo y sin él. También ponemos algunos ejemplos del problema de precisión y el de representación de números fraccionarios.

Creo que es un tema importante por que nos ayuda a entender otras cuestione relevantes, como el casteo de variables.

También nos ayuda a comprender mejor el código ASCII, en el cual se toma el bit de signo para control de transmisión y los caracteres imprimibles solo llegan hasta el 127. https://es.wikipedia.org/wiki/ASCII

Agrego un programa más, para los que quieran experimentar un poco más con los números binarios y sus representaciones, el programa se llama binconv, la siguiente es una captura del mismo corriendo:

signos2

El código lo pueden bajar aquí: http://www.mediafire.com/view/w3ob5ncbswqrgga/binconv.c

Con binconv pueden convertir de binario a decimal, de decimal a binario y con la opción -z imprimir de 0 a N números en su notación decimal, binaria, complemento a uno, a dos, valor hexadecimal y Octal.

Me animé a hacer los programas por que desde hacia tiempo quería investigar un poco más sobre las conversiones decimal a binario y visceversa, comparto el código por que tal vez a alguien más le sirve…

El código de binconv trae varias funciones interesantes y aprovecha el concepto de que un char, es un entero pequeño.

Igual si encuentran algo que no funcione bien agradecería la retroalimentación!

Nota Extra:

Para convertir un entero positivo a una cadena de texto binaria, podemos hacerlo de dos maneras, una es como está en el código de binconv.c (aplicando al entero % 2), pero también lo podemos hacer realizando un corrimiento a la derecha del número y aplicando un AND de bits con máscara 0x1 (o 01 en octal), con lo cual nos regresaría solo el primer bit (contando de derecha a izquierda) … finalmente invertimos la cadena y obtenemos la representación binaria, el código sería algo como lo siguiente:

void dectobin(unsigned n, char *s, int lim)
{
    char *scopy = s;
    
    while (n > 0 && (s - scopy) < lim - 1)
        *s++ = (n & 0x1) + '0', n = n >> 1;

    *s = '\0';
    // Invertimos cadena
    //reverse(scopy);
}

————–

EOF