Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - RayR

#81
Programación C/C++ / Re: ¿SetPixel con un array?
9 Septiembre 2019, 23:56 PM
Justamente los comentarios de la cabecera explican su uso, pero básicamente hay que llamar a la macro HEADER_PIXEL. Algo así:

char pixel[3];
char *datos = header_data;
...
HEADER_PIXEL(data, pixel)


Una vez que llamas a HEADER_PIXEL, pixel[0], pixel[1] y pixel[2] contendrán los valores para los componentes rojo, verde y azul, en ese orden, del primer pixel. La siguiente vez que llames a HEADER_PIXEL, los valores de pixel serán actualizados a los del siguiente pixel y así sucesivamente.

Pero realmente es un "formato" feo y no muy eficiente. Tienes que llamar a esa macro que hace operaciones a nivel de bits porque los valores RGB se almacenan de tal manera que cada byte guardado sea un caracter ASCII imprimible, por lo que ocupan parte de un byte, y parte de otro. Te recomendaría que consideres usar algún formato de verdad. Por ejemplo, el BMP es tan simple que escribir una función que lo lea es una tarea prácticamente trivial; ni siquiera hace falta recurrir a bibliotecas externas.
#82
Un consejo general sobre la optimización es que no te enfoques tanto en ello mientras estás aprendiendo. Lo conveniente es que te limites a optimizaciones a nivel de diseño, que consisten en elegir los algoritmos y tipos de datos abstractos adecuados para cada problema. Ejemplos son los distintos métodos de ordenamiento y tipos de datos como pilas, listas, árboles, y seguramente te los enseñarán más adelante.

Luego hay otros tipos de optimizaciones que se pueden hacer, pero dependen del lenguaje y la arquitectura del hardware para la que se esté programando. Pueden ser muy tentadoras pero es muy difícil saber realmente si conviene aplicarlas o no (sobre todo en el caso de las optimizaciones dependientes del hardware). Los procesadores actuales son extremadamente complejos, y cuando optimizamos algo, probablemente estamos empeorando otra cosa distinta. Hay tantos factores en juego que casi ninguna optimización es general. Hay que evaluar cada caso por separado.

Un ejemplo es el uso de índices de 64 bits. Es un buen consejo, ya que, efectivamente, con índices de 32 bits se puede requerir alguna instrucción adicional para expandirlos a 64. Pero ojo, que esto sólo aplica si compilamos para 64 bits. Si compilas para 32 bits (y es muy probable, ya que los IDEs de forma predeterminada suelen compilar para esa arquitectura), por más que uses procesador y sistema operativo de 64 bits, el programa se ejecutará en modo 32 bits, por lo que no habrá conversión, y de hecho, dado que en ese modo el procesador no puede realizar operaciones con enteros mayores a 32 bits, al usar datos de 64 bits, el compilador deberá generar instrucciones intermedias para obtener el resultado de 64 bits, haciendo tu código bastante ineficiente. Compilando para 64 bits sí podrías obtener algún beneficio al usar índices de 64 bits, pero ojo, esto aplicaría para índices y poco más. Los índices de 32 bits de expanden a 64 porque el acceso a elementos de un array se hace con aritmética de punteros, que en modo 64 bits ocupan precisamente 64 bits, por lo que los índices deben tener ese tamaño. Para otro tipo de operaciones, las variables de 32 bits no son expandidas (a menos que las  usemos en operaciones combinándolas con datos de 64 bits), por lo que no tendría sentido declararlas como de 64. Y es que, contrario a lo que a veces se lee, los procesadores, al menos los x86-64 (Intel/AMD), en general son al menos igual de eficientes (a veces más eficientes) al operar con 32 bits. Por ejemplo, en un procesador Intel Core de la generación actual, la división entera de datos de 64 bits ocupa 36 ciclos de reloj, mientras que con operandos de 32 bits sólo ocupa 10 ciclos. Esto trabajando en modo 64 bits. Luego hay otros factores a tener en cuenta, como el hecho de que los datos más grandes llenan más rápido los buses y cachés, pero no nos compliquemos. El caso es que es bastante complejo determinar el costo/beneficio. Por eso, mejor, de momento al menos, limitarse a las optimizaciones a nivel de diseño/algoritmo, que son más generales.

