
Do básico ao intermediário: União (I)
Introdução
O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como uma aplicação final, onde o objetivo não seja o estudo dos conceitos aqui mostrados.
No artigo anterior Do básico ao intermediário: Array (IV), tratamos de um conceito muito legal é extremamente interessante. Que apesar de muitos o considerar um conceito avançado. Na minha modesta e humilde opinião, trata-se de algo que todo iniciante em programação, deveria conhecer e entender. Isto por que, se bem utilizado, aquele mesmo conceito mostrado naquele artigo anterior. Pode literalmente abrir os portões do paraíso. Já que com ele podemos fazer coisas, que de outra maneira seriam muito difíceis, para não dizer quase impossível de serem feitas.
Além disto, aquele mesmo conceito é também utilizado em um outro tipo de coisa que será vista em um momento mais oportuno. Então para não gerar nenhuma crise de ansiedade em ninguém. Fica apenas uma dica aqui. Procure estudar e praticar bastante o que foi visto no artigo anterior. Entenda, compreenda e assimile muito bem aquele conhecimento. Pois sem ele, absolutamente nada do que será visto de agora em diante, fará sentido. Tudo e absolutamente tudo que será visto, será como se fossem operações mágicas.
Bem, dizer que tudo não será mais bem compreendido por quem não entendeu o que foi feito no artigo anterior. Talvez tenha sido um pouco exagerado da minha parte. No entanto, isto não minimiza o fato de que, o artigo anterior, é o mais importante que foi postado até este momento. Isto para quem de fato, queira se tornar um bom programador. Além daquele que desejam e almejam conseguir fazer qualquer coisa em uma linguagem de programação. Sendo que aquele conceito mostrado no artigo anterior. Não se aplica somente a linguagem MQL5. Mas a toda e qualquer linguagem com alguma qualidade, no que se refere ao uso mais adequado dos recursos computacionais.
Ok, então antes de começarmos, temos de falar do que é pré-requisito para este artigo atual. Apesar de alguns instrutores imaginarem que eu possa estar exagerando. Na minha visão, sem que você tenha pelo menos compreendido, mesmo que superficialmente o que foi visto no artigo anterior. Entender o que será feito aqui, será quase que improvável. Não estou de forma alguma dizendo que você não entenderá. Por favor, não me entenda mal. Estou dizendo que, será no mínimo bem mais difícil, que você consiga acompanhar o que será explicado aqui.
Então o artigo anterior, seria como um divisor de águas. Onde de um lado temos todo um material básico de programação e agora entraremos em um material um pouco mais avançado. E como o nome do artigo sugere, aqui iremos falar de UNIÃO. Mas não união no sentido explicito da palavra. Mas do termo encontrado em algumas linguagens que é a UNIÃO. E como de praxe, vamos iniciar um novo tópico para começar o que tornará o processo de implementação e codificação muito mais divertido e prazeroso.
O nascimento da UNIÃO
No artigo anterior, vimos a implementação de um código mostrado logo abaixo.
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
Ao executar este código 01, temos como resultado, o que é visto na imagem a seguir.
Imagem 01
Aqui nesta imagem 01, podemos notar que os valores que estão sendo definidos nas linhas oito, nove e dez. Estão sendo transferidos para dentro do procedimento presente na linha 60. No entanto, ao observamos o tipo de dados esperado pelo procedimento, notamos que ele, não é valores descritos. Mas sim um array. Algo que aparentemente não faz nenhum sentido. Já que muitos ao olhar o procedimento da linha 60, de fato esperariam ver ali, operações envolvendo um array. Porém não é bem isto que está ocorrendo. O que temos ali e a transcrição, ou tradução de valores que estejam dentro do array. Isto a fim de reconstruir os valores que foram repassados.
Observe que não temos como saber qual o nome ou tipo de variável utilizada durante a chamada do procedimento. Isto por que, não existe nenhum tipo de referência a estas informações. Tudo que sabemos é a quantidade de elementos e o valor de cada elemento presente no que seria cada variável.
Muitos programadores, e alguns até com uma boa experiência, consideram este tipo de abordagem completamente inaceitável. Já que não temos como ligar um valor a uma variável em nível de código. Porém, o fato de tais profissionais, abominarem este tipo de prática mostrada aqui. Se esquecem, ou melhor, desconhecem, o fato de que para a CPU, o nome de uma variável pouco importa. Tudo que a CPU enxerga é um monte de números. Somente isto. Ela não sabe, qual o nome da variável ou constante que estamos utilizando. Para ela este tipo de informação é completamente irrelevante.
Sendo assim, está modelagem que foi criada na memória pelo que é visto no código 01. Algo parecido com a representação mostrada logo abaixo.
Imagem 02
Irei dar uma ênfase muito especial tudo isto daqui. Pois este conceito que será explicado, pode ser bem confuso, ainda mais quando formos ver um outro conceito, que se parece muito com este daqui.
Nesta imagem 02, temos os valores que estão sendo destacados na imagem 01, assim como alguns marcadores. Estes marcadores aparecem em verde nesta imagem 02. Sendo eles os responsáveis por dizer:
Aqui começa um novo valor. E ele é composto por tantos elementos.
Os retângulos em azul representam cada bloco de elemento. Ou seja, se o array não fosse utilizado, mas sim variáveis discretas. Iriamos necessitar de seis variáveis discretas. Já que existem seis retângulos azuis na imagem 02. Porém apesar disto, temos os retângulos em vermelho. Estes representam cada um dos elementos presentes no array. Como são dez elementos temos dez retângulos em vermelho. Mas poderíamos ter cinco retângulos se cada elemento fosse composto de dois valores.
Porém devido a questões de fragmentação, que são coisas ao meu ver muito avançadas para serem explicadas aqui e agora. Usamos um valor mínimo, para o tamanho de cada elemento. Assim evitamos a fragmentação, apesar de isto deixar um pouco lento o processo de decodificar a informação depois. Este processo de decodificar é feito pelo laço da linha 70. Apesar de muitos imaginarem de maneira errônea que ele seria feito pelo laço da linha 67. Ele de fato é feito pelo laço da linha 70.
Ok. Acredito que que entender esta parte não tenha sido problema até aqui. Mas quero que você, meu caro leitor, olhe com atenção para esta imagem 02. E pare um pouco e pense: Será que não haveria um jeito de podermos ler os retângulos azuis, sem precisar necessariamente precisar passar pelos retângulos vermelhos? Ou melhor dizendo: Será que teria uma forma, de apenas olhando o retângulo azul, conseguirmos obter os valores que estão nos retângulos vermelhos? Isto iria facilitar muita a nossa vida. Agilizando tanto o processo de codificação, que é feito entre as linhas 28 até 48. Além de tornar mais simples a própria decodificação. Já que estaríamos lendo diretamente o conteúdo dos retângulos azuis. Ao contrário do que está sendo feito no código 01. Onde desmontando cada retângulo em azul, para conseguir o que seria os elementos em vermelho. E estes elementos é que seriam armazenados no array.
De fato, meu caro leitor. Esta ideia que surgiu justamente deste conceito. É que fez surgir o que conhecemos como união. Quando usamos uma união, criamos um compartilhamento de memória a fim de dividir os retângulos azuis em unidades de tamanho diferente. O tamanho que cada unidade, ou elemento irá de fato ter. Dependerá do proposito e objetivo do código, ou aplicação que está sendo criada. Mas uma união, uma vez que tenha sido criada, permite que o programador, possa controlar cada elemento de um bloco maior de maneira totalmente individualizada. Tal conceito é muito visto em códigos C e C++. Onde uniões nos ajuda a manipular cadeias inteiras de elementos de uma forma super fácil, tranquila e segura.
Para entender como uma união funciona, vamos começar com algo bem simples. Antes de mexermos no código 01, novamente. Então vamos ver o que está acontecendo no código visto logo abaixo.
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
Observe como uma coisa leva a outra. Sem entender arrays, que foi explicado anteriormente, você não conseguirá entender o que está sendo feito aqui neste ponto do código. E sem ter visto o que é feito no artigo anterior. O que será visto aqui, não faria o mínimo sentido. Mas agora acredito ser o momento correto, para explicar o que seria uma união. E qual o objetivo prático da mesma.
Quando você executar este código 02, você irá ver no terminal do MetaTrader 5, a imagem mostrada logo abaixo.
Imagem 03
Este é o ponto chave da união. Note que é algo quase mágico. Sendo este o primeiro nível do que podemos fazer. Mas vamos entender o que está ocorrendo no código 02. Primeiro uma união deve ser declarada como é visto na linha seis. Ou seja, temos a palavra reservada union, sendo usada seguida de uma outra palavra, que neste caso seria o identificador da união. Existe uma diferença aqui, entre a forma de declarar a união em MQL5 e as linguagens C e C++. No MQL5, NÃO É POSSÍVEL criar uma união anônima. Ou seja, este identificador que você vê seguindo a palavra union, SEMPRE deverá existir no MQL5.
Quando ele não existe, como acontece é o caso do C e C++, temos o que é chamado de união anônima. O problema de uma união anônima, é que você não pode usar ela fora da declaração. Já iremos ver isto acontecer em outro código. Mas por enquanto, vamos focar no código 02. Como você pode notar, uma união depois de feita a declaração e identificação da mesma, se inicia com um abre chave e finaliza em um fecha chave. Tudo que estiver dentro deste bloco, faz parte da união e compartilha a mesma região de memória.
Agora entra a parte importante que vem sendo vista já a algum tempo. Tudo na memória deve ser pensando em termos de bytes. ABSOLUTAMENTE TUDO. Sendo assim, para você criar de maneira correta uma união. Deve pensar em como você irá dividir a memória em termos de bytes. Agora volte a imagem 02. Ali temos 10 bytes sendo representados. A forma como você os irá agrupar, se bem que esta não seja a palavra mais adequada. É que irá lhe ajudar a criar a união. Esta união que estamos vendo, sendo definida na linha seis. Irá ocupar oito bytes de memória. Sendo seu elemento maior, justamente a variável u64_bits.
Bem, e quanto a variável u32_bits, que no caso é um array. Ela não estaria ocupando outros oito bytes. Já que o tipo da variável nos diz que ela ocupa 4 bytes? Então o correto não seria dizer que a união ocuparia 16 bytes ao todo? Não meu caro leitor. A união de fato ocupará apenas e somente oito bytes. Porém tanto u64_bits, quanto u32_bits, estão partilhando a mesma região de memória.
Sei que isto parece extremamente confuso neste primeiro momento. Então vamos com calma. Pois a coisa irá ficar ainda mais confusa, se forem queimadas alguma das etapas da explicação.
O proposito aqui deste código 02, é justamente fazer uma troca entre uma parte da memória e a outra parte. De modo que no final tenhamos feito um certo giro na informação presente na memória. Para fazer isto, precisamos de uma variável temporária. No caso ela está sendo declarada na linha 12. Um ponto importante: Esta variável temporária, deverá ter um tamanho em termos de bytes, igual ou maior, ao da menor variável presente na união. Normalmente usamos o mesmo tipo, garantido assim o tamanho adequado. Isto para que possamos cumprir nosso objetivo.
Já na linha 14, inicializamos a união. Note uma coisa aqui. E isto precisa ficar bem fixado em sua mente. A variável real, cujo nome a região de memória pertence, é declarado na linha 10. Mas como não podemos nos dirigir diretamente a esta região, justamente por ali poder existir mais variáveis. Precisamos dizer ao compilador, qual o nome da variável, dentro da região pertencente a união, queremos acessar. Podemos usar qualquer uma que esteja presente ali. Se isto for feito da maneira adequada, o compilador irá entender e fará a atribuição ao valor adequado. Fazendo com que a região mude de valor.
Para entender isto, vamos voltar novamente a imagem 02. Pense que toda a imagem de fato é uma união, e de fato isto é verdadeiro. Mas depois iremos ver isto com mais calma. Agora pense que cada um dos retângulos vem vermelho tem um nome. Então se você desejar acessar algum deles, basta dizer ao compilador o nome do retângulo e ele irá atribuir, ou ler o valor daquele retângulo especifico.
Bacana isto não é mesmo? Mas vamos com calma. Primeiro é preciso entender este código 02. Muito bem, uma vez que tenhamos atribuído o valor a variável u64_bits. Toda a região de memória chamada de info. Agora contém o valor que é informado na linha 14.
Para mostrar que isto é verdadeiro. Usamos a linha 16, a fim de mostrar o conteúdo presente na memória. E isto irá imprimir o que é a nossa primeira mensagem no terminal. Agora a parte divertida. Como queremos trocar os valores de posição, criando assim um novo valor na mesma região de memória. Podemos usar o compartilhamento de memória, que a união nos fornece para conseguir fazer isto de maneira muito fácil e pratica.
Assim, a primeira coisa a ser feita, é usar a nossa variável temporária, para armazenar um dos valores. Isto é feito na linha 18. Feito isto, na linha 19, atribuímos o valor que está no array, porém no índice um, ao índice zero. Agora a nossa memória está como uma salada de frutas. Toda bagunçada, só que contendo somente parte do valor original. Para que tudo seja concluído, usamos a linha 20, para colocar no índice um o valor que originalmente existia no índice zero. Assim a troca foi concluída com sucesso. Mostrando o que é a execução da linha 22, como a segunda linha vista na imagem 03.
Se você achou isto maluco. Vamos ver um caso ainda mais interessante. Onde espelhamos todo o conteúdo de maneira muito simples e prática. Para isto vamos ao código 03, que é visto logo abaixo.
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 é ainda mais interessante do que o anterior. E ao executar ele, podemos ver a imagem 04 mostrada logo abaixo, no terminal do MetaTrader 5.
Imagem 04
Cara que coisa mais maluca. Porém bastante legal e muito interessante. E eu ali tentando fazer este tipo de coisa. Na maior peleja, sofrendo e me acabando para conseguir fazer isto. Aí vem você e mostra que podemos fazer isto de uma maneira muito simples, fácil e prática. Muito legal mesmo. Gostei. Mas agora estou na dúvida com relação a algumas questões aqui.
Vamos por partes, para que eu consiga entender. Primeiro, o que é esta coisa sendo feita na linha nove? Bem, meu caro leitor, normalmente temos em uma união arrays para facilitar o acesso aos pontos, específicos dentro da memória compartilhada. Existe uma outra forma de fazer isto, sem utilizar arrays. Mas isto será visto em outro momento. Como na maior parte das vezes, precisamos acessar toda a região, a fim de que possamos manipular a mesma de maneira adequada. Não é raro usar algo parecido com o que é visto nesta linha nove. Se bem que em um código real, esta forma de declarar é ainda mais diferente. Mas o princípio, e objetivo é sempre o mesmo. Criar um modo de acessar toda a região dentro da união. Elemento por elemento.
Outra dúvida: Que coisa estranha é está sendo declarada na linha 12? Isto para mim não faz o menor sentido. Esta linha 12, meu caro leitor, é justamente o que torna a explicação dada sobre o código 02 ainda mais adequada. Lembra que foi dito, que aqui no MQL5, NÃO PODEMOS ter uma união anônima? Pois bem. Pelo fato de que na linha seis, no código 03. Estamos declarando o que será a união. Podemos usar aquele identificador, que no caso é un_01, como uma forma de declarar a variável que irá conter a união. Podemos ter diversas variáveis, contendo diferentes uniões. E todas com o mesmo identificador. Isto por que, no momento em que declaramos um tipo especial. E uma união é um tipo especial. Podemos usar o mesmo identificador, em pontos diferentes do código.
O único detalhe, é que o identificador, tem o mesmo princípio de visibilidade de uma variável, ou constante qualquer. Isto foi visto em artigos anteriores. Porém uma união, e isto é importante ser mencionado. SEMPRE será uma variável. Nunca podendo ser uma constante. Apesar de podemos apontar ela para uma constante. A união sempre será tida como sendo uma variável. Mas uma variável do tipo especial. Quase igual uma string.
Por conta disto, os artigos estão sendo postados de modo que você consiga compreender certos conceitos, antes de ver outros sendo aplicados. Sem entender primeiro, o que diferencia uma variável de uma constante, seria difícil de explicar isto daqui. Um outro ponto também muito importante: É o fato de que o array dentro de uma união JAMAIS E EM HIPOTESE ALGUMA será do tipo dinâmico. Ele sempre será do tipo estático.
Assim, não adianta você querer criar uma união gigantesca. Usando para isto um array dinâmico. Pois se tentar fazer isto, o compilador não irá entender o que você está tentando fazer.
Um último ponto, que você, talvez não tenha se atendado ainda. É como o laço da linha 20 está conseguindo espelhar o conteúdo da região de memória. Para que o espelhamento aconteça, você deve usar um contador que irá até a metade da região. Como aqui estamos usando sempre um número par de elementos. Fica fácil fazer este tipo de coisa. Assim sendo, este laço visto na linha 20, de fato consegue espelhar qualquer valor, de um tipo discreto. Desde que para isto, você faça as devidas mudanças. Não no laço. Mas sim na declaração do bloco presente na união da linha seis. Claro que também será necessário ajustar o valor que está sendo declarado na linha 14. Mas fora isto nenhuma outra mudança no código seria necessária ser feita.
Por exemplo: Se você desejar espelhar uma variável do tipo int, ou que contenha 32 bits. Teria apenas de trocar as declarações de tipo, usadas nas linhas oito e nove, do tipo ulong para o tipo int. Ao fazer isto, poderia mudar o valor declarado na linha 14, para o valor desejado e pronto. O código já seria capaz de espelhar o tipo int no lugar de estar espelhando o tipo ulong. Simples assim.
Se bem que na verdade existe uma forma bem mais simples de se fazer isto. Porém como ainda não expliquei um outro conceito que podemos usar aqui no MQL5. A forma mais simples de fato é fazendo o que foi explicado logo acima.
Muito bem, antes de finalizar este artigo, vamos ver uma última coisa que podemos fazer com as uniões. E isto está relacionado com utilizar elas em funções e procedimentos. Para demonstrar este tipo de coisa sendo feita, vamos pegar o código 03 e o modificar de maneira que o laço presente ali, seja colocado em uma função primeiramente. Depois iremos ver este mesmo tipo de coisa, só que aplicado em um procedimento. De qualquer forma, o objetivo é que a função ou procedimento faça o espelhamento para nós e depois mostremos o resultado dentro da rotina principal.
Ok, existem diversas formas de se fazer isto. Mas aqui vamos usar uma para fins didáticos. Já que o objetivo é mostrar como poderíamos utilizar a união de uma maneira mais ampla. Sendo assim, vamos começar com o código que ao meu entender é mais simples de ser compreendido. Isto porque, estaremos usando uma implementação muito próxima da vista no 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 é bem simples de entender. Porém é necessário que você fique atento a alguns detalhes aqui. O primeiro é o fato de que a união agora não é mais local. E sim global. Porém isto foi feito, justamente para dar acesso ao procedimento que está sendo declarado na linha 25, ao tipo especial que estamos criando na linha quatro. Sem que a união de fato fosse colocada como sendo global. Fazer o uso do tipo especial que declaramos como sendo un_01, seria impossível na declaração do parâmetro da linha 25. Note que tudo que fizemos aqui, foi remover o código que era visto dentro da rotina principal para dentro de um procedimento. E no lugar do laço que era visto na linha 20 do código 03. Colocamos a chamada ao nosso procedimento na mesma linha. Basicamente, apenas tornamos público um trecho de código que antes era privado. Acredito sinceramente que você, meu caro leitor conseguirá entender este código 04 sem nenhuma dificuldade. Porém agora vamos ver um caso diferente. Onde no lugar de usar um procedimento, usaremos uma função. Este é visto logo abaixo.
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
Bem, aqui a coisa é um pouco mais complicada. Mas isto apenas por ser uma novidade, para você e neste momento, meu caro leitor. Perceba que temos um código muito similar ao visto anteriormente, quando usávamos um procedimento. No entanto, olhando a mesma linha 20, em todos os três últimos códigos. Esta vista no código 05, talvez seja a que venha a gerar mais dificuldades em novos programadores. Com pouca experiência ainda na bagagem. Porém isto não é motivo para alarde, ou pânico. Visto que estamos lidando com uma função declarada na linha 23. Para começar, podemos perceber uma coisa interessante aqui. A primeira é o fato de que, nos códigos 03 e 04, temos invariavelmente a modificação do conteúdo presente na variável declarada na linha 12. Isto é fato. Porém, aqui no código 05, o conteúdo da variável declarada na linha 12, NÃO É MODIFICADO.
Mas espere um pouco. Como assim o conteúdo não está sendo modificado? Por um acaso não estamos efetuando o espelhamento para produzir o resultado visto na imagem 04? Não é isto meu caro leitor. De fato, o espelhamento está ocorrendo. E em todos estes três últimos códigos, o resultado será o que é visto na figura 04. Porém quando digo que o conteúdo da variável 12 não é modificado, é porque de fato ele não é modificado. Você pode observar isto, pelo simples fato de que na linha 23, estamos passando as coisas como uma referência constante.
Agora complicou de vez. Antes você havia dito que não poderíamos usar uma união em algo que fosse constante. Mas agora você está dizendo que podemos? Cara você precisa de decidir. Ok, talvez eu tenha sido mal interpretado. Ou talvez você esteja confundindo atribuição com declaração. Como a declaração está sendo feita na linha 23 é uma constante. Isto impedirá de que a variável, atribuída a esta constante, possa ser modificada. Porém, precisamos de uma variável para poder trabalhar. E sendo a declaração uma constante, precisamos da linha 25, para gerar uma nova variável. Esta sim será modificada e o resultado será retornado pela linha 34.
Agora é onde muitos podem ficar confusos. Mas se Swap é uma função, neste código 05, e estamos retornando uma variável. Será que não deveríamos atribuir o valor de retorno a uma outra variável. Para somente depois disto ter sido feito, poder usar o valor que Swap nos retornou? Bem, isto depende, meu caro leitor. Porém como uma função é um tipo de variável. E isto já foi explicado em outro artigo. Podemos usar o tipo especial que foi declarado na linha quatro como forma de acesso direto aos dados presentes ali.
Isto somente é possível, graças ao fato de que, a função de fato está retornando aquele tipo específico. Se o valor de retorno fosse um tipo discreto. Tal implementação que pode ser vista na linha 20, não seria possível. Sendo necessário utilizar um outro tipo de artificio para se ter o mesmo resultado. Porém como isto, é um tema, que neste momento, poderia gerar confusão em sua cabeça. Vou deixar para explicar isto em outro momento. Mas de fato existe sim formas de se fazer isto.
Considerações finais
Neste artigo começamos a ver o que seria uma união. Aqui fizemos a lição de casa, experimentando as primeiras construções em que uma união poderia ser utilizada. Apesar de tudo, o que foi visto aqui, é apenas a parte básica de todo um conjunto de conceitos e informações que ainda serão melhor exploradas em novos artigos no futuro. Então meu caro leitor, fica a dica de ouro. Pratique e estude bastante o que foi visto aqui.
No anexo, você terá acesso aos principais códigos mostrados neste artigo. E no próximo iremos mergulhar ainda mais fundo no que se refere a programação básica em MQL5. Então até breve.





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso