Autor Tema: Proyecto de Curso (Comentarios): Programación en C (2013, por Argentinator)

0 Usuarios y 1 Visitante están viendo este tema.

05 Enero, 2013, 09:24 pm
Respuesta #40

feriva

  • Matemático
  • Mensajes: 9,056
  • País: es
  • Karma: +1/-0
  • Sexo: Masculino
  • No soy matemático, eso es una etiqueta.


Tendrías que colgar acá tu programa, si querés que entienda el problema.


Por ejemplo, copié el programa de este vídeo http://www.youtube.com/watch?v=ZQrfL1jruSU y no iba; puede, quizá que yo interprete mal un símbolo que hay dentro del "scanf", delante de la "a" parece que hay un "&", pero a lo mejor es que es otra cosa y por eso no va.
 Míralo a ver qué puede ser, es un programa muy corto, se copia enseguida.

Gracias.

06 Enero, 2013, 12:37 am
Respuesta #41

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
No sirve de nada que copie un programa de ese video de Youtube, porque el programa que tiene errores es el tuyo.

Por eso tengo que ver tu programa.

Además, aunque el programa del video es corto, no lo pienso copiar a mano, porque encima lo va cambiando durante todo el video, y tengo que comerme toda la explicación hasta ver qué diablos es lo que hace.

No voy a mirar videos...

06 Enero, 2013, 01:11 am
Respuesta #42

pierrot

  • pabloN
  • Moderador Global
  • Mensajes: 3,395
  • País: uy
  • Karma: +0/-0
  • Sexo: Masculino
El tipo de datos básico asociado en C a las strings es char*.
Otro día les explico qué es ese asterisco: *::)

Estoy ansioso de que llegue ese día  :laugh:.
$_="loe  hnachaPkr erttes,urJ";$j=0;for($i=0;s/(.)(.{$j})$//;$i++){$_=$2.$_,$j+=1-$i%2,print$1}print

06 Enero, 2013, 01:27 am
Respuesta #43

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
El tipo de datos básico asociado en C a las strings es char*.
Otro día les explico qué es ese asterisco: *::)

Estoy ansioso de que llegue ese día  :laugh:.

Ese día me huele a lejano...

Me acuerdo del asterisco maldito que la pantera rosa no podía sacarse de encima:

*

Y encima vino el papá asterisco y se enojó:

*


Me estoy divirtiendo con el asunto de cómo se especifican las constantes en C, ya sea char o int, y cuáles son todas las vicisitudes en torno a la asignación de tipos de datos a esas constantes.

Es algo terriblemente complicado, pero me dí el gusto de hallar toda la información, sacar las conclusiones, y analizar todos los detalles.  >:(

Esto hay que hacerlo alguna vez, porque si no, después vienen las sorpresas, y nadie entiende por qué el programa puso un número donde uno esperaba que ponga otro...

06 Enero, 2013, 01:52 am
Respuesta #44

feriva

  • Matemático
  • Mensajes: 9,056
  • País: es
  • Karma: +1/-0
  • Sexo: Masculino
  • No soy matemático, eso es una etiqueta.
No sirve de nada que copie un programa de ese video de Youtube, porque el programa que tiene errores es el tuyo.

Por eso tengo que ver tu programa.

Además, aunque el programa del video es corto, no lo pienso copiar a mano, porque encima lo va cambiando durante todo el video, y tengo que comerme toda la explicación hasta ver qué diablos es lo que hace.

No voy a mirar videos...

Bueno, hombre, no te pongas así  :laugh:  :laugh: Decía sólo la primera parte hasta que ejecuta; lo he mirado y remirado, repasado puntos y comas, etc., pero introduzco el número y ahí se queda, el condicional que le sigue no corre. Pero da igual, ya lo repaso más veces, a ver...

06 Enero, 2013, 01:55 am
Respuesta #45

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
¿Sabían que el número 10 trillones es un desastre de constante?

Según las reglas de asignación automática de tipos enteros, el 10 trillones intenta asignarse a un long long int, pero este tipo le queda chico, así que el estándar no le asigna nada.

Mi compilador me avisa de la situación esta del estándar, pero sin embargo el valor lo toma igual, y parece que lo reconoce como un unsigned long long int.

O sea que la situación es altamente impredecible, si uno no conoce su compilador.

Por un lado, el compilador reconoce que 10 trillones es un número entero positivo (podría no reconocerle como tal).
A la vez, reconoce cuánto vale realmente (podría no hacerlo), porque puede decir que es más grande que lo que acepta el long long int.
Pero dice que no le gusta el número ese, porque no cabe en ningún tipo entero, según el estándar (el estándar podría ser otra cosa).
Aunque igual lo asigna a un tipo entero, el unsigned long long int (podría no asignarlo a nada).
Y también se trata de una constante factible de ser guardada en memoria como un dato de tipo long long int (podría darse el caso de un valor que no pertenezca a ningún tipo válido).

Así que una constante puede considerarse desde varios puntos de vista, que pueden ser todos incompatibles, si no se procede con cuidado.


06 Enero, 2013, 01:56 am
Respuesta #46

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
No sirve de nada que copie un programa de ese video de Youtube, porque el programa que tiene errores es el tuyo.

Por eso tengo que ver tu programa.

Además, aunque el programa del video es corto, no lo pienso copiar a mano, porque encima lo va cambiando durante todo el video, y tengo que comerme toda la explicación hasta ver qué diablos es lo que hace.

No voy a mirar videos...

Bueno, hombre, no te pongas así  :laugh:  :laugh: Decía sólo la primera parte hasta que ejecuta; lo he mirado y remirado, repasado puntos y comas, etc., pero introduzco el número y ahí se queda, el condicional que le sigue no corre. Pero da igual, ya lo repaso más veces, a ver...

Me puse celoso, me andás engañando con el tipo ese de youtube.  :'(

06 Enero, 2013, 02:04 am
Respuesta #47

feriva

  • Matemático
  • Mensajes: 9,056
  • País: es
  • Karma: +1/-0
  • Sexo: Masculino
  • No soy matemático, eso es una etiqueta.

Me puse celoso, me andás engañando con el tipo ese de youtube.  :'(

De eso nada, tiene voz de robot, no es mi tipo :D

 Bueno, te ponga la rutinita, a ver qué hago mal; cuando meto el número no sigue, y si doy a enter desaparece la ventana:

#include <stdio.h>

main()

{
    int a;
       
    printf("Escriba un numero: ");
   
    scanf(" %d", &a);
 
    if(a>0)
 
printf("Es positivo");
   
 
    }

Ya mañana me levanto pronto para ver los Reyes, a ver si lo han arreglado :D

Hasta mañana y buenas noches.

06 Enero, 2013, 02:37 am
Respuesta #48

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
Bueno, a mí el programa me funciona.

Veamos, al poner el número que pide scanf, funciona bien.

Pero desaparece la ventana.
Esto es porque no hay nada que frene el programa antes de que termine.

Lo que tendrías que hacer es abrir una ventana de comandos de Windows:

Menú Inicio, "Ejecutar" y luego CMD.exe, ENTER.

Ahí vas a la carpeta donde tengas guardados tus proyectos, y tratar de ejecutar tu programa desde la línea de comandos directamente.

Ahí vas a ver que no se te cierra la ventana, y que muestra el resultado correcto.

__________

Intenté ponerle un freno, como hacíamos antes, colocando una sentencia getchar(); al final del programa.

Pero no funcionó.

La cuestión es que parece que hay cierta incompatibilidad en los métodos de lectura de datos desde el teclado, al usar scanf, y luego más tarde un getchar().

El tema está en que con scanf("%d", &a); leíste un número, es cierto, pero también tuviste que apretar la tecla ENTER para que terminara el proceso de entrada de datos del teclado.

Sin embargo, esta tecla ENTER no es parte del número que ingresaste, y por lo tanto queda guardado, en cierto modo, en el buffer del archivo estándar (la consola o teclado).
Así, cuando intentás hacer una nueva lectura de datos del teclado, lo primero que aparece en escena es esa "tecla ENTER" que presionaste antes,
lo cual es un caracter, el famoso avance de línea '\n' (o ASCII 10).

Entonces cuando le llega el turno al getchar(), ya lee ese ENTER que habías presionado anteriormente, y sigue con el fin de programa, con lo cual no te da tiempo a presionar ENTER de nuevo.

Una solución horrenda, podría ser poner dos getchar() juntos... pero eso es un asco, no lo hagas.
Otro intento de solución podría ser poner un nuevo scanf(), lo cual te obligará a presionar ENTER, al igual que getchar();

Pero pueden suceder situaciones parecidas a las del getchar(), o bien habrá una forma no clara de salir del embrollo.

Me parece que en este caso lo más adecuado sería hacer una pausa con una llamada al sistema, así:

system("PAUSE");

previamente adosando la librería stdlib.h

______________




#include <stdlib.h>
#include <stdio.h>

void main(void)

{
   int a;

    printf("Escriba un numero: ");
   
    scanf(" %d", &a);

    if(a>0)
        printf("Es positivo\n\n");
   
   system("PAUSE");

 }


06 Enero, 2013, 05:55 am
Respuesta #49

pierrot

  • pabloN
  • Moderador Global
  • Mensajes: 3,395
  • País: uy
  • Karma: +0/-0
  • Sexo: Masculino
Me estoy divirtiendo con el asunto de cómo se especifican las constantes en C, ya sea char o int, y cuáles son todas las vicisitudes en torno a la asignación de tipos de datos a esas constantes.

Es algo terriblemente complicado, pero me dí el gusto de hallar toda la información, sacar las conclusiones, y analizar todos los detalles.  >:(

Me gusta el estilo con el que estás redactando las notas; te está quedando muy bien.

Esto hay que hacerlo alguna vez, porque si no, después vienen las sorpresas, y nadie entiende por qué el programa puso un número donde uno esperaba que ponga otro...

Sí, cuando dije que estaba ansioso por ver como tratarías todo el tema de los punteros y la memoria dinámica no lo dije como si toda esta introducción me aburriera; por el contrario estoy aprendiendo cantidad de cosas. En la facultad se le da muy poca importancia al lenguaje de programación en sí; en seguida se pasa a analizar las distintas estructuras de datos y sus implementaciones; también se estudian una cantidad de algoritmos relacionados a esas estructuras de datos. Pero de C en sí es muy poco lo que se enseña (al igual que sobre Pascal y Modula-2 que fueron los lenguajes que utilizamos anteriormente). Sólo lo básico para empezar a programar. Lo demás se aprende sobre la marcha y a los golpes, como es de suponer. Este año pareciera que vamos a hacer lo mismo con Java en Programación 4 y Taller.

Ya que estás con la representación interna de los distintos tipos de datos, como sugerencia nada más, podrías explicar algo sobre las máscaras de bits en C y en general sobre los operadores bit a bit. Además de esa forma se puede investigar sobre cómo representa C cada tipo de datos. Por ejemplo, para un variable de tipo int se usan 32 bits, pero al menos yo desconocía exactamente cuál era la representación interna que empleaba C para el tipo de datos entero con signo. Para descubrirlo, hice este programa

#include <stdio.h>

int main(){
   int k,n;
   n=-2;
   for(k=31; k>=0; k=k-1)
      printf("%d",(n & (1 << k)) >> k);
return 0;
}


y muestra:

-11111111111111111111111111111110

Es claro que usa complemento a dos  :P. Lo que sí no sé es por qué imprime un signo de menos, pero imagino que debe estar asociado a la implementación de la función printf. Si se cambia la línea n=-2 por n=2, despliega, obviamente:

00000000000000000000000000000010

Investigar este tipo de cosas puede estar interesante  :laugh:.

Saludos
$_="loe  hnachaPkr erttes,urJ";$j=0;for($i=0;s/(.)(.{$j})$//;$i++){$_=$2.$_,$j+=1-$i%2,print$1}print

06 Enero, 2013, 11:28 am
Respuesta #50

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
Citar
Sí, cuando dije que estaba ansioso por ver como tratarías todo el tema de los punteros y la memoria dinámica no lo dije como si toda esta introducción me aburriera;

Ya lo sé, no te hagás problema.

Citar
Además de esa forma se puede investigar sobre cómo representa C cada tipo de datos.

Esto puede depender enormemente del (1) compilador usado, (2) las opciones de compilación elegidas, (3) la versión del sistema operativo en que corre, (4) la máquina en que se está ejecutando, etc. ( ¿"etc." == "\emptyset"?  :P ).

O sea que puede haber un error al creer que vamos a descubrir lo que hace C al llevar a cabo operaciones de bajo nivel.

Me refiero a que "C" es algo abstracto, definido por un comité de personas sobre papel, firmado y sellado.
Es lo que llamamos el "estándar".
Si el estándar decide que las operaciones de bits, por ejemplo, tienen determinada representación, entonces se supone que todos los compiladores que respetan el estándar tienen que hacer lo mismo.
Lo que podemos esperar ya está definido de antemano en este caso, y no hace falta programar nada... en teoría.
Hacer el programa nos ayudaría a entender si nuestro compilador respeta el estándar o no, o si está correctamente configurado o no, entre otras cuestiones.

Si el estándar no dice nada al respecto, y lo deja indefinido, entonces investigar la representación interna de los bits de datos no puede darnos información certera de lo que podemos esperar de C, porque esto depende de todos los factores que hemos mencionado arriba, y queda abierta la posibilidad de que cada persona obtenga distintos resultados (probablemente) en sus respectivos sistemas.

Sobre este punto estoy teniendo el cuidado de diferenciar hasta dónde el estándar asegura tal o cual cosa, y a partir de dónde es que ya no asegura nada, y las decisiones las toma el compilador que nosotros usamos.

En tu frase:

Citar
se puede investigar sobre cómo representa C cada tipo de datos.

Lo correcto sería decir "se puede investigar sobre cómo representa el compilador GCC cada tipo de datos".  ;) ;) ;)



