
Del básico al intermedio: Unión (I)
Introducción
El contenido expuesto aquí tiene un propósito puramente didáctico. En ningún caso debe considerarse una aplicación final, cuyo objetivo no sea el estudio de los conceptos mostrados aquí.
En el artículo anterior, Del básico al intermedio: Array (IV), tratamos un concepto muy interesante y extremadamente útil que, aunque muchos lo consideren avanzado, en mi modesta y humilde opinión, todo principiante en programación debería conocer y entender. Esto ocurre porque, si se utiliza bien, dicho concepto puede abrir literalmente las puertas del paraíso, ya que permite hacer cosas que, de otra manera, serían muy difíciles, por no decir casi imposibles, de realizar.
Además, este mismo concepto se utiliza en otro tipo de cosas que se verán en un momento más oportuno. Para evitar generar crisis de ansiedad, solo queda un consejo: procura estudiar y practicar bastante lo visto en el artículo anterior. Es fundamental que entiendas, comprendas y asimiles muy bien ese conocimiento, pues, sin él, absolutamente nada de lo que se verá de ahora en adelante tendrá sentido. Todo lo que se verá será como operaciones mágicas.
Quizás haya sido un poco exagerado decir que nada se comprenderá si no se entiende lo que se hizo en el artículo anterior. Sí, quizás haya sido un poco exagerado por mi parte. Sin embargo, esto no minimiza el hecho de que el artículo anterior es el más importante publicado hasta la fecha. Esto para quienes quieren convertirse en buenos programadores. Además de aquellos que desean y aspiran a poder hacer cualquier cosa en un lenguaje de programación, ya que el concepto mostrado en el artículo anterior no solo se aplica al lenguaje MQL5, sino a cualquier lenguaje que se caracterice por el uso adecuado de los recursos computacionales.
Vale, entonces, antes de empezar, tenemos que hablar de lo que es un requisito previo para este artículo. Aunque algunos instructores puedan pensar que estoy exagerando, en mi opinión, si no has comprendido, al menos superficialmente, lo que se vio en el artículo anterior, entender lo que se hará aquí será casi improbable. No estoy diciendo que no lo entenderás. Por favor, no me malinterpretes. Estoy diciendo que será, como mínimo, mucho más difícil que logres seguir lo que se explicará aquí.
Entonces, el artículo anterior sería como un punto de inflexión, donde, de un lado, tenemos todo un material básico de programación y, ahora, entraremos en un material un poco más avanzado. Y, como el nombre del artículo sugiere, aquí hablaremos de la UNIÓN. Pero no unión en el sentido explícito de la palabra, sino del término que se encuentra en algunos lenguajes, que es la UNIÓN. Y, como es costumbre, iniciaremos un nuevo tema para empezar lo que hará que el proceso de implementación y codificación sea mucho más divertido y placentero.
El nacimiento de la UNIÓN
En el artículo anterior, vimos la implementación de un código mostrado justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "Tutorial\File 01.mqh" 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. const uint ui = 0xCADA5169; 09. ushort us = 0x43BC; 10. uchar uc = B'01011101'; 11. 12. uchar Infos[], 13. counter = 0; 14. uint start, 15. number; 16. 17. PrintFormat("Translation personal.\n" + 18. "FUNCTION: [%s]\n" + 19. "ui => 0x%s\n" + 20. "us => 0x%s\n" + 21. "uc => B'%s'\n", 22. __FUNCTION__, 23. ValueToString(ui, FORMAT_HEX), 24. ValueToString(us, FORMAT_HEX), 25. ValueToString(uc, FORMAT_BINARY) 26. ); 27. 28. number = sizeof(ui) + 1; 29. start = Infos.Size(); 30. ArrayResize(Infos, start + number); 31. Infos[counter++] = sizeof(ui); 32. Infos[counter++] = (uchar)(ui >> 24); 33. Infos[counter++] = (uchar)(ui >> 16); 34. Infos[counter++] = (uchar)(ui >> 8); 35. Infos[counter++] = (uchar)(ui & 0xFF); 36. 37. number = sizeof(us) + 1; 38. start = Infos.Size(); 39. ArrayResize(Infos, start + number); 40. Infos[counter++] = sizeof(us); 41. Infos[counter++] = (uchar)(us >> 8); 42. Infos[counter++] = (uchar)(us & 0xFF); 43. 44. number = sizeof(uc) + 1; 45. start = Infos.Size(); 46. ArrayResize(Infos, start + number); 47. Infos[counter++] = sizeof(uc); 48. Infos[counter++] = (uc); 49. 50. Print("******************"); 51. PrintFormat("The Infos block contains %d bytes.", Infos.Size()); 52. ArrayPrint(Infos); 53. Print("******************"); 54. 55. Procedure(Infos); 56. 57. ArrayFree(Infos); 58. } 59. //+------------------------------------------------------------------+ 60. void Procedure(const uchar &arg[]) 61. { 62. Print("Translation personal.\n" + 63. "FUNCTION: ", __FUNCTION__); 64. 65. ulong value; 66. 67. for (uchar c = 0; c < arg.Size(); ) 68. { 69. value = 0; 70. for (uchar j = arg[c++], i = 0; (c < arg.Size()) && (i < j); i++, c++) 71. value = (value << 8) | arg[c]; 72. Print("0x", ValueToString(value, FORMAT_HEX), " B'", ValueToString(value, FORMAT_BINARY), "'"); 73. } 74. } 75. //+------------------------------------------------------------------+
Código 01
Al ejecutar este código 01, tenemos, como resultado, lo que se muestra en la imagen a continuación.
Imagen 01
En esta imagen 01 podemos observar que los valores definidos en las líneas 8, 9 y 10 se transfieren al procedimiento de la línea 60. Sin embargo, al observar el tipo de datos esperado por el procedimiento, vemos que no se trata de valores descritos, sino de un array. Algo que, aparentemente, no tiene ningún sentido, ya que muchas personas, al observar el procedimiento de la línea 60, esperarían ver allí operaciones que involucren un array. Pero no es exactamente eso lo que está ocurriendo. Lo que tenemos allí es la transcripción o traducción de los valores del array para poder reconstruir los valores transmitidos.
Observa que no tenemos forma de saber cuál es el nombre o el tipo de variable utilizada durante la llamada del procedimiento. Esto ocurre porque no existe ninguna referencia a esta información. Todo lo que sabemos es la cantidad de elementos y el valor de cada elemento presente en lo que sería cada variable.
Muchos programadores, e incluso algunos con bastante experiencia, consideran este tipo de enfoque completamente inaceptable, ya que no podemos asociar un valor a una variable en el código. Sin embargo, el hecho de que estos profesionales desprecien esta práctica hace que olviden o desconozcan el hecho de que, para la CPU, el nombre de una variable importa poco. Todo lo que ve la CPU es una serie de números. Nada más. No sabe cuál es el nombre de la variable o constante que estamos utilizando. Para ella, este tipo de información es completamente irrelevante.
Esta modelización, creada en la memoria según se ve en el código 01, es algo parecida a la representación que se muestra justo abajo.
Imagen 02
Voy a hacer hincapié en todo esto de aquí. Pues este concepto que se va a explicar puede resultar bastante confuso, sobre todo cuando veamos otro concepto muy similar.
En esta imagen 02 se muestran los valores destacados en la imagen 01, así como algunos marcadores. Estos marcadores aparecen en verde en esta imagen 02 e indican:
Aquí comienza un nuevo valor, y está compuesto por tantos elementos.
Los rectángulos azules representan cada bloque de elementos; es decir, si el array no se utilizara, sino que se usaran variables discretas. necesitaríamos seis variables discretas, ya que hay seis rectángulos azules en la imagen 02. Sin embargo, a pesar de esto, tenemos los rectángulos en rojo. Estos representan cada uno de los elementos presentes en el array. Como hay diez elementos, tenemos diez rectángulos en rojo, pero podríamos tener cinco si cada elemento estuviera compuesto por dos valores.
Sin embargo, debido a la fragmentación, que, en mi opinión, es un tema muy complejo para tratar aquí y ahora, utilizamos un valor mínimo para el tamaño de cada elemento. Así evitamos la fragmentación, aunque esto haga que el proceso de decodificación de la información sea un poco más lento. Este proceso de decodificación lo realiza el bucle de la línea 70, aunque muchos imaginen, erróneamente, que se realiza a través del bucle de la línea 67. En realidad, es el bucle de la línea 70.
De acuerdo. Creo que entender esta parte no ha supuesto ningún problema hasta ahora, pero quiero que mi querido lector observe con atención esta imagen 02 y se detenga a pensar: ¿no habría una forma de leer los rectángulos azules sin tener que pasar necesariamente por los rectángulos rojos? O, dicho de otro modo: ¿habría alguna manera de obtener los valores de los rectángulos rojos solo con mirar el rectángulo azul? Esto facilitaría mucho nuestra vida, ya que agilizaría el proceso de codificación, que se realiza entre las líneas 28 y 48, y haría más simple la propia decodificación, ya que leeríamos directamente el contenido de los rectángulos azules, al contrario de lo que se está haciendo en el código 01, donde desmontamos cada rectángulo azul para conseguir los elementos en rojo, que son los que se almacenan en el array.
De hecho, querido lector, esta idea, que surgió precisamente de este concepto, es la que dio origen a lo que conocemos como unión. Cuando usamos una unión, creamos un compartimiento de memoria para dividir los rectángulos azules en unidades de tamaño diferente. De hecho, el tamaño de cada unidad o elemento dependerá del propósito y objetivo del código o aplicación que se está creando. Pero una vez creada, una unión permite que el programador pueda controlar cada elemento de un bloque mayor de manera totalmente individualizada. Este concepto se utiliza mucho en códigos C y C++, donde las uniones nos ayudan a manipular cadenas enteras de elementos de manera fácil, tranquila y segura.
Para entender cómo funciona una unión, empezaremos con algo muy simple. Antes de volver a manipular el código 01. A continuación, veamos lo que está ocurriendo en el código que se muestra justo debajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. union un_01 07. { 08. ulong u64_bits; 09. uint u32_bits[2]; 10. }info; 11. 12. uint tmp; 13. 14. info.u64_bits = 0xA1B2C3D4E5F6789A; 15. 16. PrintFormat("Before modification: 0x%I64X", info.u64_bits); 17. 18. tmp = info.u32_bits[0]; 19. info.u32_bits[0] = info.u32_bits[1]; 20. info.u32_bits[1] = tmp; 21. 22. PrintFormat("After modification : 0x%I64X", info.u64_bits); 23. } 24. //+------------------------------------------------------------------+
Código 02
Observa cómo una cosa lleva a la otra. Sin entender los arrays, que se explicaron anteriormente, no se entenderá lo que se está haciendo aquí, en este punto del código. Y, sin haber visto lo que se hizo en el artículo anterior, lo que se mostrará aquí no tendría sentido alguno. Ahora creo que es el momento adecuado para explicar qué es una unión y cuál es su objetivo práctico.
Cuando ejecutes este código 02, verás la imagen que se muestra justo abajo en el terminal de MetaTrader 5.
Imagen 03
Este es el punto clave de la unión. Fíjate que es algo casi mágico, siendo este el primer nivel de lo que podemos hacer. Pero primero veamos lo que está ocurriendo en el código 02. En primer lugar, como se ve en la línea seis, debe declararse una unión. Es decir, tenemos la palabra reservada union seguida de otra palabra, que en este caso sería el identificador de la unión. Existe una diferencia entre la forma de declarar la unión en MQL5 y en los lenguajes C y C++. En MQL5, NO ES POSIBLE crear una unión anónima. Es decir, este identificador que ves seguido a la palabra union, SIEMPRE deberá existir en MQL5.
Cuando no existe, como sucede en C y C++, tenemos lo que se llama una unión anónima. El problema de una unión anónima es que no se puede utilizar fuera de la declaración. Ya veremos esto suceder en otro código. Pero, por ahora, centrémonos en el código 02. Como puedes ver, una unión, después de declararla e identificarla, comienza con una llave de apertura y finaliza con una llave de cierre. Todo lo que esté dentro de este bloque forma parte de la unión y comparte la misma región de memoria.
Ahora entra en juego la parte importante que se viene viendo desde hace algún tiempo. Todo lo que está en la memoria debe pensarse en términos de bytes. ABSOLUTAMENTE TODO. Por tanto, para crear una unión correctamente, debes pensar en cómo vas a dividir la memoria en bytes. Vuelve a la imagen 02. En ella se muestran 10 bytes. La forma en que los agrupes, aunque esta no sea la palabra más adecuada, es lo que te ayudará a crear la unión. La unión que estamos viendo, definida en la línea seis, ocupará ocho bytes de memoria y su elemento mayor será precisamente la variable u64_bits.
Bien, ¿y qué pasa con la variable u32_bits, que en este caso es un array? ¿No estaría ocupando otros ocho bytes, ya que el tipo de la variable nos dice que ocupa cuatro? Entonces, ¿no sería correcto decir que la unión ocuparía 16 bytes en total? No, querido lector. De hecho, la unión ocupará solo y únicamente ocho bytes. Sin embargo, tanto u64_bits como u32_bits comparten la misma región de memoria.
Sé que en un primer momento parece extremadamente confuso. Así que tomemos las cosas con calma, porque se pondrá aún más confuso si se saltan algunas de las etapas de la explicación.
El propósito de este código 02 es precisamente realizar un intercambio entre una parte de la memoria y otra, de modo que, al final, la información presente en la memoria haya girado. Para ello, necesitamos una variable temporal. En este caso, se declara en la línea 12. Un punto importante: esta variable temporal deberá tener un tamaño en términos de bytes igual o mayor que el de la variable más pequeña presente en la unión. Normalmente, usamos el mismo tipo, lo que garantiza el tamaño adecuado para cumplir nuestro objetivo.
Ya en la línea 14, inicializamos la unión. Es importante destacar, que, esto debe quedar bien claro en tu mente, la variable real, cuyo nombre pertenece a la región de memoria, se declara en la línea 10. Sin embargo, como no podemos dirigirnos directamente a esta región, precisamente porque allí pueden existir más variables, necesitamos decirle al compilador cuál es el nombre de la variable que queremos acceder dentro de la región de la unión. Podemos usar cualquiera de las presentes allí. Si se hace de la manera adecuada, el compilador lo entenderá y realizará la asignación al valor adecuado, haciendo que la región cambie de valor.
Para entender esto, volvamos a la imagen 02. Piensa que toda la imagen es una unión, y es cierto. Pero ya lo veremos con más calma. Ahora piensa que cada uno de los rectángulos en rojo tiene un nombre. Entonces, si deseas acceder a alguno de ellos, basta con decirle al compilador el nombre del rectángulo y él asignará o leerá el valor de ese rectángulo específico.
Genial, ¿verdad? Pero vamos con calma. Primero, es necesario entender este código 02. Muy bien, una vez que hemos asignado el valor a la variable u64_bits toda la región de memoria llamada info contiene el valor indicado en la línea 14.
Para mostrar que esto es cierto, usamos la línea 16 para mostrar el contenido de la memoria. Esto imprimirá nuestro primer mensaje en el terminal. Ahora, la parte divertida: como queremos intercambiar los valores de posición para crear un nuevo valor en la misma región de memoria, podemos usar el compartimiento de memoria que nos proporciona la unión para lograrlo de una manera muy fácil y práctica.
Así, lo primero que hay que hacer es usar nuestra variable temporal para almacenar uno de los valores. Esto se hace en la línea 18. A continuación, en la línea 19, asignamos el valor del array en el índice uno al índice cero. Ahora nuestra memoria está como una ensalada de frutas: toda desordenada, pero conteniendo solo parte del valor original. Para concluir, en la línea 20 colocamos en el índice uno el valor que, originalmente, existía en el índice cero. Así, el intercambio se ha completado con éxito y, como se puede observar en la imagen 03, la ejecución de la línea 22 se muestra como la segunda línea.
Si te pareció esto una locura, vamos a ver un caso aún más interesante, donde espejamos todo el contenido de manera muy simple y práctica. Para esto, vamos al código 03, que se muestra justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. union un_01 07. { 08. ulong u64_bits; 09. uchar u8_bits[sizeof(ulong)]; 10. }; 11. 12. un_01 info; 13. 14. info.u64_bits = 0xA1B2C3D4E5F6789A; 15. 16. PrintFormat("The region is composed of %d bytes", sizeof(info)); 17. 18. PrintFormat("Before modification: 0x%I64X", info.u64_bits); 19. 20. for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--) 21. { 22. tmp = info.u8_bits[i]; 23. info.u8_bits[i] = info.u8_bits[j]; 24. info.u8_bits[j] = tmp; 25. } 26. 27. PrintFormat("After modification : 0x%I64X", info.u64_bits); 28. } 29. //+------------------------------------------------------------------+
Código 03
Este código 03 es aún más interesante que el anterior. Y, al ejecutarlo, podemos ver la imagen 04, mostrada justo abajo, en el terminal de MetaTrader 5.
Imagen 04
Amigo, qué cosa más loca. Sin embargo, es bastante genial y muy interesante. Y yo allí, intentando hacer esto en la mayor pelea, sufriendo y agotándome para lograrlo. Y entonces vienes tú y nos enseñas que podemos hacer esto de una manera muy simple, fácil y práctica. Es genial, de verdad. Me gustó. Pero ahora tengo dudas con respecto a algunas cuestiones aquí.
Vamos por partes, para que yo lo entienda. Primero, ¿qué es lo que se está haciendo en la línea nueve? Bueno, mi querido lector, normalmente utilizamos arrays en las uniones para facilitar el acceso a los puntos específicos dentro de la memoria compartida. Existe otra forma de hacer esto sin utilizar arrays, pero eso se verá en otro momento. Como la mayor parte del tiempo necesitamos acceder a toda la región para poder manipularla de manera adecuada, no es raro usar algo parecido a lo que se ve en la línea nueve. Aunque en un código real esta forma de declarar es aún más diferente. Pero el principio y el objetivo son siempre los mismos: crear un modo de acceder a toda la región dentro de la unión. Elemento por elemento.
Otra duda: ¿qué cosa tan extraña es esta que se está declarando en la línea 12? Esto no tiene ningún sentido para mí. La línea 12, mi querido lector, es precisamente lo que hace que la explicación dada sobre el código 02 sea aún más adecuada. ¿Recuerdas que se dijo que aquí, en MQL5, NO PODEMOS tener una unión anónima? Pues bien, debido a que, en la línea seis, en el código 03, estamos declarando la unión, podemos usar el identificador un_01 como una forma de declarar la variable que la contendrá. Podemos tener diversas variables que contengan diferentes uniones y todas con el mismo identificador. Esto se debe a que, en el momento en que declaramos un tipo especial -y una unión es un tipo especial-, podemos usar el mismo identificador en puntos diferentes del código.
El único detalle es que el identificador tiene el mismo principio de visibilidad que el de una variable o constante cualquiera. Esto se ha visto en artículos anteriores. Sin embargo, una unión -y esto es importante- SIEMPRE será una variable, nunca podrá ser una constante. Aunque podamos apuntarla a una constante, la unión siempre se considerará como una variable. Pero una variable de tipo especial se parece mucho a una cadena de caracteres.
Por esta razón, los artículos se publican de modo que puedas comprender ciertos conceptos antes de ver otros aplicados. Sin entender primero la diferencia entre una variable y una constante, sería difícil explicar esto. Otro punto muy importante es el hecho de que el array dentro de una unión JAMÁS SERÁ DEL TIPO DINÁMICO. Siempre será del tipo estático.
Así, no sirve de nada que quieras crear una unión gigantesca usando, para ello, un array dinámico, porque, si intentas hacer esto, el compilador no entenderá lo que estás intentando hacer.
Un último punto al que quizá no hayas prestado atención todavía es cómo el bucle de la línea 20 espeja el contenido de la región de memoria. Para que el espejado ocurra, debes usar un contador que llegue hasta la mitad de la región. Como aquí estamos usando siempre un número par de elementos, resulta fácil hacer esto. Este bucle, visto en la línea 20, logra espejar cualquier valor de tipo discreto, siempre que hagas los debidos cambios. —no en el bucle. Sino en la declaración del bloque presente en la unión de la línea seis—. Claro que también será necesario ajustar el valor declarado en la línea 14. Pero, fuera de eso, no será necesario realizar ningún otro cambio en el código.
Por ejemplo: si quieres espejar una variable del tipo int, de 32 bits, solo tendrías que cambiar las declaraciones de tipo usadas en las líneas ocho y nueve, de ulong a int. Al hacer esto, podrías cambiar el valor declarado en la línea 14 por el valor deseado y listo. El código ya sería capaz de espejar el tipo int en lugar del tipo ulong. Así de simple.
Aunque, en realidad, hay una forma mucho más sencilla de hacerlo. Sin embargo, como aún no he explicado otro concepto que podemos usar aquí en MQL5. La forma más simple es hacer lo que se explicó justo arriba.
Muy bien, antes de terminar, vamos a ver una última cosa que podemos hacer con las uniones. Y esto está relacionado con utilizarlas en funciones y procedimientos. Para demostrarlo, tomaremos el código 03 y modificaremos el bucle para colocarlo en una función. Después, veremos este mismo proceso, solo que aplicado a un procedimiento. De cualquier forma, el objetivo es que la función o el procedimiento haga el espejado por nosotros y, luego, muestre el resultado dentro de la rutina principal.
Vale, existen diversas formas de hacerlo, pero aquí vamos a usar una con fines didácticos, ya que el objetivo es mostrar cómo podríamos utilizar la unión de una manera más amplia. Comencemos con el código que, en mi opinión, es más sencillo de comprender, porque utilizaremos una implementación muy parecida a la vista en el código 03.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. ulong u64_bits; 07. uchar u8_bits[sizeof(ulong)]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { 12. un_01 info; 13. 14. info.u64_bits = 0xA1B2C3D4E5F6789A; 15. 16. PrintFormat("The region is composed of %d bytes", sizeof(info)); 17. 18. PrintFormat("Before modification: 0x%I64X", info.u64_bits); 19. 20. Swap(info); 21. 22. PrintFormat("After modification : 0x%I64X", info.u64_bits); 23. } 24. //+------------------------------------------------------------------+ 25. void Swap(un_01 &info) 26. { 27. for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--) 28. { 29. tmp = info.u8_bits[i]; 30. info.u8_bits[i] = info.u8_bits[j]; 31. info.u8_bits[j] = tmp; 32. } 33. } 34. //+------------------------------------------------------------------+
Código 04
Este código 04 es bastante sencillo. Sin embargo, es necesario que prestes atención a algunos detalles. El primero es que la unión ya no es local, sino global. Pero esto se ha hecho precisamente para dar acceso al procedimiento que se declara en la línea 25 al tipo especial que estamos creando en la línea 4. Si la unión no se hubiera declarado como global. Utilizar el tipo especial que declaramos como un_01 sería imposible en la declaración del parámetro de la línea 25. Nota que todo lo que hicimos aquí fue mover el código que estaba dentro de la rutina principal hacia dentro de un procedimiento. Y en lugar del bucle que se veía en la línea 20 del código 03, colocamos la llamada a nuestro procedimiento en la misma línea. Básicamente, hemos hecho público un fragmento de código que antes era privado. Creo, sinceramente, que tú, querido lector, lograrás entender este código 04 sin ninguna dificultad. Sin embargo, ahora vamos a ver un caso diferente, donde en lugar de usar un procedimiento, usaremos una función. Este se ve justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. ulong u64_bits; 07. uchar u8_bits[sizeof(ulong)]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { 12. un_01 info; 13. 14. info.u64_bits = 0xA1B2C3D4E5F6789A; 15. 16. PrintFormat("The region is composed of %d bytes", sizeof(info)); 17. 18. PrintFormat("Before modification: 0x%I64X", info.u64_bits); 19. 20. PrintFormat("After modification : 0x%I64X", Swap(info).u64_bits); 21. } 22. //+------------------------------------------------------------------+ 23. un_01 Swap(const un_01 &arg) 24. { 25. un_01 info = arg; 26. 27. for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--) 28. { 29. tmp = info.u8_bits[i]; 30. info.u8_bits[i] = info.u8_bits[j]; 31. info.u8_bits[j] = tmp; 32. } 33. 34. return info; 35. } 36. //+------------------------------------------------------------------+
Código 05
Bueno, aquí la cosa es un poco más complicada, pero solo porque es una novedad para ti en este momento, querido lector. Observa que tenemos un código muy similar al visto anteriormente, cuando usábamos un procedimiento. Sin embargo, al mirar la misma línea 20 en los tres últimos códigos, es posible que el código 05 sea el que más dificultades genere en programadores nuevos con poca experiencia. Pero esto no es motivo de alarma o pánico. Ya que estamos tratando con una función declarada en la línea 23. Para empezar, podemos observar algo interesante. La primera es que, en los códigos 03 y 04, se modifica invariablemente el contenido de la variable declarada en la línea 12. Esto es un hecho. Sin embargo, en el código 05, el contenido de la variable declarada en la línea 12, NO SE MODIFICA.
Pero espera un momento, ¿cómo que el contenido no se está modificando? ¿Acaso no estamos realizando el espejado para obtener el resultado que se ve en la imagen 4? No es eso, querido lector. De hecho, el espejado está ocurriendo. Y, en estos tres últimos códigos, el resultado será el que se ve en la figura 4. Pero, cuando digo que el contenido de la variable 12 no se modifica, es porque, de hecho, no se está modificando. Puedes comprobarlo porque, en la línea 23, estamos pasando cosas como una referencia constante.
Ahora sí que se ha complicado del todo. Antes dijiste que no podíamos usar una unión en algo constante, ¿pero ahora estás diciendo que sí podemos? Amigo, tienes que decidirte. De acuerdo, tal vez me haya expresado mal. O tal vez tú estás confundiendo asignación con declaración. Como la declaración se está haciendo en la línea 23, es una constante. Esto impedirá que la variable asignada a esta constante pueda ser modificada. Sin embargo, necesitamos una variable para poder trabajar. Y, al ser la declaración una constante, necesitamos la línea 25 para generar una nueva variable. Esta sí se podrá modificar y el resultado se devolverá en la línea 34.
Aquí es donde muchos pueden confundirse. Pero si Swap es una función en este código 05 y estamos retornando una variable. ¿no deberíamos asignar el valor de retorno a otra variable para, solo después de haber hecho esto, poder usar el valor que Swap nos devolvió? Bueno, depende, querido lector. Sin embargo, como una función es un tipo de variable -y esto ya se explicó en otro artículo-, podemos usar el tipo especial declarado en la línea cuatro como forma de acceso directo a los datos presentes allí.
Esto solo es posible gracias a que la función está devolviendo realmente ese tipo específico. Si el valor de retorno fuera un tipo discreto, no sería posible implementar lo que se ve en la línea 20, por lo que sería necesario utilizar otro tipo de recurso para lograr el mismo resultado. Sin embargo, como este es un tema que podría generar confusión, lo dejaré para explicarlo en otro momento. De todas formas, sí existen formas de hacer esto.
Consideraciones finales
En este artículo, veremos qué es una unión. Aquí, hicimos ejercicios para experimentar con las primeras construcciones en las que se podría utilizar una unión. Aun así, lo que se ha visto aquí es solo la parte básica de todo un conjunto de conceptos e información que se explorará aún mejor en nuevos artículos en el futuro. Entonces, querido lector, aquí va mi consejo de oro. Practica y estudia mucho lo visto aquí.
En el anexo, encontrarás los códigos principales mostrados en este artículo. Y en el próximo artículo, nos sumergiremos aún más profundamente en la programación básica en MQL5. Así que, hasta pronto.
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/15502
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
¡No entiendo nada! Me gustaría mucho que alguien iniciara una serie de artículos "Del nivel cero al iniciante". Y aquí tienes a un principiante con dos formaciones superiores en programación ....
Pero el propósito de este artículo mío es exactamente ese. Iniciar a una persona desde cero. Sin embargo, has entrado en un artículo donde el material ya es un poco más avanzado. Te sugiero que empieces por éste:
De básico a intermedio: variables (I)
Detalle: Todos los artículos con material más avanzado tienen un enlace al principio para que puedas ver el artículo anterior. Pero éste es el que te sugiero. De hecho, es el primero. Donde empiezo a explicar las cosas desde cero 🙂👍.
¡No hay nada claro! Ojalá alguien empezara una serie de artículos "¡De nivel cero a principiante!". Tienes un principiante con dos títulos en programación....
Es una traducción del portugués, y no es la mejor.![😑]()
Lo que está escrito originalmente en ruso será mucho más claro. Por ejemplo, este libro.