Por cierto, el tipo de datos recomendado para índices es el size_t, que ocupará 32 o 64 bits en función de la arquitectura para la que compiles, por lo que si declaras así tus índices, siempre se usará el tamaño "óptimo".
#83
Programación C/C++ / Re: C, ¿cómo avanzar?
26 Agosto 2019, 21:11 PM
Sigue leyendo y, sobre todo, haz programas. Un error que cometemos muy comúnmente es sobrestimar nuestro conocimiento. Cualquier libro, incluso de los buenos, por sí sólo únicamente te enseñará una pequeña parte de lo que hay que saber, así que lo que sigue es, además de buscar libros sobre cosas más avanzadas, hacer muchos programas para asegurarte de haber aprendido bien lo que leíste. Sólo con programas de libro no vas a llegar muy lejos. Puedes haberte leído un manual de 1000 páginas de C o C++ y haber hecho todos los ejercicios, y aún así, tu nivel no pasará de básico, a menos de que por tu cuenta intentes hacer cosas más complejas y bien hechas.

Te voy a dar un ejemplo muy sencillo que siempre me ha parecido muy útil para ilustrar el punto (es un ejercicio para hacerse uno mismo, no hace falta responderlo en un foro).  Imagina que tienes el siguiente fragmento de código:

printf("Escribe un numero: ");
scanf("%d", &num1);
printf("Escribe un numero: ");
scanf("%d", &num2);


Es un simple programa que pide dos números (y digamos que al final los muestra, para comprobar que los leyó correctamente). Es casi un "Hola mundo", un juego de niños, ¿cierto...? ¿Pero qué pasa si el usuario escribe letras en lugar de números? Sin consultar ningún libro, simplemente observando esas 4 líneas, ¿sabes exactamente de qué manera va a fallar el programa? ¿Que vería el usuario luego de teclear, digamos, "asdf" en el primer scanf? ¿Por qué falla de esa forma? E igualmente importante, ¿podrías corregirlo, de nuevo, sin consultar manuales e incluso sin recurrir a un compilador? No necesitaría ser perfecto; simplemente que lea dos números y, si el usuario escribió símbolos que no son números, lo detecte, informe del error, y vuelva a pedir el número, todo esto con scanf. Ojo, que hay mejores formas de leer datos, por ejemplo, usando fgets y parseando la cadena leída, pero en este caso de lo que se trata es de ver si sabemos cómo funciona scanf, que es una de las primeras funciones que se aprenden en C. Y ya que estamos ¿por qué en la enorme mayoría de los casos, al trabajar con ints, deberíamos usar el especificador %d en lugar de %i (que muchos tutoriales usan :-\)?

La cuestión es que muchísima gente que se ha leído libros enteros sobre C no es capaz de corregir mentalmente esto que es poco más que un Hola mundo. Y esto no es una crítica, ni a los programadores ni a los libros. Es entendible: escribir código correcto y (casi) libre de errores es muy difícil, tedioso, y conlleva muchísimo trabajo. Cualquier manual tendría que tener 2000 páginas o más. El punto aquí es entender que los libros únicamente nos dan una base. Enseñan poco sobre muchos temas. Lo que nos toca después de leerlos es profundizar en ellos. El ejemplo de arriba tiene como objetivo hacernos pensar (suponiendo que no podamos contestar todas las preguntas/corregir el programa sobre papel): si en un ejercicio tan simple ya tenemos algunas carencias, ¿cuánto nos falta aprender sobre tantos temas que creíamos dominados? Te recomiendo que intentes hacer programas con lo que ya sabes, pero tratando de que sean correctos. Intenta hacer que tus programas fallen, y cuando lo hagan, investiga por qué y corrígelos. Esto te ayudará a profundizar en la programación de una forma en que difícilmente se consigue sólo con libros. Cuando sientas que ya eres capaz de hacer programas más o menos correctos con todos los temas básicos de C, busca libros, no tanto sobre el lenguaje en sí sino sobre aplicaciones del lenguaje. Programación de aplicaciones en red, multihilo, bases de datos, videojuegos o lo que te interese. Y de nuevo, ten presente que los ejemplos que veas en libros y tutoriales son sólo para fines didácticos. Están muy, pero muy lejos de tener calidad de producción. Te toca a ti mejorarlos y hacerlos casi a prueba de fallos. La mejor forma de aprender cómo funciona algo es rompiéndolo y viendo qué tiene dentro.

