
Do básico ao intermediário: União (II)
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: União (I), começamos a falar sobre o que seria uma união. Sendo que na verdade, este tema já vem sendo preparado para ser visto desde o primeiro artigo. Já que tudo que está e será visto aqui e nos próximos artigos está de alguma forma correlacionado entre si. De qualquer forma, o que foi visto no artigo anterior é apenas a parte inicial do que está diretamente relacionado ao o que seria uma união. Este tópico de fato é um tópico bastante extenso, assim como o que está ligado a questão sobre arrays.
Pois bem, neste artigo, iremos ver um pouco mais sobre uniões. Mas também iremos estender um pouco mais o nosso conhecimento, a respeito de arrays. Então se acomode de maneira confortável e vamos ao primeiro tópico deste artigo.
Arrays e uniões
Uniões são um assunto realmente muito interessante, e que abrem diversas portas, para temas bem mais elaborados. Isto quando o assunto é programação. Porém devemos tomar alguns cuidados, para não cairmos no pecado, de imaginar já entender sobre um assunto, apenas por ter visto a base do mesmo.
O motivo para isto, é que muitos ignoram o conceito e se apegam apenas a uma ideia frágil e pouco produtiva. De imaginar que ao declaramos uma união, estamos de fato limitando tudo a somente aquela modelagem simples que foi criada durante a declaração. Quando na verdade, podemos fazer muito mais coisas, se aplicamos corretamente o conceito envolvido em uma dada ferramenta.
No artigo anterior, mencionei que ao criamos, ou melhor dizendo, declaramos como uma união seria montada. Estaríamos criando um tipo novo e especial de dado. Quase igual ao que seria uma string. Porém muitos podem ficar meio que desconfiados, ou mesmo com receio ao ver certas construções sendo feitas com este tipo de dado criado.
Para demonstrar como isto pode vir a ser interessante. E por que alguém faria isto. Será necessário, que venhamos a entender um pequeno detalhe. Como uma união, é de fato um tipo novo e especial. Podemos utilizar a mesma de diversas formas. Tanto na declaração, quanto também na forma de utilizar este tipo de dado especialmente criado para uma dada situação. Então vamos ver um primeiro exemplo de isto sendo feito.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. uint u32_bits; 07. uchar u8_bits[4]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { 12. un_01 info[2]; 13. 14. PrintFormat("The region is composed of %d bytes", sizeof(info)); 15. ZeroMemory(info); 16. View(info); 17. Set(info, 0xA1B2C3D4E5F6789A); 18. View(info); 19. for (uchar c = 0; c < info.Size(); c++) 20. info[c] = Swap(info[c]); 21. View(info); 22. } 23. //+------------------------------------------------------------------+ 24. void Set(un_01 &arg[], ulong value) 25. { 26. arg[0].u32_bits = (uint)(value >> 32); 27. arg[1].u32_bits = (uint)(value & 0xFFFFFFFF); 28. } 29. //+------------------------------------------------------------------+ 30. void View(const un_01 &arg[]) 31. { 32. Print("------------"); 33. for(uchar c = 0; c < arg.Size(); c++) 34. PrintFormat("Index: [ %d ] = 0x%I64X", c, arg[c].u32_bits); 35. } 36. //+------------------------------------------------------------------+ 37. un_01 Swap(const un_01 &arg) 38. { 39. un_01 info = arg; 40. 41. for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--) 42. { 43. tmp = info.u8_bits[i]; 44. info.u8_bits[i] = info.u8_bits[j]; 45. info.u8_bits[j] = tmp; 46. } 47. 48. return info; 49. } 50. //+------------------------------------------------------------------+
Código 01
Quando executamos este código 01, iremos poder ver no terminal do MetaTrader 5, a seguinte saída mostrada logo abaixo.
Imagem 01
Aqui, neste código 01, estamos aplicando tudo que foi visto anteriormente. Tanto com relação ao esquema e forma de trabalhar com arrays. Como a forma de trabalhar e lidar com uma união. Perceba que na linha 12 estamos declarando um array do tipo estático. Isto talvez possa parecer um tanto quanto estranho. Já que até o momento, você muito possivelmente, estaria imaginando que somente tipos discretos poderiam ser utilizados em arrays. Mas aqui estamos utilizando um tipo especial. E este foi declarado na linha quatro. Algo bem básico.
Porém, ao executar a linha 14, você nota que a primeira informação que está sendo impressa é a que nos diz, que a variável info, é composta de 8 bytes. Mas isto, à primeira vista não faz sentido. Bem, meu caro leitor, se você teve esta sensação. Sugiro que volte e estude o artigo anterior. Pois de fato, a variável info é composta de 8 bytes. Já que temos como maior largura possível ali, a variável u32_bits, que é do tipo uint. Então na verdade, seria como se estivéssemos declarando algo parecido com o que é visto logo abaixo.
. . . 12. uint info[2]; . . .
Acredito que isto não seja de fato algo que você fique confuso ao observar. Porém existe uma declaração oculta nesta mesma linha. Que também poderia ser enxergada por um outro programador mais observador. Esta é o que vemos logo abaixo.
. . . 12. uchar info[2][4]; . . .
Epá. Espere um pouco aí. Que história é esta que você está me dizendo? Como assim, temos duas maneiras de ver o mesmo código. E duas formas de escrever a mesma coisa? Agora de fato, fiquei bastante confuso. Pois isto não faz nenhum sentido para mim.
Pois bem meu caro leitor. Por isto venho fixando para que você estude e pratique o que está sendo mostrado em cada artigo. O grande problema que vejo, isto em quase todo iniciante, é que ele se apega muito a forma e modelos de programação. Ele de fato, não procura entender o conceito que está sendo criado e aplicado. Apenas procura se apegar ao que está sendo programado.
No artigo anterior, mencionei que quando criamos uma união, de fato criamos uma forma de compartilhar e dividir as coisas em blocos menores. Então você pode pegar um bloco de memória, que seria bem longo e o dividir em pequenos blocos menores a fim de manipular o conteúdo do próprio bloco. Por isto conseguimos girar, mudar, trocar um ponto específico sem de fato utilizar certos operadores. Não por que uma união é algo mágico. Mas sim por que o que estamos de fato criando é algo que pode ser visto de diversas maneiras ao mesmo tempo.
Apesar de não ter mencionado isto antes. Tanto uma quanto a outra forma de se escrever a linha 12, que podemos ver acima, representam a mesma coisa. Só que dividido de uma maneira diferente. No primeiro caso, temos um array com dois elementos. Onde cada elemento é composto de quatro bytes. Já no segundo caso temos os mesmo dois elementos, só que desta vez cada elemento é composto de quatro bytes. Muita gente considera complicado entender arrays multidimensionais. Porém um array multidimensional, como o que é visto na segunda forma de escrever a linha 12. Nada mais é do que o mesmo array visto antes. Só que escrito de uma maneira diferente.
Arrays multidimensionais, são muito uteis para lidar com diversas situações. Mas basicamente são melhor compreendidos quando formos falar de um outro tema no futuro. Que são matrizes. Até lá, não vejo motivo para falar ou explicar arrays multidimensionais. Então tenha calma, meu caro leitor. E procure focar no que está sendo mostrado aqui e agora. Ou seja, viva o presente e não se preocupe com o futuro.
Muito bem, passado este primeiro contato, e explicado o que pode ser um tanto quanto confuso sobre o funcionamento do código 01. Podemos ver uma outra coisa sendo feita.
Uma observação importante: Não vou entrar em detalhes de como o código 01 funciona. Pois isto já foi visto e explicado em outros artigos. Já que tudo que está sendo feito ali, foi mostrado, explicado anteriormente. Então se você não está conseguindo entender. Procure estudar o que foi mostrado antes. Já que não pretendo ou quero que você fique apegado ao código, ou a forma como ele está sendo implementado. Quero que você entenda o que está sendo feito, e que consiga criar sua própria solução, a fim de conseguir um resultado idêntico ao mostrado nas imagens. Não um resultado parecido. Mas um resultado IDÊNTICO.
Feita esta observação podemos partir para o que é o nosso próximo exemplo, que é visto logo na sequência.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. uint u32_bits[2]; 07. uchar u8_bits[8]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { 12. un_01 info; 13. 14. PrintFormat("The region is composed of %d bytes", sizeof(info)); 15. ZeroMemory(info); 16. View(info); 17. Set(info, 0xA1B2C3D4E5F6789A); 18. View(info); 19. info = Swap(info); 20. View(info); 21. } 22. //+------------------------------------------------------------------+ 23. void Set(un_01 &arg, ulong value) 24. { 25. arg.u32_bits[0] = (uint)(value >> 32); 26. arg.u32_bits[1] = (uint)(value & 0xFFFFFFFF); 27. } 28. //+------------------------------------------------------------------+ 29. void View(const un_01 &arg) 30. { 31. Print("------------"); 32. for(uchar c = 0; c < arg.u32_bits.Size(); c++) 33. PrintFormat("Index: [ %d ] = 0x%I64X", c, arg.u32_bits[c]); 34. } 35. //+------------------------------------------------------------------+ 36. un_01 Swap(const un_01 &arg) 37. { 38. un_01 info = arg; 39. 40. for (uchar i = 0, j = sizeof(info) - 1, tmp; i < j; i++, j--) 41. { 42. tmp = info.u8_bits[i]; 43. info.u8_bits[i] = info.u8_bits[j]; 44. info.u8_bits[j] = tmp; 45. } 46. 47. return info; 48. } 49. //+------------------------------------------------------------------+
Código 02
Agora vamos ver um código que quando executado gera um resultado diferente. Este é o que podemos observar sendo feito no código 02. Bem, mas ele parece muito com o código 01. Não parece? Meu caro leitor. Porém quando executado ele irá nos mostrar algo que é visto logo abaixo.
Imagem 02
Você pode estar pensando: Mas é a mesma coisa que foi obtida e mostrada na imagem 01. Bem, olhe de novo. Mas agora observe com ainda mais atenção esta imagem 02 e a compare com a imagem 01. Elas ainda, lhe parecem iguais, mesmo você olhando atentamente cada valor? Pois de fato elas são diferentes. E o motivo é justamente a declaração da união. Observe que agora temos dentro da união dois arrays. Ambos do tipo estático. Mas tem algo ali que não parece fazer muito sentido. No código 01, tínhamos o array da linha sete, contendo quanto elementos. Mas agora temos oito. Por que? Será que não poderíamos manter os mesmos quatro elementos?
Bem, para entender isto de uma maneira correta. Vamos por partes. Primeiro, vamos entender a questão de por que mudar o número de elementos ali. E o que isto de fato gera na união. Por conta de um pequeno detalhe matemático e para não complicar de maneira inútil o código. Vamos mudar o valor que está sendo colocado como sendo o número de elementos no array declarado na linha sete. De modo que este número seja sempre PAR. Se ele for ímpar irá dar um pequeno problema, o que dificultará entender os resultados. Então use sempre um número par ali. Vamos supor que no lugar de colocar oito elementos, coloquemos seis elementos. Todo o restante do código deverá permanecer inalterado. Assim termos algo como o visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. uint u32_bits[2]; 07. uchar u8_bits[6]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { . . .
Fragmento 01
Veja que apenas mudei o número na linha sete. E ao executar o código 02, com esta mudança vista acima. O resultado é o que você pode observar logo abaixo.
Imagem 03
Hum, isto não foi legal. Mas, vamos agora mudar o valor da linha sete para que o número de elementos seja igual a dez. Assim teremos o que é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. uint u32_bits[2]; 07. uchar u8_bits[10]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { . . .
Fragmento 02
Ao executar o código, você irá ver como resultado, o que é mostrado na imagem a seguir.
Imagem 04
Isto também não foi muito legal. Já que aparentemente alguns dos valores simplesmente desapareceram. Agora realmente não consegui entender por que isto ocorreu. Bem, meu caro leitor, é por isto que estamos aqui. Para lhe ajudar a entender, algo que à primeira vista não faz muito sentido. Mas conforme você for pegando experiência e praticando. Irá acabar entendendo melhor como e por que as coisas funcionam. Mas o principal é sempre procurar entender o conceito que está sendo aplicado.
No primeiro caso, onde tivemos a imagem 03 sendo mostrada. E com um erro, nos dizendo que o código teria falhado por tentar acessar uma região fora do range. O erro ocorreu, justamente por conta do fato de que a variável info é composta de oito bytes. Mas, e agora preste atenção, você tem a variável u8_bits, presente na união, contendo seis elementos, ou seis bytes. Já que cada elemento é composto de um byte, devido a largura do tipo uchar. Sendo assim, quando usamos a variável j, que naquele momento, estará apontando para o oitavo elemento em u8_bits. Termos uma falha na execução da linha 43. Por conta disto, o código falhou. Porém ele não falhou por conta que estamos usando mais ou menos elementos em u8_bits. Antes de mostrar como seria feita a correção para podemos utilizar seis elementos em u8_bits. Vamos entender o que ocorreu no segundo caso. Onde o resultado é o que podemos ver na imagem 04.
Neste caso você pode observar que info contém dez bytes, ou dez elementos. Justamente por conta que u8_bits, contem estes mesmos dez elementos. Assim quando fizermos o giro usando a função Swap, presente na linha 36. Parte destes valores serão trocados por valores que foram inicializados pelo procedimento da linha 15. Ou seja, zeros. Já que ZeroMemory, irá limpar todo o bloco presente na variável info.
Por conta disto alguns valores aparentemente sumiram. Mas eles não sumiram de fato. Apenas foram colocados em outro local. Para provar isto, vamos fazer uma pequena mudança no código. Em para evitar o mesmo resultado visto nas imagens 03 e 04. O novo código que iremos usar é mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. union un_01 05. { 06. uint u32_bits[2]; 07. uchar u8_bits[8]; 08. }; 09. //+------------------------------------------------------------------+ 10. void OnStart(void) 11. { 12. un_01 info; 13. 14. PrintFormat("The region is composed of %d bytes", sizeof(info)); 15. ZeroMemory(info); 16. Set(info, 0xA1B2C3D4E5F6789A); 17. View(info); 18. Debug(info); 19. info = Swap(info); 20. View(info); 21. Debug(info); 22. } 23. //+------------------------------------------------------------------+ 24. void Set(un_01 &arg, ulong value) 25. { 26. arg.u32_bits[0] = (uint)(value >> 32); 27. arg.u32_bits[1] = (uint)(value & 0xFFFFFFFF); 28. } 29. //+------------------------------------------------------------------+ 30. void View(const un_01 &arg) 31. { 32. Print("------------"); 33. for(uchar c = 0; c < arg.u32_bits.Size(); c++) 34. PrintFormat("Index: [ %d ] = 0x%I64X", c, arg.u32_bits[c]); 35. } 36. //+------------------------------------------------------------------+ 37. void Debug(const un_01 &arg) 38. { 39. string sz = ""; 40. 41. Print("*************"); 42. for(uchar c = 0; c < (uchar)arg.u8_bits.Size(); c++) 43. sz = StringFormat("%s0x%X ", sz, arg.u8_bits[c]); 44. PrintFormat("Number of elements in %cinfo.u8_bits.Size()%c is %d\nInternal content is [ %s ]", 34, 34, arg.u8_bits.Size(), sz); 45. Print("*************"); 46. } 47. //+------------------------------------------------------------------+ 48. un_01 Swap(const un_01 &arg) 49. { 50. un_01 info = arg; 51. 52. for (uchar i = 0, j = (uchar)(info.u8_bits.Size() - 1), tmp; i < j; i++, j--) 53. { 54. tmp = info.u8_bits[i]; 55. info.u8_bits[i] = info.u8_bits[j]; 56. info.u8_bits[j] = tmp; 57. } 58. 59. return info; 60. } 61. //+------------------------------------------------------------------+
Código 03
Ok, ao executar este código, você verá algo parecido com a imagem visto logo abaixo.
Imagem 05
Aqui temos algo, que tornará toda a explicação bem mais simples de ser compreendida. Note que estou marcando duas regiões nesta imagem 05. Mas observe o fato de que este código 03 é uma modificação do código 02. Onde a linha sete é que é o nosso real interesse aqui. Como precisamos de uma execução para podermos padronizar as coisas a fim de as entender. Estamos usando o mesmo valor na linha sete, neste código 03, que também foi usado no código 02. Fazendo assim com que o resultado final seja o que estamos vendo na imagem 05.
Agora preste muita atenção, meu caro leitor. Isto para que você consiga reproduzir o que será feito aqui, de maneira local. Note que na linha 52 do código 03, fiz uma mudança a fim de que a falha observada na imagem 03 não se repita. Desta forma, agora podemos fazer o que foi tentado anteriormente.
Primeiro vamos usar o fragmento 01 neste código 03. E ao fazermos isto, o resultado obtido é mostrado logo abaixo.
Imagem 06
Neste caso estou marcando mais pontos de interesse. Isto por que é muito importante que você entenda o que está sendo feito dentro do código. Note que apesar de termos um número menor de elementos em u8_bits, a operação aconteceu. Porém olhe o conteúdo do index um. Perceba que parte dele continua intacto. Por que? Bem, antes de responder isto, vamos ver o que ocorre, quando usamos o fragmento 02, neste código 03.
Ao fazermos isto, teremos como resultado o que é mostrado logo abaixo.
Imagem 07
É de fato a coisa não é tal simples como eu havia pensando. Calma meu caro leitor. Calma. Tudo aqui é muito simples e prático. Basta você prestar atenção e procurar praticar para de fato entender como as coisas funcionam. Não é somente fazer a leitura do artigo e estará tudo certo. É preciso praticar. Em ambos casos, tanto na imagem 06, quanto na imagem 07, você pode perceber que a memória está sendo rotacionada, ou espelhada como haveria de se esperar. Porém, o resultado observado nos índices de array, no caso de quando estamos acessando diretamente os elementos em u32_bits. Podem não corresponder ao que você havia imaginando. E o motivo para isto ocorrer, justamente o fato de que temos diferentes composições sendo feitas na memória.
No caso de quando a composição, do array declarado na memória, cobre completamente todos os bytes presentes ali, naquela região. Temos uma modelagem perfeita. Por isto vemos a imagem 05 sendo mostrada, nos dados exatamente o que imaginávamos encontrar. Mas quando, deixamos algum byte descoberto, ou venhamos a cobrir uma quantidade maior do que a realmente necessária, temos um resultado completamente diferente e disperso. E isto é mostrado nas imagens 06 e 07. Onde em uma estamos cobrindo uma região menor e na outra estamos cobrindo uma região maior.
Entendeu agora por que, de ter sido necessário mudar o valor do número de elementos na linha sete? Sem fazer isto de maneira adequada, acabamos ficando com uma cobertura inadequada em algumas situações. Por isto é necessário praticar e tentar novas formas de se fazer as coisas. Os arquivos estão no anexo, não para enfeitar o artigo. Mas para que você os utilize e aprenda o que acontece, quando mudamos algo em nossa forma de implementar algum tipo de solução. Então os utilize para aprender e conseguir desenvolver conceitos adequados sobre cada elementos de programação que está sendo mostrado nestes artigos. Somente assim você irá de fato se tornar um programador de excelência. E não mais um, cópia e cola.
Muito bem, acredito que já tenha sido dado uma boa base, sobre como você poderá e deverá estudar o que está sendo mostrado aqui nos artigos. Sendo assim, imagino que você já esteja pronto para que venhamos a resolver uma questão que foi mostrada lá no artigo Do básico ao intermediário: Array (IV). Na verdade, iremos tornar aquele código muito mais interessante. Se bem que para explicar isto, iremos para um novo tópico.
Tornando o que era bom ainda melhor
Aqui iremos fazer algo bastante divertido e muito legal. Porém, para um perfeito entendimento do que estará sendo feito aqui. É necessário que você tenha conseguido dominar o que foi visto no tópico anterior. Sem que este pré-requisito tenha de fato sido atingido, você poderá ficar muito confuso e perdido no que será mostrado neste tópico de agora. Então, não tenha presa. Estude com calma e paciência o que foi mostrado até aqui. Para somente depois se aventurar nesta nova e divertida brincadeira que iremos fazer usando o MQL5.
Dado este pequeno aviso, vamos a nossa questão principal deste tópico. Para isto, vamos ver o código que foi mostrado em um outro artigo que mencionei a pouco. Este é visto 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 04
Quando executado este código 04, irá nos reportar o seguinte resultado visto na imagem a logo abaixo.
Imagem 08
A brincadeira e desafio a ser feito aqui, é justamente tornar este código 04 em algo mais agradável de ser implementado de maneira a usar os recursos visto até este momento. Bem, você pode estar olhando e pensando: Este cara é muito doido. Como eu, que estou aprendendo a programar, posso conseguir usar o que foi visto até aqui, para tornar este código ainda melhor? Isto é insano. Bem, meu caro leitor, quem não pratica e não se desafia de vez em quando, acaba ficando para trás. Já que a todo momento surge novas coisas e conceitos mais adequados para se fazer o mesmo tipo de atividade.
E conforme estes novos conceitos vão surgindo, vai diminuindo o nosso trabalho, aumentando nossa produtividade e ao mesmo tempo dificultando que pessoas estáticas, do tipo cópia e cola. Possa nos seguir. Já que a cada momento, passamos a ver novas formas de se obter o mesmo resultado, mas de maneira mais simples. Então vamos aprender como podemos usar o conhecimento mostrado até aqui, para melhorar este código 04. Para isto, precisamos primeiro analisar o que está sendo feito ali.
Basicamente você nota que temos algumas variáveis, e que elas são colocadas em uma certa sequência. Sendo que a maior largura é dada pelo tipo uint, presente na linha oito, e utilizado entre as linhas 28 e 35. Todos os demais tipos são menores. Muito bem, sendo assim, podemos pensar em um tipo de solução onde o array Infos, declarado na linha 12 venha a receber as informações de maneira a produzir o mesmo conteúdo interno em sua região de memória. Mas qual está sendo este conteúdo? Bem, precisamos saber isto. Caso contrário, como teremos certeza que a implementação ficou correta?
Ok, se você observar a imagem 08. Irá notar quem em um determinado ponto, é mostrado o exato conteúdo presente no array Info. Sendo assim, tudo que precisamos é replicar aquele mesmo resultado. Porém de uma forma que o código fique mais agradável de ser utilizado.
Perfeito. Já temos por onde começar. E como foi mostrado aqui neste e no artigo anterior. Podemos utilizar uniões para conseguir estrutura de uma forma melhor os dados. Mas como? A primeira coisa que você precisa fazer, meu caro leitor, é criar uma declaração de união adequada para colocar os valores dentro dela. Como iremos precisar de valores do tipo uint, ushort e uchar. Isto por conta das linhas 28 até 48, onde acessamos as variáveis e constantes declaradas nas linhas oito, nove e dez. Você provavelmente imaginaria necessitar declarar três uniões. Porém bastará declarar uma como você pode ver no fragmento logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "Tutorial\File 01.mqh" 05. //+------------------------------------------------------------------+ 06. union un_Model 07. { 08. uint u32; 09. ushort u16; 10. uchar u8, 11. array[sizeof(uint)]; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnStart(void) 15. { . . .
Fragmento 03
Muito bem, observe neste fragmento 03, que na linha seis estamos criando uma união. E em cada uma das linhas estamos declarando um tipo discreto para utilizarmos depois. Isto talvez possa ser um pouco confuso à primeira vista. Mas a parte que nos importa de verdade é a linha onze. Pois é justamente nela que estamos fazendo com que toda a região pertencente a união dos tipos possa ser acessada. Note que ali estou declarando um array estático, com o número de elementos igual ao maior tipo discreto existente dentro da união. Este tipo de coisa pode ser feito sempre que você ficar na dúvida de quantos bytes são necessários dentro da união.
Agora você pode estar pensando: Mas espere um pouco. Se eu entendi direito. Quando mexemos em um valor, ou melhor dizendo, uma variável, presente em uma união. Não estamos mexendo em uma variável específica. Mas em todas as demais, pois elas fazem parte de uma mesma região da memória. Estou certo? Sim, meu caro leitor você está correto. Então não podemos usar esta união declarada neste fragmento. Pois assim que atribuirmos um valor a uma das variáveis ali. O valor das demais também será afetado. De fato, você está novamente correto ao pensar isto, meu caro leitor. No entanto, existe uma coisa que ainda não falei sobre as uniões e é isto que iremos usar aqui.
O fato é que, não nos interessa o valor que será atribuído em uma das variáveis. O que nos interessa é o que estará dentro do array. Isto sim é que nos interessa para que possamos construir o sistema desejado. Se você entendeu bem o que foi explicado em todos os artigos até aqui. E procurou praticar. Deve ter notado que podemos copiar um array para dentro de outro array. Então sabendo da quantidade de elementos que queremos copiar e tendo o array que queremos copiar. Podemos melhorar imensamente o código que está entre as linhas 28 até a linha 48. Como? Simples, copiando arrays. Para isto, usaremos o código que pode ser visto logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "Tutorial\File 01.mqh" 005. //+------------------------------------------------------------------+ 006. union un_Model 007. { 008. uint u32; 009. ushort u16; 010. uchar u8, 011. array[sizeof(uint)]; 012. }; 013. //+------------------------------------------------------------------+ 014. void OnStart(void) 015. { 016. const uint ui = 0xCADA5169; 017. ushort us = 0x43BC; 018. uchar uc = B'01011101'; 019. 020. enum eValues { 021. U32_Bits, 022. U16_Bits, 023. U8_Bits 024. }; 025. 026. uchar Infos[], 027. counter = 0; 028. 029. PrintFormat("Translation personal.\n" + 030. "FUNCTION: [%s]\n" + 031. "ui => 0x%s\n" + 032. "us => 0x%s\n" + 033. "uc => B'%s'\n", 034. __FUNCTION__, 035. ValueToString(ui, FORMAT_HEX), 036. ValueToString(us, FORMAT_HEX), 037. ValueToString(uc, FORMAT_BINARY) 038. ); 039. 040. for (eValues c = U32_Bits; c <= U8_Bits; c++) 041. { 042. un_Model data; 043. 044. ArrayResize(Infos, Infos.Size() + 1); 045. switch (c) 046. { 047. case U32_Bits: 048. Infos[counter++] = sizeof(ui); 049. data.u32 = ui; 050. break; 051. case U16_Bits: 052. Infos[counter++] = sizeof(us); 053. data.u16 = us; 054. break; 055. case U8_Bits: 056. Infos[counter++] = sizeof(uc); 057. data.u8 = uc; 058. break; 059. } 060. 061. counter += (uchar)ArrayCopy(Infos, Swap(data).array, counter, sizeof(data) - Infos[counter - 1]); 062. } 063. 064. Print("******************"); 065. PrintFormat("The Infos block contains %d bytes.", Infos.Size()); 066. ArrayPrint(Infos); 067. Print("******************"); 068. 069. Procedure(Infos); 070. 071. ArrayFree(Infos); 072. } 073. //+------------------------------------------------------------------+ 074. un_Model Swap(const un_Model &arg) 075. { 076. un_Model info = arg; 077. 078. for (uchar i = 0, j = (uchar)(info.array.Size() - 1), tmp; i < j; i++, j--) 079. { 080. tmp = info.array[i]; 081. info.array[i] = info.array[j]; 082. info.array[j] = tmp; 083. } 084. 085. return info; 086. } 087. //+------------------------------------------------------------------+ 088. void Procedure(const uchar &arg[]) 089. { 090. Print("Translation personal.\n" + 091. "FUNCTION: ", __FUNCTION__); 092. 093. ulong value; 094. 095. for (uchar c = 0; c < arg.Size(); ) 096. { 097. value = 0; 098. for (uchar j = arg[c++], i = 0; (c < arg.Size()) && (i < j); i++, c++) 099. value = (value << 8) | arg[c]; 100. Print("0x", ValueToString(value, FORMAT_HEX), " B'", ValueToString(value, FORMAT_BINARY), "'"); 101. } 102. } 103. //+------------------------------------------------------------------+
Código 05
Ao executar este código 05, você irá ver o resultado mostrado logo abaixo.
Imagem 09
Parece que funcionou. Bem, não é bem isto que de fato ocorreu aqui meu caro leitor. Você pode notar se observar a região que marquei nesta imagem 09 e a comparar com a imagem 08. Irá notar que os valores estão girados, ou espelhados. Isto não é problema para nós. Já que mostrei como podemos espelhar os valores. Porém antes de fazermos isto, quero mostrar uma outra solução. Que dependendo do caso pode ser suficiente.
Observe que os valores, mostrados na imagem 09 estão espelhados também entre o que foi mostrado na OnStart e o que foi mostrado em Procedure. Porém dependendo da situação, e é bom que isto fique claro. Podemos ignorar o fato de que o array está espelhado e usar algo visto logo abaixo.
. . . 62. //+------------------------------------------------------------------+ 63. void Procedure(const uchar &arg[]) 64. { 65. un_Model data; 66. 67. Print("Translation personal.\n" + 68. "FUNCTION: ", __FUNCTION__); 69. 70. for (uchar c = 0; c < arg.Size(); ) 71. { 72. ZeroMemory(data.array); 73. c += (uchar)(ArrayCopy(data.array, arg, 0, c + 1, arg[c]) + 1); 74. Print("0x", ValueToString(data.u32, FORMAT_HEX), " B'", ValueToString(data.u32, FORMAT_BINARY), "'"); 75. } 76. } 77. //+------------------------------------------------------------------+
Fragmento 04
Se, e que isto fique bem claro, for possível utilizar este fragmento 04 no código 05. O resultado a final é mostrado logo abaixo.
Imagem 10
Agora preste muita atenção meu caro leitor. Observe que na região em vermelho nesta imagem 10 temos os valores originais. Já na região em azul temos os valores de saída. Notem que eles não estão espelhados, em relação aos valores originais. Como estava ocorrendo na imagem 09. No entanto, a região em rosa que representa exatamente o conteúdo presente na memória se encontra espelhada, ou bagunçada. Assim como está sendo mostrada na imagem 09. No entanto, o resultado de saída e entrada da imagem 10 é o mesmo que você pode observar na imagem 08.
Assim sendo, se, e somente se, o conteúdo presente na memória puder ser ignorado. E o objetivo de fato seja conseguir a entrada e saída com valores corretos. Assim como é visto na imagem 10. A solução vista no fragmento 04 de fato poderia ser adotada. Mas dependendo do caso, este tipo de solução, não se adequa ao que realmente queremos ou podemos utilizar. Sendo de fato necessário que venhamos a espelhar os dados de modo que o resultado final venha a ficar da forma como é mostrado na imagem 08.
Assim precisamos fazer um pequeno acréscimo ao código. De forma a espelhar os valores antes de os colocarmos na memória a ser transferida. Isto é simples de ser feito. Basta mudar o código para o que é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "Tutorial\File 01.mqh" 05. //+------------------------------------------------------------------+ 06. union un_Model 07. { 08. uint u32; 09. ushort u16; 10. uchar u8, 11. array[sizeof(uint)]; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnStart(void) 15. { 16. const uint ui = 0xCADA5169; 17. ushort us = 0x43BC; 18. uchar uc = B'01011101'; 19. 20. uchar Infos[], 21. counter = 0; 22. 23. PrintFormat("Translation personal.\n" + 24. "FUNCTION: [%s]\n" + 25. "ui => 0x%s\n" + 26. "us => 0x%s\n" + 27. "uc => B'%s'\n", 28. __FUNCTION__, 29. ValueToString(ui, FORMAT_HEX), 30. ValueToString(us, FORMAT_HEX), 31. ValueToString(uc, FORMAT_BINARY) 32. ); 33. 34. { 35. un_Model data; 36. 37. ArrayResize(Infos, Infos.Size() + 1); 38. Infos[counter++] = sizeof(ui); 39. data.u32 = ui; 40. counter += (uchar)ArrayCopy(Infos, Swap(data).array, counter, 0, sizeof(ui)); 41. 42. ArrayResize(Infos, Infos.Size() + 1); 43. Infos[counter++] = sizeof(us); 44. data.u16 = us; 45. counter += (uchar)ArrayCopy(Infos, Swap(data).array, counter, 0, sizeof(us)); 46. 47. ArrayResize(Infos, Infos.Size() + 1); 48. Infos[counter++] = sizeof(uc); 49. data.u8 = uc; 50. counter += (uchar)ArrayCopy(Infos, Swap(data).array, counter, 0, sizeof(uc)); 51. } 52. 53. Print("******************"); 54. PrintFormat("The Infos block contains %d bytes.", Infos.Size()); 55. ArrayPrint(Infos); 56. Print("******************"); 57. 58. Procedure(Infos); 59. 60. ArrayFree(Infos); 61. } 62. //+------------------------------------------------------------------+ 63. un_Model Swap(const un_Model &arg) 64. { 65. un_Model info = arg; 66. 67. for (uchar i = 0, j = (uchar)(info.array.Size() - 1), tmp; i < j; i++, j--) 68. { 69. tmp = info.array[i]; 70. info.array[i] = info.array[j]; 71. info.array[j] = tmp; 72. } 73. 74. return info; 75. } 76. //+------------------------------------------------------------------+ 77. void Procedure(const uchar &arg[]) 78. { 79. Print("Translation personal.\n" + 80. "FUNCTION: ", __FUNCTION__); 81. 82. ulong value; 83. 84. for (uchar c = 0; c < arg.Size(); ) 85. { 86. value = 0; 87. for (uchar j = arg[c++], i = 0; (c < arg.Size()) && (i < j); i++, c++) 88. value = (value << 8) | arg[c]; 89. Print("0x", ValueToString(value, FORMAT_HEX), " B'", ValueToString(value, FORMAT_BINARY), "'"); 90. } 91. } 92. //+------------------------------------------------------------------+
Código 06
Bem, meu caro leitor, a mudança foi feita. No entanto, apesar de isto ter ocorrido. Veja só o resultado logo abaixo.
Imagem 11
Isto sim, costuma matar muito iniciante. Isto por que, apenas o primeiro valor está correto, da forma como se apresenta na imagem 08. No entanto, os demais estão errados. Por que? O motivo para isto está justamente na questão do valor de start que precisa ser configurado da maneira correta, meu caro leitor. Isto nas linhas 45 e 50. Como o valor de start está sendo igual a zero, acaba que ficamos com a posição inicial errada. Sendo feita a cópia apenas do primeiro elemento do array presente na união. E como você viu no tópico anterior. O array foi girado de modo que este primeiro elemento, agora é na verdade o último.
Então para corrigir isto, precisamos mudar as linhas 45 e 50 para o que é mostrado logo abaixo.
. . . 34. { 35. un_Model data; 36. 37. ArrayResize(Infos, Infos.Size() + 1); 38. Infos[counter++] = sizeof(ui); 39. data.u32 = ui; 40. counter += (uchar)ArrayCopy(Infos, Swap(data).array, counter, 0, sizeof(ui)); 41. 42. ArrayResize(Infos, Infos.Size() + 1); 43. Infos[counter++] = sizeof(us); 44. data.u16 = us; 45. counter += (uchar)ArrayCopy(Infos, Swap(data).array, counter, sizeof(data) - sizeof(us), sizeof(us)); 46. 47. ArrayResize(Infos, Infos.Size() + 1); 48. Infos[counter++] = sizeof(uc); 49. data.u8 = uc; 50. counter += (uchar)ArrayCopy(Infos, Swap(data).array, counter, sizeof(data) - sizeof(uc), sizeof(uc)); 51. } . . .
Fragmento 05
Ao executar o código 06, com estas mudanças mostradas no fragmento 05. O resultado é este que você pode observar na imagem 12. Ou seja, igual ao que era visto e esperado devido a imagem 08. Ou seja, funciona perfeitamente bem. Mas olhando este fragmento 05, você logo nota que podemos jogar isto dentro de um laço. Isto por que tem muitas partes sendo repetidas ali. Então o código final, que você de fato irá ter no anexo para poder estudar é visto logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "Tutorial\File 01.mqh" 005. //+------------------------------------------------------------------+ 006. union un_Model 007. { 008. uint u32; 009. ushort u16; 010. uchar u8, 011. array[sizeof(uint)]; 012. }; 013. //+------------------------------------------------------------------+ 014. void OnStart(void) 015. { 016. const uint ui = 0xCADA5169; 017. ushort us = 0x43BC; 018. uchar uc = B'01011101'; 019. 020. enum eValues { 021. U32_Bits, 022. U16_Bits, 023. U8_Bits 024. }; 025. 026. uchar Infos[], 027. counter = 0; 028. 029. PrintFormat("Translation personal.\n" + 030. "FUNCTION: [%s]\n" + 031. "ui => 0x%s\n" + 032. "us => 0x%s\n" + 033. "uc => B'%s'\n", 034. __FUNCTION__, 035. ValueToString(ui, FORMAT_HEX), 036. ValueToString(us, FORMAT_HEX), 037. ValueToString(uc, FORMAT_BINARY) 038. ); 039. 040. for (eValues c = U32_Bits; c <= U8_Bits; c++) 041. { 042. un_Model data; 043. 044. ArrayResize(Infos, Infos.Size() + 1); 045. switch (c) 046. { 047. case U32_Bits: 048. Infos[counter++] = sizeof(ui); 049. data.u32 = ui; 050. break; 051. case U16_Bits: 052. Infos[counter++] = sizeof(us); 053. data.u16 = us; 054. break; 055. case U8_Bits: 056. Infos[counter++] = sizeof(uc); 057. data.u8 = uc; 058. break; 059. } 060. 061. counter += (uchar)ArrayCopy(Infos, Swap(data).array, counter, sizeof(data) - Infos[counter - 1]); 062. } 063. 064. Print("******************"); 065. PrintFormat("The Infos block contains %d bytes.", Infos.Size()); 066. ArrayPrint(Infos); 067. Print("******************"); 068. 069. Procedure(Infos); 070. 071. ArrayFree(Infos); 072. } 073. //+------------------------------------------------------------------+ 074. un_Model Swap(const un_Model &arg) 075. { 076. un_Model info = arg; 077. 078. for (uchar i = 0, j = (uchar)(info.array.Size() - 1), tmp; i < j; i++, j--) 079. { 080. tmp = info.array[i]; 081. info.array[i] = info.array[j]; 082. info.array[j] = tmp; 083. } 084. 085. return info; 086. } 087. //+------------------------------------------------------------------+ 088. void Procedure(const uchar &arg[]) 089. { 090. Print("Translation personal.\n" + 091. "FUNCTION: ", __FUNCTION__); 092. 093. ulong value; 094. 095. for (uchar c = 0; c < arg.Size(); ) 096. { 097. value = 0; 098. for (uchar j = arg[c++], i = 0; (c < arg.Size()) && (i < j); i++, c++) 099. value = (value << 8) | arg[c]; 100. Print("0x", ValueToString(value, FORMAT_HEX), " B'", ValueToString(value, FORMAT_BINARY), "'"); 101. } 102. } 103. //+------------------------------------------------------------------+
Código 07
Considerações finais
Neste artigo tratamos de diversos pontos muito interessantes e com o objetivo claro de entender como dados são colocados na memória e podem ser trabalhados. Apesar do que foi visto aqui, vir a exigir que você, meu caro e estimado leitor, de fato se esforce um pouco além do que muitos conseguem ou admitem querer se esforçar. Todo e qualquer avanço que você conseguir fazer a fim de compreender o que foi mostrado aqui, neste artigo, irá lhe beneficiar bastante em diversas questões no futuro.
Isto por que, mesmo sem falar a respeito de certas coisas, aqui podemos ver que nem tudo é tão complicado como nos parece à primeira vista. E ao mesmo tempo, nem tudo é tão simples, que somente uma breve leitura, já nos fará entender algo. É preciso sempre estar praticando e estudando. Mas acima de tudo, é preciso se esforçar para tentar entender os conceitos envolvidos no desenvolvimento de uma aplicação qualquer.
O fato de simplesmente aceitar que um ou outro código, criado por outro programador, lhe parece mais adequado. Não irá de modo algum lhe ajudar a criar algo que de fato venha a ser adequado. Pode até parecer adequado naquele momento. Mas quando posto para funcionar. Acaba lhe decepcionando devido a falhas e problemas que você não conseguirá resolver devido a falta de conhecimento adequado. Então divirta-se com os códigos presentes no anexo e procure praticar e estudar o que foi visto aqui. E nos vemos no próximo artigo.





- 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