En la universidad lo que enseñan es a razonar algoritmos, y el lenguaje es tema secundario, lo cual está bien, porque uno tiene que aprender a pensar los problemas informáticos en forma independiente al lenguaje que está usando.

Estudiar un lenguaje por sí mismo es un gustito que uno puede darse en su casa.  :P
Está permitido con lenguajes importantes como el C.
No sé si vale la pena hacer el mismo análisis con la mayoría de otros lenguajes.

El análisis técnico de un lenguaje es para entender las limitaciones que tiene, y hacer una programación más profesional, en la que los errores de ejecución no nos agarren por sorpresa, como les pasa todo el tiempo a los programadores del Windows.  :P

Citar
Ya que estás con la representación interna de los distintos tipos de datos, como sugerencia nada más, podrías explicar algo sobre las máscaras de bits en C y en general sobre los operadores bit a bit.

Ahora estoy con los tipos de datos, sin analizar los operadores, que son muchos.
Pero me parece muy bien la sugerencia, seguramente le dedicaré sus buenos capítulos.



En cuanto a tu programa:

Citar
#include <stdio.h>

int main(){
   int k,n;
   n=-2;
   for(k=31; k>=0; k=k-1)
      printf("%d",(n & (1 << k)) >> k);
return 0;
}

y muestra:

-11111111111111111111111111111110



Cambié la línea del printf así:

printf("%d  ",(n & (1u << k)) >> k);

(Agregué un espacio en blanco para que se vean mejor los supuestos "bits", y agregué un sufijo "u" al 1).
Con eso parece funcionar bien. Lo que hago es forzar a que la constante 1 sea de tipo unsigned int, y así los corrimientos de bits "conservan el signo".

Creo que el problema podía estar en el tipo de datos que automáticamente se asigna a los números enteros.

Según las reglas de asignación de tipos, el 1 "intenta" ser un signed int, si es que cabe ahí.
Como cabe, lo pone de ese tipo.
Después, la operación (1 << k) conserva el tipo de datos, porque es sólo un corrimiento de bits.
El resultado será un entero positivo para k < 31 porque el bit de signo sigue siendo 0.
Mientras que para k = 31, el "1" ya ha llegado al bit de signo, el primero de todos: 1000000000...
En ese caso, como se trata de un signed int, el valor que toma es el de un número negativo, el más negativo de todos, digamos.

El código siguiente:

for(k=0; k<=31; k=k+1)
      printf("k==%d: (1 << k) == %d\n  ",k, (1 << k));

Genera esta salida:

  k==0: (1 << k) == 1
  k==1: (1 << k) == 2
  k==2: (1 << k) == 4
  k==3: (1 << k) == 8
  k==4: (1 << k) == 16
  k==5: (1 << k) == 32
  k==6: (1 << k) == 64
  k==7: (1 << k) == 128
  k==8: (1 << k) == 256
  k==9: (1 << k) == 512
  k==10: (1 << k) == 1024
  k==11: (1 << k) == 2048
  k==12: (1 << k) == 4096
  k==13: (1 << k) == 8192
  k==14: (1 << k) == 16384
  k==15: (1 << k) == 32768
  k==16: (1 << k) == 65536
  k==17: (1 << k) == 131072
  k==18: (1 << k) == 262144
  k==19: (1 << k) == 524288
  k==20: (1 << k) == 1048576
  k==21: (1 << k) == 2097152
  k==22: (1 << k) == 4194304
  k==23: (1 << k) == 8388608
  k==24: (1 << k) == 16777216
  k==25: (1 << k) == 33554432
  k==26: (1 << k) == 67108864
  k==27: (1 << k) == 134217728
  k==28: (1 << k) == 268435456
  k==29: (1 << k) == 536870912
  k==30: (1 << k) == 1073741824
  k==31: (1 << k) == -2147483648



Podría ser alguna cuestión de la función printf (pero no, porque printf imprime correctamente datos de tipo signed int), así que podemos verificar el signo. Basta depurar con algo como:

   printf("%d\n", (1 << 31) < 0);

que dio salida "1" (verdadero).

Por otra parte, al hacer la máscara de bits con n = -2, que aparentemente, como vos decís, tiene representación complemento a dos,
el resultado de (n & (1 << 31)) tiene que dar esto: 1000000000000000.....

De nuevo, eso como signed int es el número -2147483648.