Sobre tu otra duda, si te gusta programar en C, aprender C++ podría ser una buena opción. La mayor parte de lo que hay en C es válido en C++, además de que tiene más salida laboral, y aunque actualmente hay varios lenguajes mucho más solicitados por las empresas, hay muy pocos buenos programadores en C++. Las empresas sufren bastante para conseguir personal calificado y en muchas ocasiones tienen que traerlos de otros países, o terminan contratando gente que no cumple al 100% con los requisitos, y los tienen que capacitar, y aún así muchas veces al final no se quedan, y se pierde tiempo y dinero en eso. Si aprendes realmente bien C++, no vas a tener mucha competencia y seguro no te faltará trabajo.
#84
En realidad en esas dos líneas no se está llamando a ninguna función; simplemente se están poniendo sus prototipos. Aunque normalmente se ponen al inicio del código y fuera de cualquier función, nada impide hacerlo dentro del cuerpo de una función, como en este caso. La única diferencia es que en este caso, sólo podrán ser invocadas desde la función o funciones que tengan los prototipos.
#85
Programación C/C++ / Re: Error C2662 - const
8 Julio 2019, 04:08 AM
Por cierto, respecto a tu segunda pregunta, begin y end no necesariamente debe ser funciones miembro, pero en caso de que sean funciones sueltas, necesitan estar en el mismo namespace que el objeto para que el compilador las encuentre; sin embargo, en ese caso hacer que puedas usarlos con objetos const se complica más, y tendrías que declarar versiones de begin y end que acepten y retornen const, así como una sobrecarga de sólo lectura (const) del operador [].
#86
Programación C/C++ / Re: Error C2662 - const
8 Julio 2019, 02:20 AM
Lo que pasa es que si tienes un objeto/struct const (o una referencia o puntero a objeto const), sólo puedes invocar a sus funciones miembro que también sean const. Si no fuera así, no habría forma de garantizar que el objeto sea realmente const (piensa, por ejemplo, que podrías llamar a sus "setters", con lo cual estarías modificando alguna de sus variables miembro). Al declarar una función como const, estás indicando que no modificarás ningún miembro del objeto, y el compilador se asegurará de que así sea: si dentro de una función miembro const intentas modificar alguna variable miembro, se producirá un error.

Ahora, cuando tienes algo como esto:

Código (cpp) [Seleccionar]
for (auto i : obj)

y obj hace referencia a un objeto, el "range-based for" internamente llama a obj.begin() y obj.end(), así que si obj es const, esas dos funciones también deben serlo, o el compilador no dejará que sean invocadas.
#87
 :huh:

No hay ninguna diferencia entre lo que haces en la línea 22 y la línea 35, desde el punto de vista de la no inicialización. De entrada, usar el valor de una variable no inicializada es totalmente incorrecto (por más que no sea un error de compilación), aunque supongo que lo sabes y este ejercicio es sólo un test para ver qué pasa.

Ahora, el hecho de que aparentemente funcione bien en una línea pero no en la otra no tiene nada de extraño. Una variable no inicializada (a menos que sea variable global o static, que siempre son inicializadas a 0) tendrá "basura", es decir, cualquier valor que hubiera en la posición de memoria ocupada por esa variable. Si la línea 22 te funciona bien, simplemente significa que dio la casualidad de que el valor aleatorio que contenía tam era positivo y no muy grande. Si en la línea 35 tienes error, es porque la variable x (y por cierto, nunca deberías usar un mismo identificador para un tipo y para una variable) contenía un valor negativo o demasiado grande: new falla y arroja una excepción si se le pide reservar un número negativo de bytes o una cantidad mayor a la máxima que es posible reservar. Esto último depende de la implementación, la arquitectura para la que estés compilando, la cantidad de memoria disponible en el sistema, etc.

Dado que no se puede predecir el valor de variables no inicializadas (es perfectamente posible que en otra PC, o con otro compilador, o con otra versión del mismo compilador, la línea 22 falle pero la 35 no, o que ambas fallen, o ninguna) siempre se deben inicializar antes de usarse.
#88
No lo ejecuté, pero tienes varios errores. Primero, la recomendación es que uses fgets en lugar de gets, pues esta última es una función insegura. Pasando a los errores, uno es que estás leyendo year con gets, pero year es un int, por lo que gets está sobrescribiendo memoria incorrectamente y obviamente year queda con basura. Usa scanf.

