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

#31
Claro que lo entendí. Y su problema más que con el cast, era con lo que le explicó MinusFour. Estás mezclando y confundiendo varias cosas. Por ejemplo, la alineación no tiene nada que ver en este caso. Le fallaba el programa porque aux->data tenía un puntero a puntero a GeneralNode, pero en su código lo trataba como puntero a GeneralNode. Al desreferenciarlo, estaba sólo haciéndolo con el primer puntero. En todo caso, el cast correcto debería haber sido:

GeneralNode* tmp = *(GeneralNode**)aux->data;

Pero de ninguna manera el puntero a size_t era correcto. Esta línea

   GeneralNode *tmp =(GeneralNode *)*((size_t*)aux->data);

aunque compile y funcione, no es correcta. En ningún lugar donde se dediquen a esto lo aceptarían.

Y por supuesto que entendí por qué tu solución funcionó. Llevo muchos años trabajando en esto profesionalmente, casi todos ellos en C o C++, como para no entender algo tan básico. Además, sí que estabas usando un size_t, aunque de forma implícita. Cuando haces esto

*((size_t*)aux->data)

el asterisco más a la izquierda desreferencia el puntero y convierte su valor a size_t. Ya desde ahí tu código está equivocado. Estás invocando UB (undefined behavior, comportamiento indefinido). Para empezar, size_t no necesariamente tiene el mismo tamaño que un puntero y, de hecho, en algunas plataformas es diferente, por lo que, en teoría, se podría perder precisión con esa conversión intermedia. Ahora, en realidad en todas las plataformas actuales, size_t sí que ocupa lo mismo que la mayoría de punteros, pero aún así, eso es incorrecto, es UB. No lo digo yo, lo dice el estándar del lenguaje. Sí, es un error algo común pensar que size_t (o sus punteros) es apto para punteros "genéricos". No lo es. Como ya había mencionado, a veces sí se usa, pero sólo en casos muy concretos y a  sabiendas de que no es algo estándar. El ejemplo de este tema de ninguna manera justifica su uso. En todo caso, para eso existe uintptr_t. Aún así, y esta es la verdadera cuestión del asunto, cuando tenemos un GeneralNode**, como prosebas en su código original, lo que se hace es convertirlo a GeneralNode**; nada más.

Ya perdí más tiempo del que hubiera querido, pero, una última cosa. Te recomiendo que leas más sobre punteros. En tus comentarios de este hilo noto más o menos lo mismo que en tus dudas sobre ensamblador: tienes una cierta idea del tema, pero confundes y mezclas muchas cosas. Lo peor que puede uno hacer es creerse un experto cuando apenas empieza (por el tipo de dudas que recuerdo que has planteado, y por lo que escribes en este tema, no parece que tengas mucha experiencia) porque ahí se deja de aprender. Si aún piensas que estás en lo correcto, ¿por qué no pones el código con tu línea en stackoverflow y preguntas si es correcta? Te garantizo que te van a responder que no, y sus respuestas no van a variar mucho de lo que yo te dije. No te tomaría ni 5 minutos copiar y pegar, y saldrías de dudas. O te puedes quedar así, convencido de que tienes razón porque sí. Cada quién.

Editado: veo que MinusFour ya te había respondido. Y sí, de nuevo, el código que él pone es el correcto.
#32
El problema concreto que tenías se arregla como te dijo MinusFour. El cast de la línea 12 tal como lo tenías ya estaba bien. El size_t es innecesario e incorrecto. Jamás vas a tener problemas de alineación si usas punteros al mismo tipo del dato al que deseas referenciar. aux->data se refiere a proof, que apunta a un bloque reservado mediante malloc(sizeof(GeneralNode)); por lo que (GeneralNode *) es lo único que necesitas. Ese size_t no tiene ninguna razón para estar ahí.

Más aún, esto:

   int x;
   size_t *p;
   p=(size_t *)&x;
   *p=1;


viola las reglas de C. Simplificando un poco, sólo es válido acceder a un objeto o variable de tipo A mediante un puntero al mismo tipo A (o a un tipo compatible), o bien, mediante un puntero char. size_t, por supuesto, no es un tipo compatible con int, y mucho menos con GeneralNode. En un ejemplo tan sencillo, seguramente no habría problemas con ese código, pero independientemente de eso, es una clara violación a las reglas del lenguaje; en concreto, a la llamada "strict aliasing rule", por lo que, aunque compile, es un error, y el compilador es libre de realizar optimizaciones que hagan que tu programa funcione de forma incorrecta. A veces puede ser válido saltarse algunas reglas, y de hecho los compiladores a veces ofrecen opciones para estos casos, tipo -fno-strict-aliasing, pero esto sólo se debería hacer si es 100% necesario y siendo conscientes de que nos salimos del estándar y nuestro código ya no es no portable.

Las reglas que definen qué tipos son compatibles con otros pueden ser algo complejas, pero como consejo y regla general, a menos que sepamos muy bien lo que hacemos y tengamos amplia experiencia, sólo deberíamos usar punteros de exactamente el mismo tipo que la variable a referenciar (o punteros char, que es la excepción a la regla).
#33
El problema con 64 bits pasa porque esta expresión:

Código (cpp) [Seleccionar]
(1<<i)

tiene el tipo del operando izquierdo (la constante literal 1), o sea int. En otras palabras, en realidad estás efectuando el desplazamiento sobre un valor de 32 bits. Si conviertes ese 1 a int64_t, debería funcionar.
#34
Cierto, fue un resbalón escribit que primero se incrementa. Lo que quise decir es que, dada la precedencia de operadores, primero se evalúa la expresión dir_numeros++ (que, al tratarse de postincremento, nos da el valor actual de dir_numeros), esto se desreferencia, y después se incrementa el puntero.
#35
El operador ++ tiene mayor precedencia que el *, por lo que primero se incrementa el puntero y luego se "desreferencia". En cambio así:

Código (cpp) [Seleccionar]
(*dir_numeros)++

incrementas el valor.
#36
Programación C/C++ / Re: Procesos con fork
23 Mayo 2020, 17:11 PM
A simple vista el error que veo es en el último proceso hijo. Siempre debes cerrar los descriptores que no usas en cada proceso, y en ese último no lo estás haciendo con los de fd. Eso debe estar provocando un bloqueo.

Además, el wait final lo estás usando incorrectamente. Deberías pasarle NULL como parámetro (o usar waitpid, si quieres especificar pids concretos, pero no lo veo necesario).
#37
Me parece un poco ambiguo lo de cada vez que llegue a una 'a'. O sea, si la cadena no empieza con 'a', como en "xyzajndfvaorncak89s", ¿la primera subcadena debería ser xyz, o ajndfv? Como lo planteas parecería que es la primera, pero a mí me parece más lógica la segunda, así que me enfoco en ella.

Una opción es la función strtok, que ya te mencionaron, pero ten en cuenta que es para cadenas de C (arreglos o punteros char). Aunque hay forma de acceder al buffer interno de string, por varias razones podría no funcionar con strtork, además de que esta función es "destructiva"; es decir, modifica la cadena origen, por lo que al final tu string quedaría alterado. En todo caso, si quieres usar strtok, sería mejor crear una copia de tu string en un arreglo char, y trabajar sobre ella.

Una alternativa es usar las funciones miembro de string find() y substr(). Un ejemplo para encontrar la primera subcadena:

Código (cpp) [Seleccionar]

std::string::size_type inicio, fin = 0;

inicio = cadena.find('a', fin);

// Si se encontro una letra 'a'
if (inicio != std::string::npos) {
 fin = cadena.find('a', inicio + 1);
 subcadena = cadena.substr(inicio, fin - inicio);
}


Ya sólo te faltaría crear el programa y hacer un bucle basándote en esto.
#38
Cita de: marax en 16 Mayo 2020, 18:01 PM
Si me permites otra pregunta, ¿cual fue la primera version de Windows y/o Linux que utilizo GDTs?

Bueno, considerando que las GDTs no existen en modo real, y que en el protegido no son opcionales (crear una y pasársela al procesador es requisito para siquiera iniciar este modo), la respuesta sería: la primera que corriera en este modo. En el caso de Linux, todas, desde la primera versión del kernel, lo han hecho. Con Windows no estoy seguro, pero probablemente la Windows/386. En esos tiempos Windows no era un sistema operativo, sino una interfaz gráfica encima de DOS, pero me parece que esa versión sí iniciaba el modo protegido al ejecutarse. Ya como SO propiamente dicho, todas sus versiones, NT o 9x, corrían en este modo, por lo que necesariamente usaban una GDT.
#39
¿Hablas de modo real o protegido? Porque es muy diferente según el caso y veo que se están mezclando los dos.

En modo real las direcciones sí son de 20 bits y se calculan multiplicando el selector de segmento por 16 (que añade 4 bits 0) para formar una dirección base de 20 bits y a esto se le suma el offset para obtener la dirección efectiva.

En modo protegido, las direcciones son de 32 bits (quitando el 80286, en el cual son de 24 bits), y aunque se sigue usando un selector de segmento y un offset, el proceso de conversión es muy diferente. En este modo, existe una tabla global de descriptores de segmento. Cada uno de estos descriptores es una estructura de 64 bits con información acerca de un segmento, como su dirección de inicio, su dirección máxima válida (que sirve para calcular su tamaño), nivel de privilegio, permisos, etc. Entonces, cuando tenemos una dirección lógica, primero se toma el selector de segmento, que en este caso hace referencia a un descriptor dentro de la tabla global; después, de dicha tabla se obtiene la dirección base del segmento; luego se le suma el offset para obtener una dirección lineal. Si no se usa paginación, esta dirección ya se refiere directamente a una dirección física de memoria. En caso de que se use paginación (que, por cierto, sólo está admitida en modo protegido) se hace una conversión adicional, que varía según el tamaño de página usado. Y, porque quizás más adelante te surja esta duda, te comento que aunque en modo protegido se puede usar un modelo de memoria plano, la segmentación sigue estando presente. La ventaja es que todo esto se realiza de forma interna y, en general, transparente al programador. Es decir, usando modelo plano, te puedes olvidar de la segmentación, y escribir programas como si no existiera.

En cuanto a lo que preguntabas sobre la memoria de los procesos, únicamente en modo protegido se ofrece protección. Para eso se puede usar tanto la segmentación como la paginación. En el caso de la primera, una forma es mediante tablas de descriptores locales. Cada tarea tiene una propia, la cual contiene únicamente los segmentos válidos para ella (aparte de los de la tabla global, que son accesibles a las tareas, siempre que los niveles de privilegio lo permitan). Todo esto podría sonar muy ineficiente, pero el procesador tiene maneras de hacer que estas verificaciones se realicen lo más rápido posible, como registros que guardan información de la tabla global y de la tarea actual, entre otras cosas
#40
ASM / Re: Declaracion de segmentos en TASM
8 Mayo 2020, 17:29 PM
Sé que no hay desperdicio; eso lo puse como ejemplo de una conclusión errónea a la que se podía llegar. Pero tienes razón, aquí yo estaba mezclando selectores de segmento con los segmentos que el ensamblador declara. Y es que hace demasiado tiempo que no toco nada de esto, y si ya de por sí el tema de la segmentación en x86 es complejo...

Cita de: marax en  8 Mayo 2020, 11:15 AM

Código (asm) [Seleccionar]

SEGMENT cseg 'code'


Eso último entre comillas simples es el nombre de "clase". El ensamblador pone juntos en memoria los segmentos pertenecientes a una clase, independientemente del orden en que hayan sido declarados o si están en diferentes archivos. Hay algunos nombres predefinidos y "code" es uno de ellos. Ése es básicamente su uso.