Cuando lo volvés a correr hacia la derecha con >> k, ¡va agregando 1's a la izquierda!  :o
El resultado de (n & (1 << k)) >> k, con k = 31, es 1111111111111.....  >:(

Pareciera que el corrimiento a derecha no funciona bien.

Al parecer esto no está bien definido en el estándar cuando se trata de números negativos.
Así que depende de la implementación.
Nuestro compilador lo que hace con el corrimiento a la derecha es conservar el bit de signo.

Si el número es positivo, hace lo que esperabas que hiciera, agrega 0's a la izquierda.
Pero si es negativo, agrega 1's por la izquierda, porque en cada corrimiento de los 31 que hace, va agregando un 1 por la izquierda...

Como 111111111111.... es la representación binaria de -1, eso explica el -1 al principio de la salida de tu programa.



Conclusión: El problema no es la representación binaria del -2, sino cómo se implementa en forma local (en el compilador) la operación de corrimiento a derecha.

Buscando seguro se puede encontrar datos sobre esto.
Por ahora sólo encontré algo de C++, que igual se aplica a nosotros, seguramente:

Citar
>> Corrimiento a derecha:

El patrón de bits de expr-desplazada sufre un desplazamiento derecho del valor indicado por la expr-desplazamiento. Como en el caso anterior, ambos operandos deben ser números enteros o enumeraciones. En caso contrario, el compilador realiza una conversión automática de tipo. El resultado es del tipo del primer operando.

Una vez promovida a entero, expr-desplazamiento debe ser un entero positivo y menor que la longitud del primer operando. En caso contrario, el resultado es indefinido (depende de la implementación).

Nota: en C++Builder y GNU-C++, el signo se mantiene, lo que significa que el desplazamiento se realiza contando con el signo, el nuevo bit más significativo será 0 si se trata de un número positivo y 1 si el número es negativo ( 2.2.4a).

Fuente: http://www.zator.com/Cpp/E4_9_3.htm

06 Enero, 2013, 12:03 pm
Respuesta #51

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
Una cosa que no te expliqué es por qué diablos mi cambio de 1 a 1u arregló todo.
La razón es muy oscura.

Tiene que ver con la promoción automática de tipos de datos enteros en una operación aritmética.
Fijate que, aunque (1u << k) ahora es un unsigned int, gracias al sufijo u, resulta que n = -2 sigue siendo negativo.

Así que, ¿qué es lo que ocurre cuando hacemos (n & (algo-unsigned-int))?

Además, debemos recordar que estamos en la situación en que el "algo" tiene un bit izquierdo igual a 1.

Ahí hay que estudiar las complicadas reglas de promoción de tipos.
La verdad es que no me quedan muy claras estas reglas (están en la sección 7.4 del libro de King).
Pero es posible que ambos operandos hayan promocionado a unsigned int antes de llevar a cabo la operación &, y entonces el resultado se consideraría de tipo unsigned int.
En ese caso, el posterior corrimiento a la derecha con >> k, k = 31, provocaría que se agreguen 0's por la izquierda...

 :banghead: :banghead: :banghead:

06 Enero, 2013, 12:24 pm
Respuesta #52

feriva

  • Matemático
  • Mensajes: 9,056
  • País: es
  • Karma: +1/-0
  • Sexo: Masculino
  • No soy matemático, eso es una etiqueta.


Me parece que en este caso lo más adecuado sería hacer una pausa con una llamada al sistema, así:

system("PAUSE");

previamente adosando la librería stdlib.h



 Muchas Gracias, Argentinator, eso es lo que buscaba, un comando tipo el stop() como el del Java u otros lenguajes así; sabía que tenía que tenerlo, porque el "C" es el padre de todos estos lenguajes.

 Yo recuerdo cuando apareció el "C" en el mercado allá por los años ochentaitantos, leí un artículo en una de aquellas revistas (No sé si era Micromanía, Microhoby, o Microsoft, que también entonces tenía su revista para usuarios de Spectrum, Comodore y MSX). Se decía que incorporaba una serie de características revolucionarias respecto de los lenguajes más usados en aquella época, como Pascal, Cobol o Basic. Y una de esas cosas era que se compilaba más fácilmente y que corría como un lenguaje de bajo nivel. Por entonces existían compiladores para el Basic, y muchos "videojuegos" (llamémosles así aunque haya una gran diferencia con los de hoy)  se escribían en ese lenguaje y después se compilaban (aunque había quien escribía juegos y programas pokeando binarios directamente en memoria, o sea, no ya en ensamblador, sino en el más puro código máquina; también leí un artículo, en una de esas revistas, sobre un estudiante de colegio que programó así un juego comercial para "Dinamic", una firma famosa de entonces de juegos para ordenador).
 Y es que el Basic de la primera época tenían comando llamado Poket con el que se podía hacer eso, meter los bytes a mano directamente en una dirección de memoria de la pila de máquina; así, por ejemplo, podías cambiar de golpe el color de la pantalla, la dirección del cursor, las mayúsculas... o introducir una orden "stop" que bloqueaba el Basic y tenias que apagarlo.

 Bueno, que me he enrollado :D voy a leer lo que habéis escrito.

 Saludos.

06 Enero, 2013, 01:57 pm
Respuesta #53

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
Una cosa que no te expliqué es por qué diablos mi cambio de 1 a 1u arregló todo.
La razón es muy oscura.

Tiene que ver con la promoción automática de tipos de datos enteros en una operación aritmética.
Fijate que, aunque (1u << k) ahora es un unsigned int, gracias al sufijo u, resulta que n = -2 sigue siendo negativo.

Así que, ¿qué es lo que ocurre cuando hacemos (n & (algo-unsigned-int))?

Además, debemos recordar que estamos en la situación en que el "algo" tiene un bit izquierdo igual a 1.

Ahí hay que estudiar las complicadas reglas de promoción de tipos.
La verdad es que no me quedan muy claras estas reglas (están en la sección 7.4 del libro de King).
Pero es posible que ambos operandos hayan promocionado a unsigned int antes de llevar a cabo la operación &, y entonces el resultado se consideraría de tipo unsigned int.
En ese caso, el posterior corrimiento a la derecha con >> k, k = 31, provocaría que se agreguen 0's por la izquierda...

 :banghead: :banghead: :banghead:


En el libro "The new C Standard", en la regla 714 (que requiere tener en cuenta algunas otras reglas previas), especifica esto:

Citar

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type signed integer
converted
to unsigned
of the other operand, then the operand with signed integer type is converted to the type of the operand with
unsigned integer type.

El "rank" o rango, se refiere a una definición que da en la regla 661, que básicamente considera que los tipos enteros long long (con o sin signo) tienen estrictamente mayor que los tipos long, y estos mayor rango que los int, y estos mayor ranto que los short, y estos mayor que _Bool.

Es sólo una definición matemática, un orden parcial.

Además, ambos long long (con o sin signo) se consideran del mismo rango, así como ambos long, etc.

_________

La consecuencia es que (signed int) & (unsigned-int) obliga a promocionar a ambos operandos a (unsigned int).
En particular, si tu lado izquierdo era el negativo n = -2, ahora se convirtió en un número positivo, vaya uno a saber cuál.

Esta regla puede tener muchos resultados indeseados.

Fijate lo que pasa cuando ejecutamos estas dos líneas:

   printf("Es positivo? %d\n\n", (-1 + 0u) > 0);
   printf("Es positivo? %d\n\n", (-1 + 0) > 0);

En el primer caso, el resultado es 1,
y en el segundo es 0.
 ??? ??? :o :o :o :o

Y eso que son números muy sencillos: estoy comparando el -1 contra el 0, a ver cuál es mayor.
En el primer caso, como 0u es de tipo (unsigned int) y el -1 es de tipo (signed int), la regla 714 nos dice que, en la operación binaria (de suma en este caso) (-1 + 0u): el -1 se convierte a (unsigned int). Luego sumamos, y el resultado es un (unsigned int).
Como es distinto de 0, y no tiene signo, al compararlo con 0 nos da que es mayor.

En el segundo casi, tanto el -1 como el 0 en la suma -1+0 son de tipo (signed int), y así no hay promoción. La suma me da -1, como debe ser, y al comparar con 0 me da que es negativo.

__________

Cuando intentamos visualizar el valor de (-1 +0u), obtenemos con printf el valor -1.
O sea que "pareciera" que está todo bien.

En realidad, el printf, con el modificador %d, lo que hace es asumir que "lo que le tiramos ahí" es un "signed int".

Así que, aunque (-1 + 0u) es un unsigned (positivo), el printf lee la versión binaria de un "signed", que coincide con -1.

Si no me creés que esto realmente funciona mal, lo que podemos hacer es dividir por 2.
Si el "-1" es legítimo, la división entera por 2 tiene que dar resultado 0: (-1 / 2) en C da 0.
Pero si yo tengo razón, y el (-1 + 0u) es "trucho", entonces lo que hacemos al dividir por 2 es tomar el número 1111.......... en tipo unsigned int ( que sería el \( 2^{32}-1) \)), y al dividirlo por 2 nos da un número entero positivo grande, que el printf es capaz de mostrar:

   printf("(-1 + 0)/2 = %d\n\n", (-1 + 0) / 2);
   printf("(-1 + 0u)/2 = %d\n\n", (-1 + 0u) / 2);