Otro, el getchar que usas es para eliminar el caracter de nueva línea '\n' que algunas funciones dejan en el buffer de entrada, pero gets no es una de esas funciones, por lo que, en tu programa, los getchar lo unico que ocasionan es que el primer caracter de la siguiente línea introducida sea descartado, es decir, en el ejemplo de la captura que pusiste, lo que en realidad se estaría almacenando en grupo es "ammstein", ya que la "R" es leída y removida por el getchar que le precede. Debes borrar todos los getchar y sólo ponerlos después de los scanf, que sí dejan el '\n'.

Y el que quizás sea el más importante, el parámetro de crearLista es de tipo incorrecto. Como sabrás, cuando quieres que una función sea capaz de modificar una variable (como un int, float, etc.) que le pases como argumento, hay que hacerlo por referencia, que en C significa pasar un puntero a dicha variable. Lo mismo pasa con los punteros: si quieres modificar un puntero (no sólo los valores a los que apunta, sino el propio puntero), debes pasarle un puntero a puntero. Porque tal como lo tienes, crearLista recibe una copia del puntero inicio, por lo que cuando haces esto:

inicio=newNodo;

lo que estás modificando es la copia de inicio que sólo existe dentro de crearLista. Al regresar a main, inicio (la variable local de main) seguirá apuntando a NULL. Cambia el prototipo y la definición de la función a:

void crearLista(NODO **inicio)

y dentro de esa función, donde ponías inicio, pon *inicio. Y la invocas así:

crearLista(&inicio);

Te reitero que no lo probé, por lo que no sé si haya más errores, pero esto debería corregir al menos el problema que tienes actualmente.
#89
Cita de: MAFUS en 26 Junio 2019, 00:21 AM
Pero entonces el usuario sabría qué número aleatorio saldría, violando una de las premisas del enunciado que dice:


¿Es en referencia a mi mensaje? Si es así, realmente no se viola la premisa. Por definición, si el usuario es capaz de deducir el número, significa que sabe el número. Podrá haberlo sabido apenas un segundo (o medio, o una décima) antes de decirlo, pero lo tuvo que saber. No puede saber y no saber al mismo tiempo. Dado que éste no es un foro de esoterismo sino de programación, doy por sentado que cuando Beginner Web escribe "sin saber el numero aleatorio que almaceno la variable dato" se refriere a sin conocer el número a priori, sin husmear en la memoria, o a cualquier otra forma de trampa; es decir, no usa el término "adivinar" en sentido literal, sino refiriéndose a si existe alguna forma efectiva para que el usuario deduzca el número generado.

En mi respuesta proporciono una forma de hacer eso, simplemente sabiendo cómo funciona la generación de números pseudoaleatorios en C (además de voltear a ver la hora al ejecutar el programa) y haciendo unos pocos cálculos sencillos. No considero que esto califique como trampa, de la misma forma que estudiar antes de un examen de álgebra, y por lo tanto, saber cómo se resuelve una ecuación, no es hacer trampa.
#90
Sí es posible, pero bajo ciertas condiciones. Como dices que sin "trampa", descarto accesos a la memoria de tu programa o modificaciones al mismo. En ese caso, lo que el usuario necesita para adivinar el número generado es saber como está implementada la función rand y conocer la semilla, en este caso, la hora exacta a la que time(NULL) calcula el valor que retornará. En un caso tan simple como tu código de ejemplo, donde time se ejecuta al inicio, esto básicamente equivale a saber a qué hora se ejecuta el programa (hora, minuto y segundo).

El valor retornado por time es el número de segundos transcurridos desde el 1 de enero de 1970. Así que, sabiendo la hora, no es difícil calcular este valor, que se usará como semilla para srand. Podría haber una variación de un segundo, por ejemplo, si tu programa es ejecutado a las 11:30:52, pero el segundo 52 estaba por concluir, puede que time se ejecute a las 11:30:53.

Esto no es sólo teórico. Por ejemplo, en Windows, el valor retornado por rand() la primera vez que se invoque es:

( (semilla * 214013L + 2531011L) >> 16 ) & 0x7fff

Así que si el usuario puede ver la hora, incluyendo segundos, a la que se ejecuta el programa, sí, es bastante factible que pueda adivinar el número al primer o segundo intento.