Русский Português
preview
Del básico al intermedio: Array (II)

Del básico al intermedio: Array (II)

MetaTrader 5Ejemplos | 24 marzo 2025, 15:29
231 0
CODE X
CODE X

Introducción

El contenido expuesto aquí tiene un propósito puramente didáctico. En ningún caso debe considerarse como una aplicación final, si el objetivo no es el estudio de los conceptos mostrados aquí.

En el artículo anterior Del básico al intermedio: Array (I), empezamos a hablar de uno de los temas más complicados y difíciles de dominar en programación. Sé que muchos pueden decir que eso es algo sencillo. Y es que no tiene sentido que yo diga que es tan complicado. Aunque, conforme avancemos, entenderás por qué digo que este tema es complicado y difícil de dominar. Porque es la base de todo lo demás.

Una vez que haya explicado y mostrado cómo se puede utilizar este tipo de cosas. Tú, mi querido lector, lograrás asimilar bien lo que se demostrará. Seguramente entenderás por qué existen otras cosas en un lenguaje de programación. Todo lo demás será mucho más simple de dominar y comprender.

La mayor dificultad aquí es, en realidad, presentar las cosas de manera que no abordemos otros temas aún no tratados. Intento explicar por qué se crearon algunas cosas. Sin mostrarlas aún. Esto se debe a que es mucho más importante entender el concepto que hay detrás de tales herramientas que la herramienta en sí. Y, como muchos programadores ignoran el concepto y se aferran a la herramienta, en ocasiones se quedan sin opciones. Esto se debe a que no es la herramienta la que resuelve el problema. Sino el concepto. Es como un martillo: sirve para clavar cosas. Pero no es la herramienta la que resuelve el problema, sino el concepto. Puede servir para clavar cosas, pero no es lo único que se puede hacer con él. Aunque también sirve para demoler cosas. A pesar de que exista una herramienta mejor, que sería una maza.

Muy bien, antes de comenzar, existe un requisito previo que será necesario para comprender este artículo. El mismo es conocer y entender qué sería una variable y qué sería una constante.


Array tipo ROM

Existen básicamente dos formas de declarar un array. Una es declarar un array estático y la otra es declararlo como array dinámico. Aunque en la práctica entender cada uno es relativamente simple. Existen algunas pequeñas sutilezas que dificultan o imposibilitan que comprendamos correctamente qué es un array dinámico y qué es un array estático. Esto ocurre especialmente en otros lenguajes. Especialmente C y C++. Sin embargo, incluso en MQL5, en algunos momentos puedes tener dudas. Esto se debe a que la diferencia básica y esencial entre un array estático y uno dinámico es la posibilidad de que pueda cambiar de tamaño durante la ejecución del código.

Pensando de esta manera, parece sencillo clasificar un array como dinámico o estático. Aunque conviene recordar que una cadena es un array. Solo que es un array especial. Esto hace que resulte difícil clasificar una cadena como estática o dinámica. No obstante, vamos a ignorar este hecho. Y no trabajaremos directamente con el tipo string. Así evitaremos confusiones e iremos comprendiendo este tema.

Básicamente, y esto sin que surja ninguna discusión, sea en cualquier lenguaje de programación que estudies en el futuro, un array del tipo constante es siempre un array estático. Da igual el lenguaje que utilices. SIEMPRE será del tipo estático.

Bien, pero ¿por qué podemos afirmar que un array constante es siempre del tipo estático? El motivo es que un array constante, y tienes que pensar en él de esta manera, es en realidad una memoria ROM. Tal vez en este punto no tenga mucho sentido. Esto se debe a que nuestra aplicación siempre se ejecutará en una región de la memoria RAM. ¿Cómo podemos entonces imaginar una memoria ROM, en la que solo se pueden leer datos, dentro de una memoria RAM que, a su vez, nos permite leer y escribir información en cualquier punto de esta? Esto no tiene sentido.

Precisamente por esta razón, mi querido lector, es necesario que entiendas qué son las constantes y variables. Pero, desde el punto de vista de nuestra aplicación, podemos tener una memoria ROM en uso sin que esto comprometa la integridad y la usabilidad de nuestra aplicación. De hecho, usar arrays constantes con el fin de crear una pequeña ROM en una aplicación es algo relativamente común. Esto es algo relativamente común en códigos más complejos o que necesiten valores muy específicos que no vayan a cambiar.

Piensa en los mensajes que se utilizan para reportar algo. Puedes colocarlos todos juntos y traducirlos a diferentes idiomas. En el momento en que se vaya a cargar la aplicación, se verificará el idioma de los mensajes y se creará la memoria ROM. Así, la aplicación podrá ser utilizada por diferentes usuarios que utilizan diferentes idiomas. Esta sería una aplicación para un array que estaría en una ROM. Aunque, si utilizas archivos con las traducciones y necesitas cargar el archivo correcto, ya no tendríamos un array del tipo ROM. O puramente estático. Aunque todavía pudiera ser clasificado de esa manera.

Para hacer esto más sencillo de entender, ya que este concepto es importante para que sepas cómo y cuándo podrás utilizar las llamadas a la biblioteca estándar para controlar arrays, vamos a ver un ejemplo sencillo. Veremos este tipo de cosas en la práctica.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     const char Rom_01[]  = {72, 101, 108, 111, 33};
07.     const char Rom_02[8] = {'H', 'e', 'l', 'o', '!'};
08. 
09.     PrintFormat("%c%c%c%c%c%c", Rom_01[0], Rom_01[1], Rom_02[2], Rom_01[2], Rom_02[3], Rom_01[4]);
10. }
11. //+------------------------------------------------------------------+

Código 01

Cuando este código 01 es ejecutado, el resultado se muestra justo abajo.

Imagen 01

Es algo bastante simple de entender. Sin embargo, lo que nos interesa aquí son las líneas seis y siete. En ambos casos se crean dos ROM con el mismo contenido aparente. Pero con tamaños totalmente diferentes. Pero espera un momento, ¿cómo es eso? No lo entiendo. Al mirar el código, veo que ambas tienen cinco elementos. Y que todos los elementos mostrados son iguales. Solo están declarados de manera diferente. Pero no consigo entender por qué estas dos ROM son diferentes a pesar de eso. Para mí no tiene sentido.

Es verdad, mi querido lector, que ambas tienen los mismos elementos declarados. Aunque de forma diferente. Pero lo que las hace diferentes es precisamente la forma de declararlos. Ahora presta atención para entender algo importante.

La ROM_01, que se declara en la línea seis, cuenta con cinco elementos. Normalmente, en un código donde creamos un array estático, encontraremos este tipo de declaración. Es decir, no existe un valor dentro de los corchetes. Aunque, y esta es la parte que confunde a mucha gente, no declarar un valor entre corchetes en el caso de un array constante, es algo perfectamente normal. Y hasta bastante habitual. Esto se debe a que puedes añadir, DURANTE LA IMPLEMENTACIÓN, más valores al array durante la implementación. Los valores que se añadan quedarán completamente bloqueados, no podrán modificarse después. Con esto, el tamaño del array termina siendo el número de elementos que contiene.

Ahora, en relación con ROM_02, la cosa es un poco diferente. En este caso, tenemos declarados cinco elementos. Sin embargo, debido a que afirmamos que el array tiene ocho elementos, tres de estos elementos son desconocidos. Debes tener cuidado al utilizar estos elementos desconocidos. Esto se debe a que puede que tengan un cierto valor, pero también puede que tengan un valor completamente aleatorio. Esto dependerá de cómo el compilador vaya a crear el array. Recordad que estamos trabajando con arrays del tipo constante.

En cualquier caso, en ambos casos tendremos un array del tipo estático. Es decir, el número de elementos, NO CAMBIARÁ durante todo el tiempo de vida del array. En un caso tendremos siempre cinco elementos y en el otro, siempre ocho. Recordemos que el primer elemento tiene índice igual a cero. Es decir, la cuenta siempre empieza en cero.

De acuerdo. Sabemos cuántos elementos hay en cada uno de los arrays. Pero ¿qué pasa si intentamos leer el elemento del índice igual a cinco? Recordad que la cuenta empieza en cero. Bien, en este caso podemos hacer una pequeña prueba para que puedas verificarlo. Para ello, vamos a cambiar una cosa en el código, de modo que quede como se muestra justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     const char Rom_01[]  = {72, 101, 108, 111, 33};
07.     const char Rom_02[8] = {'H', 'e', 'l', 'o', '!'};
08. 
09.     const uchar pos = 6;
10. 
11.     PrintFormat("%c%c%c%c%c%c", Rom_01[0], Rom_01[1], Rom_02[2], Rom_01[2], Rom_02[3], Rom_01[4]);
12. 
13.     PrintFormat("Contents of position %d of the ROM_02 array: %d", pos, Rom_02[pos - 1]);
14.     PrintFormat("Contents of position %d of the ROM_01 array: %d", pos, Rom_01[pos - 1]);
15. }
16. //+------------------------------------------------------------------+

Código 02

Como podemos ver, ahora tenemos en la línea nueve de este código 02 una constante que indica qué elemento estamos queriendo acceder. En este caso, queremos el sexto elemento. Sin embargo, está en la posición cinco. Podrías pensar que, como estamos buscando el elemento en la posición cinco y hay cinco elementos declarados, queremos imprimir el valor decimal de ese elemento en las líneas 13 y 14, es decir, 33. De hecho, esta es la forma más natural de pensar. Sin embargo, esta es la forma más natural de pensar, pero está equivocada. Esto se debe a que la cuenta se inicia en cero. Por tanto, el quinto elemento que se está declarando, es, en realidad, el elemento cuyo índice es cuatro. Por lo tanto, cuando intentemos acceder al índice cinco, algo sucederá en el código. El resultado es lo que puedes observar justo abajo.

Imagen 02

Observa que aquí están ocurriendo dos cosas extrañas. La primera es que se ha ejecutado la línea 13 y el resultado mostrado es un cero. Es decir, el array declarado en la línea 7 contiene valores ocultos. Pero lo importante está precisamente en el mensaje de error que se ve en esta imagen 02. Nos indica que en la línea 14 del código 02 existe un intento de acceder a algo fuera del array. Como el array que se va a utilizar en esta línea 14 es precisamente el array declarado en la línea 6, es posible que no sepas por qué se ha generado ese error. Esto se debe precisamente a que estás intentando acceder al elemento que está en la quinta posición. Sin embargo, como el array tiene cinco elementos, debería ser posible hacerlo. Pero, nuevamente, la cuenta se inicia a partir del cero.

Para confirmar esto, vamos a hacer un cambio. Este cambio se encuentra en la línea nueve de este código 02. Al cambiar el valor seis, que se ve allí, por el valor cinco, como se muestra en la línea de código de abajo, las cosas cambiarán.

const uchar pos = 5;

Al compilar y ejecutar nuevamente el código 02 usando esta línea mostrada anteriormente, el resultado en el terminal se muestra a continuación.

Imagen 03

¿Notaste cómo ahora el código logró de hecho mostrar el contenido correcto? Con esto, creo que tú conseguirás comprender cómo debemos acceder a los arrays. Y cómo esa pequeña diferencia en la declaración del array puede llevarnos a un tipo de resultado completamente distinto.

Muy bien, como los arrays del tipo ROM son siempre estáticos, incluso cuando los declaramos dinámicos, no hay mucho más que decir sobre ellos aquí. Esto se debe a que este tipo de array, donde los intentos de escritura se consideran un error, y el código no se compila, no nos permite hablar de más cosas en este tema. Así que vamos a tratar sobre un nuevo tema, en el que hablaremos acerca de otro tipo de array, que es solo un poco más complicado. Esto se debe a que podemos escribir en él.


Array tipo RAM

Para entender este array, es necesario haber comprendido primero el array de tipo ROM. La declaración puede ser estática o dinámica. A diferencia del array tipo ROM, cuyo contenido y número de elementos son constantes, en el array tipo RAM las cosas son un poco más complicadas. Esto se debe a que el número de elementos puede ser constante o no. Además, el contenido de cada elemento puede variar conforme se ejecute el código.

Bien, con estas primeras palabras, quizá estés imaginando que este tipo de array es una pesadilla viviente. Aunque solo exige un poco más de atención para usarlo correctamente. Aquí es donde cada detalle marca la diferencia. Sin embargo, existen algunos detalles relacionados con los arrays en MQL5 que me gustaría que ignoraras por un tiempo, mi querido lector. Se debe a que existe un tipo especial de array que sirve como buffer. Esto es en relación con MQL5. Pero este tipo se explicará mejor en otro momento. Cuando hablemos de cálculos y programación orientada a MetaTrader 5. Por ahora, lo que se explicará aquí no se aplica a estos arrays cuyo objetivo es servir como buffer de datos. El propósito aquí, y en este momento, es ofrecer una explicación general sobre la utilización de arrays en programación. No solo orientado a MQL5, sino a cualquier lenguaje de programación.

Está bien, entonces vamos hacer lo siguiente: con base en el último código visto en el tema anterior, vamos remover la palabra reservada const de ambas declaraciones. Al hacer esto, quitamos la limitación de los arrays. Sin embargo, existe un detalle muy importante a ser explicado. Esto cuando hacemos el cambio, con el fin de crear el código que se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char Ram_01[]  = {72, 101, 108, 111, 33};
07.     char Ram_02[8] = {'H', 'e', 'l', 'o', '!'};
08. 
09.     const uchar pos = 5;
10. 
11.     PrintFormat("%c%c%c%c%c%c", Ram_01[0], Ram_01[1], Ram_02[2], Ram_01[2], Ram_02[3], Ram_01[4]);
12. 
13.     PrintFormat("Contents of position %d of the RAM_02 array: %d", pos, Ram_02[pos - 1]);
14.     PrintFormat("Contents of position %d of the RAM_01 array: %d", pos, Ram_01[pos - 1]);
15. }
16. //+------------------------------------------------------------------+

Código 03

Cuando ejecutes este código 03, obtendrás el mismo resultado que se ve en la imagen 03. Pero hay una diferencia crucial. Y esta diferencia es precisamente que NO ESTAMOS TRABAJANDO CON UNA ROM. Sino con una RAM. Esto nos permite modificar el código 03 para crear lo que se muestra justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char Ram_01[]  = {72, 101, 108, 111, 33};
07.     char Ram_02[8] = {'H', 'e', 'l', 'o', '!'};
08. 
09.     uchar pos = 5;
10. 
11.     PrintFormat("%c%c%c%c%c%c", Ram_01[0], Ram_01[1], Ram_02[2], Ram_01[2], Ram_02[3], Ram_01[4]);
12. 
13.     PrintFormat("Contents of position %d of the Ram_02 array: %d", pos, Ram_02[pos - 1]);
14.     PrintFormat("Contents of position %d of the Ram_01 array: %d", pos, Ram_01[pos - 1]);
15. 
16.     Ram_02[pos - 1] = '$';
17.     PrintFormat("Contents of position %d of the Ram_02 array: %d", pos, Ram_02[pos - 1]);
18. 
19.     Ram_01[pos - 1] = '$';
20.     PrintFormat("Contents of position %d of the Ram_01 array: %d", pos, Ram_01[pos - 1]);
21. }
22. //+------------------------------------------------------------------+

Código 04

Este código 04 es muy curioso e interesante. Esto se debe a que en las líneas 16 y 19 estamos intentando modificar el valor de una posición concreta del array. Si se entiende bien, esto puede despertar bastante la curiosidad de los más osados. Entonces, cuando ejecutemos el código 04, obtendremos el resultado que se ve justo abajo.

Imagen 04

Nota que fue posible modificar los valores. Esto no sería posible en el caso de que estuviéramos utilizando un array del tipo ROM. Ahora mismo debes estar preguntándote una cosa: ¿podemos cambiar cualquier información dentro del array? La respuesta es: depende.

Observa lo siguiente, mi querido lector: el array de la línea seis, a pesar de ser del tipo dinámico, debido a que no se le ha indicado un índice de número de elementos, sigue siendo estático. Esto se debe a que lo iniciamos en la propia declaración. El array de la línea siete es totalmente estático. Aunque el número de elementos posibles dentro de él es mayor que el número de elementos que estamos declarando en la inicialización del array.

Entendiendo este hecho, puedes decir lo siguiente: si usamos un índice que apunte a algún elemento, podemos cambiar su valor. ¿Cierto? Es correcto. De hecho, podrías utilizar un código parecido al que se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char r[10] = {72, 101, 108, 108, 111, 33};
07. 
08.     string sz0;
09. 
10.     sz0 = "";
11.     for (uchar c = 0; c < ArraySize(r); c++)
12.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
13. 
14.     Print(sz0);
15. 
16.     Print("Modifying a value...");
17. 
18.     r[7] = 36;
19. 
20.     sz0 = "";
21.     for (uchar c = 0; c < ArraySize(r); c++)
22.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
23. 
24.     Print(sz0);
25. }
26. //+------------------------------------------------------------------+

Código 05

Al ejecutar este código 05, el resultado sería lo que podemos ver en la imagen justo abajo.

Imagen 05

Es decir, que funciona. Podemos cambiar cualquier valor, siempre y cuando el elemento al que se acceda exista realmente dentro del array. Aunque esta línea 18 solo funcionó porque en la línea 6 informamos de que el array tendría 10 elementos. De acuerdo, creo que se ha entendido esta primera parte. Pero ¿tendremos siempre que trabajar de esta manera? Es decir, cuando declaramos un array, ¿tenemos que indicar en la declaración cuántos elementos debe tener o cuáles deben existir? De hecho, esta es una cuestión que genera muchas dudas a los principiantes. Y nuevamente la respuesta es: depende. Aunque, y es importante que esto quede claro para ti, querido lector, un array constante, es decir, del tipo ROM, deberá SIEMPRE tener sus elementos, o su número, declarados en el momento de la creación del array. Un detalle importante: la declaración de los elementos en este caso es obligatoria. No obstante, declarar el número de elementos es opcional.

Esta regla es rígida y no admite cambios ni interpretaciones. Sin embargo, no existe una regla clara para los arrays del tipo RAM. Todo depende del propósito u objetivo que se quiera alcanzar. Sin embargo, declarar elementos en un array del tipo dinámico en un array que no sea del tipo ROM, es decir, constante, no es común. Como se observa en la línea seis del código 04. Esto se debe a que, al hacer esto, estamos transformando el array dinámico en un array estático. Algo similar ocurre en el caso del array de la línea siete del mismo código 04.

Aunque, desde el punto de vista de la aplicación, un array declarado como se muestra en la línea seis del código 04 podría ser sustituido por otro como el que se muestra justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char  Ram_01[5],
07.           pos = 5;
08.     
09.     Ram_01[0] = 72;
10.     Ram_01[1] = 101;
11.     Ram_01[2] = 108;
12.     Ram_01[3] = 111;
13.     Ram_01[4] = 33;
14. 
15.     PrintFormat("%c%c%c%c%c%c", Ram_01[0], Ram_01[1], Ram_01[2], Ram_01[2], Ram_01[3], Ram_01[4]);
16. 
17.     PrintFormat("Contents of position %d of the Ram_01 array: %d", pos, Ram_01[pos - 1]);
18.     Ram_01[pos - 1] = '$';
19.     PrintFormat("Contents of position %d of the Ram_01 array: %d", pos, Ram_01[pos - 1]);
20. }
21. //+------------------------------------------------------------------+

Código 06

Como puedes ver, en este código 06 tenemos ahora la declaración de un array estático en la línea seis. Al hacer esto, le indicamos al compilador que necesitamos que sea él quien asigne un bloque de memoria. Esto se hará de manera totalmente automática. Así, durante la compilación, el compilador asignará un espacio en la memoria suficientemente grande para contener la cantidad de elementos indicados. En este caso, cinco elementos. Y como el tipo es char, se asignarán cinco bytes y estarán disponibles para uso inmediato. Ahora piensa en este espacio asignado como si fuera una variable de cinco bytes. Tú puedes usarla como mejor te convenga. También podemos asignar un número arbitrario. Por ejemplo, si en lugar de cinco, como se ve en la línea seis, colocamos 100, crearíamos una variable de 100 bytes. Recordemos que el mayor ancho de banda actualmente es de 8 bytes, o 64 bits. Es decir, podríamos colocar poco más de 12 de estas variables de 8 bytes dentro de este array de 100 bytes.

De cualquier manera, cuando tú ejecutes este código 06, verás lo que se muestra justo abajo.

Imagen 06

Ahora, fíjate en que la inicialización no se ha realizado en la declaración del array, sino entre las líneas nueve y trece. Funciona igual que en la línea 18 de este código 06. Como se hizo en los códigos anteriores.

Aunque existe un pequeño detalle aquí. Como el array está siendo creado de manera estática, si necesitas más espacio por cualquier motivo, no podrás asignar más espacio en la memoria. Esto ocurre durante la fase de ejecución del código. Por esta razón, los arrays estáticos se usan en situaciones muy específicas. Lo mismo ocurre con los arrays dinámicos. Pero si, en lugar de definir un valor para indicar el número de elementos en el array, como se ve en la línea 6 de este código, se utilizaría la línea que se ve justo debajo.

    char  Ram_01[]

Tendríamos la declaración de un array totalmente dinámico. Ahora presta mucha atención, querido lector. Al hacer esto, le estamos diciendo al compilador que seremos nosotros, los programadores, quienes nos encargaremos del array, asignando y liberando la memoria conforme sea necesario. Sin embargo, tan pronto como se ejecute la línea nueve, se generará un error. Esto se debe a que se está intentando acceder a una memoria que aún no ha sido asignada. Este error puede verse justo abajo.

Imagen 07

No es raro tener la incidencia de este tipo de error en programas con muchas variables. Aunque corregirlo es muy sencillo. Basta con asignar la memoria antes de usar el array. Luego, cuando hayamos utilizado el array, debemos liberar la memoria. Esta liberación debe hacerse de manera explícita, ya que es una buena práctica de programación. En algunos casos más simples, en códigos mucho más modestos y menos profesionales, no se hace esta liberación. Aunque no es recomendable. Ya que normalmente la memoria asignada no se libera solo porque hayas dejado de usarla. Entonces, comienza haciendo las cosas de la forma correcta. Si has asignado memoria, libérala tan pronto como ya no la necesites para evitar errores extraños durante la ejecución del código.

Bien, para terminar, veamos cómo se debe corregir el código para usar un array dinámico. A continuación, cambiamos el código 06 por el código 07.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char   r[];
07.     string sz0;
08.     
09.     PrintFormat("Allocated enough position for %d elements", ArrayResize(r, 10));
10. 
11.     r[0] = 72;
12.     r[1] = 101;
13.     r[2] = 108;
14.     r[3] = 111;
15.     r[4] = 33;
16. 
17.     sz0 = "";
18.     for (uchar c = 0; c < ArraySize(r); c++)
19.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
20.     Print(sz0);
21. 
22.     Print("Modifying a value...");
23. 
24.     r[7] = 36;
25. 
26.     sz0 = "";
27.     for (uchar c = 0; c < ArraySize(r); c++)
28.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
29.     Print(sz0);
30. 
31.     ArrayFree(r);
32. }
33. //+------------------------------------------------------------------+

Código 07

Y ahí lo tienes, querido lector, un código que hace uso de un array puramente dinámico. Como verás, se parece bastante al código 05. Esto es intencional. Precisamente para mostrar que podemos hacer lo mismo, solo que de maneras diferentes. El resultado de la ejecución del código 07 se ve justo abajo.

Imagen 08

Pero fíjate en la imagen 08, donde estoy marcando información importante. Quiero que mi querido y estimado lector preste mucha atención a lo que voy a explicar. Porque es muy importante. Normalmente, los programadores menos cuidadosos o cuyo código no se utilizará para propósitos críticos, simplemente usan la memoria sin inicializarla adecuadamente. Este tipo de práctica genera una cantidad absurda de errores difíciles de detectar. Incluso por programadores extremadamente experimentados. Aquí muestro uno de estos errores. Fíjate en que la memoria se asignó en la línea 9 del código 07. Entre las líneas 11 y 15, asignamos valores a algunas posiciones de dicha memoria. Sin embargo, cuando hacemos la lectura para obtener el contenido de la memoria, vemos que hay información extraña. A esta información se le llama basura, ya que existe en la memoria, pero no debería estar allí.

Pero la parte importante aquí es precisamente la línea 22, ya que nos indica que en ese momento estaremos modificando la memoria. Y esto se hace en la línea 24. No obstante, al hacer una nueva lectura de la memoria, parece que el programa sabe lo que va a pasar. Antes de haber aplicado el valor a esa posición de memoria, el valor habría surgido de la nada. Como por arte de magia.

Bien, ¿y por qué ocurrió esto? No se trata de ningún truco de magia ni de viajar en el tiempo. El motivo está directamente relacionado con el hecho de que la memoria asignada no está bajo tu control. Es el sistema operativo quien asigna la memoria. Y lo que hay allí puede ser de todo. Puede contener restos de información de otros programas, lo que se llama «basura», o incluso un montón de ceros. El contenido es siempre aleatorio. Sin embargo, si el sistema operativo asigna la misma posición de memoria para dos ejecuciones consecutivas, donde la memoria fue asignada, liberada y luego asignada nuevamente, que puede ocurrir con un mismo código o con cualquier otro, es muy probable que allí haya basura.

Y si imaginas que el contenido es uno u otro, puedes acabar teniendo un problema realmente difícil de resolver. Por eso, NUNCA, pero NUNCA JAMÁS, pienses que la memoria contiene un dato específico. NUNCA. Siempre que asignes memoria, LIMPIA la región o, al menos, inicialízala completamente. Existen diversas formas de hacerlo en MQL5. Yo mismo prefiero usar una llamada bastante simple y eficiente de la biblioteca estándar para tal cosa. Así, el código 07 corregido se muestra justo debajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char   r[];
07.     string sz0;
08.     
09.     PrintFormat("Allocated enough position for %d elements", ArrayResize(r, 10));
10. 
11.     ZeroMemory(r);
12. 
13.     r[0] = 72;
14.     r[1] = 101;
15.     r[2] = 108;
16.     r[3] = 111;
17.     r[4] = 33;
18. 
19.     sz0 = "";
20.     for (uchar c = 0; c < ArraySize(r); c++)
21.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
22.     Print(sz0);
23. 
24.     Print("Modifying a value...");
25. 
26.     r[7] = 36;
27. 
28.     sz0 = "";
29.     for (uchar c = 0; c < ArraySize(r); c++)
30.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
31.     Print(sz0);
32. 
33.     ArrayFree(r);
34. }
35. //+------------------------------------------------------------------+

Código 08

Al ejecutar este código 08, tendrás lo que se ve a continuación.

Imagen 09

Se puede observar que la única diferencia entre las imágenes 08 y 09 es precisamente la posición de la memoria. En la primera ejecución, había basura en ese lugar. Pero, cuando añadimos la línea 11, que se ve en el código 08, ese tipo de error, en el que la basura estaba siendo o podría estar siendo utilizada, deja de ocurrir. Simplemente así. Aunque, como dije, existen otras funciones con el mismo objetivo en MQL5. Todo es cuestión de elección y del objetivo que se quiere alcanzar.


Consideraciones finales

En este artículo, que en realidad es la base de algo mucho más complicado que lo mostrado aquí, demostré cómo podrías ver un array de una manera un poco más profesional. Aunque puede que no hayas logrado captar lo que quise mostrar, ya que este tipo de cosa es mucho más complicada de explicar que cualquier otro tema de programación. De todas formas, creo haber alcanzado parte del objetivo principal. Por ejemplo, expliqué cómo podemos crear una memoria ROM dentro de la memoria RAM. También vimos cómo se elige si se usará un array dinámico o estático.

Sin embargo, es importante tener en cuenta que nunca debes suponer nada en lo que respecta a un array o el acceso a la memoria. Porque elementos no inicializados pueden contener basura. Y usar basura en un código puede causar daños graves en los resultados esperados o dificultar la resolución de problemas de interacción entre programas diferentes.

De todos modos, en el anexo tendrás acceso a los códigos que hemos visto aquí. Así podrás estudiar y practicar con algo que de hecho funciona. Tanto en lo que se refiere a generar fallos de ejecución, para que aprendas a lidiar con ellos, como a conceptos relacionados con el uso de arrays en actividades más recreativas.

Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/15472

Archivos adjuntos |
Anexo.zip (3.32 KB)
Métodos de William Gann (Parte I): Creación del indicador de ángulos de Gann Métodos de William Gann (Parte I): Creación del indicador de ángulos de Gann
¿Cuál es la esencia de la teoría de Gann? ¿Cómo se construyen los ángulos de Gann? Crearemos un indicador de ángulos de Gann para MetaTrader 5.
Redes neuronales en el trading: Modelo Universal de Generación de Trayectorias (UniTraj) Redes neuronales en el trading: Modelo Universal de Generación de Trayectorias (UniTraj)
La comprensión del comportamiento de los agentes es importante en distintos ámbitos, pero la mayoría de los métodos se centran en una única tarea (comprensión, eliminación del ruido, predicción), lo cual reduce su eficacia en escenarios del mundo real. En este artículo, propongo al lector introducir un modelo capaz de adaptarse a diferentes tareas.
Del básico al intermedio: Array (III) Del básico al intermedio: Array (III)
En este artículo, veremos cómo trabajar con arrays en MQL5, hasta el punto de transferir información entre funciones y procedimientos mediante arrays. El objetivo es prepararte para lo que se verá y explicará en artículos futuros. No obstante, es extremadamente recomendable que estudies muy bien lo que se mostrará en este artículo.
Algoritmo de optimización basado en la migración animal (Animal Migration Optimization, AMO) Algoritmo de optimización basado en la migración animal (Animal Migration Optimization, AMO)
El artículo está dedicado al algoritmo AMO, que modela la migración estacional de los animales en busca de condiciones óptimas para la vida y la reproducción. Las principales características de AMO incluyen el uso de vecindad topológica y un mecanismo de actualización probabilística, lo que lo hace fácil de implementar y flexible para diversas tareas de optimización.