Cuando corro el programa con esas dos líneas, me da esta salida:

(-1 + 0)/2 = 0

(-1 +0u)/2 = 2147483647

Lo cual muestra que ando en lo cierto.  >:D >:D >:D

Pero fijate que no hay solución a este desperfecto, porque viene así definido ya desde las reglas del estándar (la regla 714).  :banghead: :banghead: :banghead:

06 Enero, 2013, 02:04 pm
Respuesta #54

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)


Me parece que en este caso lo más adecuado sería hacer una pausa con una llamada al sistema, así:

system("PAUSE");

previamente adosando la librería stdlib.h



 Muchas Gracias, Argentinator, eso es lo que buscaba, un comando tipo el stop() como el del Java u otros lenguajes así; sabía que tenía que tenerlo, porque el "C" es el padre de todos estos lenguajes.

 Yo recuerdo cuando apareció el "C" en el mercado allá por los años ochentaitantos, leí un artículo en una de aquellas revistas (No sé si era Micromanía, Microhoby, o Microsoft, que también entonces tenía su revista para usuarios de Spectrum, Comodore y MSX). Se decía que incorporaba una serie de características revolucionarias respecto de los lenguajes más usados en aquella época, como Pascal, Cobol o Basic. Y una de esas cosas era que se compilaba más fácilmente y que corría como un lenguaje de bajo nivel. Por entonces existían compiladores para el Basic, y muchos "videojuegos" (llamémosles así aunque haya una gran diferencia con los de hoy)  se escribían en ese lenguaje y después se compilaban (aunque había quien escribía juegos y programas pokeando binarios directamente en memoria, o sea, no ya en ensamblador, sino en el más puro código máquina; también leí un artículo, en una de esas revistas, sobre un estudiante de colegio que programó así un juego comercial para "Dinamic", una firma famosa de entonces de juegos para ordenador).
 Y es que el Basic de la primera época tenían comando llamado Poket con el que se podía hacer eso, meter los bytes a mano directamente en una dirección de memoria de la pila de máquina; así, por ejemplo, podías cambiar de golpe el color de la pantalla, la dirección del cursor, las mayúsculas... o introducir una orden "stop" que bloqueaba el Basic y tenias que apagarlo.

 Bueno, que me he enrollado :D voy a leer lo que habéis escrito.

 Saludos.

En el no muy famoso sistema operativo DR-DOS 6.0, alguna vez fui capaz de meter "bits en bruto" en la línea de comandos, logrando borrar la pantalla.

Otra cosa que uno puede hacer, si quiere jugar con bits en bruto, es mandar bytes diseñados a mano a una impresora matricial, anteponiendo un comando de la impresora que admite "modo gráfico" por así decirlo, y uno puede controlar el dibujo de lo que quiera ahí...

Son cosas de frikis todas estas cuestiones...

La cuestión es que en las viejas épocas es común que se haya elogiado al C por encima de otros lenguajes.

Aún así, el C en esas épocas no estaba tan pulido ni era tan bueno como lo fue años después.

Hoy día el C tiene que competir con lenguajes que tienen paradigmas más modernos, y que encima son sólidos.

Creo que una de las cosas que permiten sobrevivir al C es que  uno puede aprender lo básico de él fácilmente, como para largarse a programar. Es simple, dentro de todo.

____________-

Ahora, fijate lo que le contesté a PabloN: escribí un par de líneas que comparan -1 con 0, y en un caso me da que -1 es mayor y en otro que es menor.  >:D

06 Enero, 2013, 02:11 pm
Respuesta #55

feriva

  • Matemático
  • Mensajes: 9,056
  • País: es
  • Karma: +1/-0
  • Sexo: Masculino
  • No soy matemático, eso es una etiqueta.
Bien. Una cosa: Pensad (me dirijo también a Pablo como profesor adjunto, si no te pones celoso :D ) que yo en esto soy un hombre de Crogmanon; que en sus tiempos sólo fue hombre de Crogmanon aficionado y además un hombre de Crogmanon con menos memoria de la que tenían los primeros ordenadores personales.
  Por tanto, si vas a explicar estas cosas que dice Pablo, tendrás que ir pasito a pasito y despacio porque si no me va a costar muchísimo seguirte.

 El primer ordenador que tuve (con 27 años, ya no era un niño) tenía 48 kb de memoria RAM (incluida la reservada para el sistema) no tenía disco duro y su microprocesador era un Z80 (así que daos cuenta). Muchos años después tuve ya un PC que me regalaron y en el cual todavía se incluía el Basic como lenguaje de programación que venía de fábrica.
Aprendí muy por encima algunos comandos de MS2 y leí y puse en practica sólo lo básico. En cuanto al código máquina y la programación del micro (cómo opera el microprocesador mediante la alteración del séptimo bite para considerar números negativos -o sea, lo del complemento a dos- y después el comando ADD, carga de registros en el acumulador "A", los saltos JUMP y todas esas cosas) también leí un par de libros sobre el Z80 y el 486, pero como curioso nada más, no como estudiante.
 
 Lógicamente, tengo mejor grabadas en mi memoria, y tampoco demasiado, las cosas antiguas, y las nuevas, por las que me he interesado más tarde o muy recientemente, me cuestan más.
 El Basic era un lenguaje de un nivel muy alto donde todo era muy intuitivo, aprendías a programar sólo con analizar rutinas someramente, porque eran literales ("for a=1 to 6; next a" y cosas así).
 Aunque después he leído y he hecho algunas cosillas de vez en cuando sobre programación tipo lenguajes Java y PHP, no termino de acostumbrarme a pensar con soltura en ellos, tengo que ir despacio, por la sintaxis.

 Otra cosa más por último. Ya que Pablo habla de Java, sí que me parecía interesante, más adelante en el curso, introducir conceptos y rutinas de los lenguajes que yo llamo de la familia Java (que son todos el mismo, prácticamente, salvo dos o tres cosas) o bien montar un curso especial para estos lenguajes. No creo que sean, hoy en día, menos importantes que el "C" ( :o ) las webs de todo tipo están repletas de programación Java y PHP, son los lenguajes imprescindibles para la programación en Internet; fíjate por ejemplo en el CSM que utiliza el foro, está hecho en PHP (y supongo que también llevará Javascript) lo mismo que todos, todos, los gestores de contenidos que existen, Simple Machines Forum, Joomla, Oscomerce, los Wikis de todo tipo...

 Así que yo creo que, más adelante, sería estupendo un curso de lenguajes Java; o bien bifurcar este mismo curso, ya que tienen comandos y cosas en común con C. Existen cursos gratis de éstos en la red, pero no con tutor; los que llevan tutorías incluidas son de pago.

 Saludos.
  

06 Enero, 2013, 02:17 pm
Respuesta #56

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
Bueno feriva, esta es la sección de comentarios, y da para hablar de cualquier cosa.

Me he adelantado bastante con temas complicados como el de la promoción de tipos de datos enteros, porque tiene que ver con el programita que hizo PabloN.

Pero, en lo que se refiere al curso que voy llevando, basta conque se entienda lo que he escrito hasta ahora en las notas teóricas.

Así que no te preocupes si no se entiende alguna cosa que discutamos acá en los comentarios, porque pueden estar muy salidas de la teoría.

En las notas teóricas he analizado sólo la primer parte de la discusión sobre la promoción de tipos de datos enteros.
No he llegado aún allí al tema de la promoción de tipos, en presencia de operadores, sino sólo la adjudicación de tipos según cómo se escriban las constantes numéricas en un programa fuente.

Lo que le expliqué a PabloN está en un terreno mucho más resbaladizo todavía.


06 Enero, 2013, 02:19 pm
Respuesta #57

feriva

  • Matemático
  • Mensajes: 9,056
  • País: es
  • Karma: +1/-0
  • Sexo: Masculino
  • No soy matemático, eso es una etiqueta.


Ahora, fijate lo que le contesté a PabloN: escribí un par de líneas que comparan -1 con 0, y en un caso me da que -1 es mayor y en otro que es menor.  >:D

Estaba escribiendo la respuesta anterior y no había ésta visto aún. Luego lo miro despacito; como ya decía ahí, soy un Cromagnon, hago fuego con dos palos y poco más, vosotros sabéis un montón y lo tenéis bien aprendido como profesionales, pero yo...  :P programo más "intuitivamente", a base de probaturas, método prueba-error-fallo-desastre (anoche día reinstale la IDE, porque la dejé para la rastre de tocar cosas. Así es como aprendo yo, equivocándome; vamos, quiero decir más bien que espero aprender algún día :D )

Saludos

06 Enero, 2013, 02:29 pm
Respuesta #58

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,272
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
  • Vean mis posts activos en mi página personal
    • Mis posts activos (click aquí)
Bueno feriva, pero lo que pasa es que todo depende de qué cosa estemos haciendo.

Si se trata de programar cosas más o menos normales, seguro que ya sabés de qué va el tema, y no vas a tener dificultades.

Pero yo lo que me he puesto a discutir es sobre las especificaciones técnicas más intrincadas del lenguaje C.
Ese tipo de cosas yo las estoy aprendiendo ahora, y me sorprende descubrir algunos detalles inesperados.

Meterse en los detalles técnicos de la definición del lenguaje C es costoso, porque no requiere programas complicados, sino mucho esfuerzo mental, jejeje.

Fijate que el programita que demuestra que el C anda para el diablo, tiene dos líneas:

#include <stdio.h>

int main(){
   
   printf("(-1 + 0)/2 = %d\n\n", (-1 + 0) / 2);
   printf("(-1 + 0u)/2 = %d\n\n", (-1 + 0u) / 2);
   getchar();
}

En ambas líneas se muestra el resultado, supuestamente, de dividir -1 por 2.
Como la barra / hace división entera, toma la parte entera, y descarta los decimales.
El resultado esperado de -1/2 es, entonces, igual a 0.

La primer línea muestra ese resultado correctamente cuando se ejecuta el programa.
Pero la 2da línea muestra de resultado 2 mil millones y pico, que es un disparate total.

La razón de fondo es que hay un problema con la conversión de varios tipos distintos de enteros en las sumas, lo cual trae aparejado resultados impredecibles a nivel de bits, y el programa termina interpretando cualquier cosa.

No hace las operaciones en forma correcta matemáticamente.

Este ejemplo es muy sencillo, y aunque sea algo arduo entender cómo se genera ese error, lo importante es entender cómo evitar que se produzca, porque fijate que se trata de operaciones muy sencillas (-1 más cero dividido por 2), que nos dan un desastre inaceptable.

La moraleja de esto es que hay que llevar el control cuidadosamente de los tipos de datos que estamos usando.
Y en lo posible, ser uno quien obligue a que todos los operandos de nuestros cálculos pertenezcan uniformemente a un mismo tipo de datos.

Esto se logra de varias maneras, que otro día explicaré.

_______________

En cuanto a explicar Java, no será parte de este curso...  :'(


06 Enero, 2013, 03:51 pm
Respuesta #59

feriva

  • Matemático
  • Mensajes: 9,056
  • País: es
  • Karma: +1/-0
  • Sexo: Masculino
  • No soy matemático, eso es una etiqueta.


Meterse en los detalles técnicos de la definición del lenguaje C es costoso, porque no requiere programas complicados, sino mucho esfuerzo mental, jejeje.


Claro, mi problema es que, por culpa de mi memoria y falta de práctica, cada vez que tengo que acordarme de cuántos bits, en decimal, tiene un byte o cosas así (de esto sí me acuerdo, creo que eran 256) pues se me olvida y tengo que buscar en libros o en Internet; y por culpa de no tener memorizados esos datos básicos que habitualmente cualquier profesional tiene en su cabeza, me cuesta esfuerzo y voy despacio; por eso y por la vista  8^) también, pero ésa es una "despaciéz" aparte, de etiología distinta a la que estamos hablando.
 No suelo tener dificultad en cuanto a comprensión, si una cosa es lógica y está bien explicada, la suelo entender; puedo tardar más o menos, según lo largo y abstruso del argumento, pero si me pongo, acabo entendiendo. Mi cruz son las muchas nomenclaturas, numeritos, símbolos y sus significados, que o no me los sé o se me olvidan fácilmente; esto se soluciona consultando a don Google cada dos por tres, pero, claro, se resiente la "dinámica", es lento.

Citar

Fijate que el programita que demuestra que el C anda para el diablo, tiene dos líneas:

#include <stdio.h>

int main(){
   
   printf("(-1 + 0)/2 = %d\n\n", (-1 + 0) / 2);
   printf("(-1 + 0u)/2 = %d\n\n", (-1 + 0u) / 2);
   getchar();
}


 ¿Cómo entiende "0u" la maquina, qué es para ella la u, un indicador de estado de algún bit de cierto byte relacionada con alguna dirección importante de memoria; una orden...? Quizá lo supe, pero si lo supe no me acuerdo; ¿ves?, es lo que me pasa. 

Citar

En cuanto a explicar Java, no será parte de este curso...  :'(



Bueno, no te preocupes, comprendo que das muchos cursos a la vez y tiene que ser un lío; pero quizá algún moderador experto en informática podría...   ::)

Saludos.