
From Basic to Intermediate: Array (III)
Introduction
In the previous article From Basic to Intermediate: Array (II), I explained the basics of using dynamic and static arrays, the difference between them, and the basic precautions to take while using arrays in applications.
We will continue to examine the topic of arrays. The prerequisite for understanding this article is a good understanding of the concepts covered in the previous one. Additionally, it is essential to understand how passing by value and passing by reference work. All these topics were discussed and demonstrated in earlier articles. So, if you have any doubts or are unfamiliar with these concepts because you're just starting, I suggest that you take some time to review the previous articles before continuing. This will ensure that you can follow along with the explanations.
As usual, let's begin a new section to continue our study and demonstrations.
Using Arrays in Functions and Procedures
One of the topics that often baffles beginners is the use of arrays as a means to pass values between functions and procedures. Here, I will be straightforward about one point regarding this concept. The way MQL5 handles this is much simpler than how some other languages deal with the same issue.
Languages such as C and C++ are extremely complex in this regard. In those cases, there is NO true array structure as there is in MQL5. Instead, we use another structure known as a pointer. The problem with pointers, which makes them complex and difficulty to master, stems from the fact that we can use indirect references in some cases. While this offers tremendous power to the programmer, it also makes the code much harder to understand. Especially for those with limited experience in coding and pointer usage.
In MQL5, things are much easier to grasp and apply. Starting with the simple fact that:
Every array is ALWAYS passed by reference, whether to a function or to a procedure.
There is no pass-by-value option when using an array as an argument in a function or procedure in MQL5.
Now, you might be thinking: "But if every argument using arrays is passed by reference, whether to a function or a procedure, doesn't that make the code less secure?" Actually, no, my dear reader. As surprising as it may sound, using arrays in MQL5 is far safer than using other types of variables.
In fact, I would go as far as to say that using arrays in MQL5 is significantly easier than employing any other method used in other programming languages. This is because, in MQL5, you have complete control over what can or cannot happen to an array, even though it is always passed by reference to a function or procedure.
What ensures this level of safety and reliability is, in fact, the programmer's careful attention. When I explained how we can use passing by reference or passing by value, I highlighted the pros and cons of each approach. But here, it's much simpler. The very declaration of a function or procedure makes this clear and evident.
There is a second key detail to consider. It concerns the fact that:
Any array declared as a parameter of a function or procedure must ALWAYS be dynamic.
With these two principles applied, you will be able to create any type of implementation in MQL5 that involves arrays. Often, it is more practical and appropriate to perform computations within functions or procedures rather than placing code inline.
Note: For those unfamiliar with the term "inline code," it refers to the practice where the programmer does not use functions or procedures in the application. Instead, they create a series of routines, one after another, like following a long recipe. Although there are rare scenarios where this approach might be feasible, it does happen. The main characteristic of such code is the complete absence of functions or procedures in the implementation.
Now, let's return to our main topic: how to use arrays as parameters in functions and procedures. Despite the apparent simplicity of this process, you, dear reader, should not become less careful and assume that everything is simple and risk-free.
In fact, there are certain nuances that only become clear when we put things into practice. The theory may seem neat, requiring us to understand only a few concepts. However, in practice, things can get tricky and occasionally bite back before we manage to overcome them. So, let's start by seeing how things actually work in practice. Let's begin with the code shown below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. const char Infos[] = {2, 3, 3, 5, 8}; 07. 08. Print("Based on the values:"); 09. ArrayPrint(Infos); 10. PrintFormat("There are %d possibilities.", Possibilities(Infos)); 11. } 12. //+------------------------------------------------------------------+ 13. ushort Possibilities(const uchar &arg[]) 14. { 15. ushort info = 0; 16. 17. for (uchar c = 0; c < arg.Size(); c++) 18. info = (c ? info * arg[c] : arg[c]); 19. 20. return info; 21. } 22. //+------------------------------------------------------------------+
Code 01
When code 05 is executed, you will see the result as shown in the image below.
Figure 01
What you're seeing in Figure 01 could be generated in many different ways. The method used in code 01 is purely for educational purposes. The goal here is precisely to explain what's happening in that output, to demonstrate how we can pass an array into a function or procedure.
So, let's break down what's going on here. I'll highlight only the parts that I consider not yet covered in any of the previous articles. Let's dive into the highlights, starting with line six. Although we're using a static array there, this is done merely to simplify the code. We could have used a dynamic array in that line, and nothing would change in terms of functionality. The actual goal is to pass the values into the function declared at line thirteen. So, anything that happens before line ten in code 01 doesn't affect how the array is passed. Well, as long as the function is declared as shown in line thirteen.
Now, pay close attention to what will be explained here, dear reader. In previous articles, we've seen that an array declared as constant MUST be initialized in the declaration itself. This is done to avoid compilation errors. However, look carefully at line thirteen. There, we're declaring an array as constant. Yet, the compiler still generates the executable.
Why is it allowed to declare it this way here, while it wouldn't work in line six? The reason is that in line thirteen, unlike line six, the array is being declared not as a variable but as a parameter. And as mentioned earlier in this topic, every array must be passed by reference. And that's exactly what's happening here.
A point that some of you, especially those new to programming, might be incorrectly thinking that the array declared in line thirteen is NOT DECLARED as constant because of the array in line six. One has nothing to do with the other. You could even declare and initialize the array in line six as fully dynamic. However, this wouldn't change the declaration in line thirteen because the context and purpose are completely different.
There's another detail in code 01 that, in my view, deserves to be highlighted again here, though it's been mentioned before. The issue relates to the test value in line seventeen. There, we use arg.Size(). This is allowed and perfectly valid, as the result is the same as if we had used the ArraySize function. In either case, the check would be performed in the same manner. As a homework assignment, try replacing arg.Size() with ArraySize to better understand how the code would be implemented in practice.
Great, I believe this initial overview has been relatively pleasant and enjoyable, giving you an idea of how to implement code to pass an array whether to a function or a procedure. The method of working with and declaring the array as a parameter in this specific context would not change. Only the purpose might differ.
This was the easy part. Now we can move on to something a bit more complex. However, to allow you to study each topic carefully, we'll cover these new complex things in a new section.
Modifying an Array Remotely
One of the use cases that can make working with arrays in functions and procedures a bit complicated is the ability to modify the contents of an array remotely. In other words, you pass an array to a function or procedure, and it is modified within that function or procedure. This behavior can lead to quite confusing and complex problems to resolve. However, since we can - and in many cases will - need to do this sort of thing, it is crucial that you fully understand what happens. But first of all, it is important to understand why this happens.
Unlike discrete values such as char, int, long, double, and similar types, most programming languages do NOT allow us to return arrays, except in special cases where the language itself provides a mechanism to handle this. One such example in MQL5 is strings.
As demonstrated in previous articles, a string is, in fact, a special type of array. This makes it one of the few cases where we can return an array from a function in MQL5. This feature already removes the risk and potential issues that might arise in certain implementations where we genuinely need to return a modified array.
But before we dive deeper into this topic, since it's quite a complex concept to understand in real code, we need to look at how other languages handle this problem. In C and C++, for instance, the responsibility is entirely placed in the hands of the programmer. In these two languages, we have both the option to modify the array and the option to return a completely new array. However, don't be misled into thinking this makes you less prone to errors. In fact, it exposes you to even more complex mistakes. That's one of the reasons why C and C++ are so challenging to master.
Other languages, such as Python and JavaScript, essentially bypass the use of conventional data types altogether. They implement their own methods to allow us to return arrays or even modify them. This process is less common and, therefore, somewhat simpler for certain types of refactoring. Nonetheless, our focus here is on MQL5.
So, let's start with the simplest example possible. We'll take code 01 and modify just a small detail. This leads us to what is shown below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char Infos[] = {2, 3, 3, 5, 8}; 07. 08. Print("Based on the values:"); 09. ArrayPrint(Infos); 10. PrintFormat("There are %d possibilities.\nValue after call:", Possibilities(Infos)); 11. ArrayPrint(Infos); 12. } 13. //+------------------------------------------------------------------+ 14. ushort Possibilities(uchar &arg[]) 15. { 16. ushort info = 0; 17. 18. for (uchar c = 0; c < arg.Size(); c++) 19. info = (c ? info * arg[c] : arg[c]); 20. 21. arg[0] += 2; 22. 23. return info; 24. } 25. //+------------------------------------------------------------------+
Code 02
When we run code 02, we'll get the output shown below.
Figure 02
Notice that in Figure 02, I'm highlighting two values. This is precisely to draw your attention to what is happening here.
Observe that the difference between code 02 and code 01 lies exactly in the fact that the parameter declaration in the function at line 14 is NO LONGER CONSTANT. Therefore, the pass-by-reference mechanism, which always occurs with arrays, allows us to use line 21, where we modify the value of one of the elements in the array declared back in line six.
Since we're working in a didactic context here, focusing on explaining certain concepts applicable to MQL5, this modification is quite easy to spot. However, in real-world code, it can be much more deeply embedded and often so entangled within the code that it might make you want to give up and start coding everything from scratch again, such is the complexity that can arise.
That's why, dear reader, I encourage you, especially if you're just beginning to study programming or have limited experience, to practice these concepts thoroughly. Only through practice will you gain the experience necessary to handle the problems that will inevitably arise.
Alright, code 02 is indeed the simplest type possible. It's almost trivial in a sense. However, things start to get more complicated as we delve deeper into the subject. So, let's look at another example, this time a bit more complex. It's shown just below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char Infos[]; 07. 08. ArrayResize(Infos, 7); 09. ZeroMemory(Infos); 10. 11. Infos[0] = 2; 12. Infos[1] = 3; 13. Infos[2] = 3; 14. Infos[3] = 5; 15. Infos[4] = 8; 16. 17. Print("Based on the values:"); 18. ArrayPrint(Infos); 19. PrintFormat("There are %d possibilities.\nValue after call:", Possibilities(Infos)); 20. ArrayPrint(Infos); 21. 22. ArrayFree(Infos); 23. } 24. //+------------------------------------------------------------------+ 25. ushort Possibilities(uchar &arg[]) 26. { 27. ushort info = 0; 28. 29. for (uchar c = 0; c < arg.Size(); c++) 30. info = (c ? (arg[c] ? info * arg[c] : info) : arg[c]); 31. 32. arg[arg.Size() - 1] = 9; 33. 34. return info; 35. } 36. //+------------------------------------------------------------------+
Code 03
Alright, in code 03, we encounter something a bit more complicated. However, the added complexity isn't due to line 30. This line remains quite simple.
The complexity of code 03 arises from the fact that in line 6, we're declaring a purely dynamic array. This is where things start to get truly complex. Despite this, code 03 is still something that can be completely and perfectly understood by a beginner who is following the material presented in these articles, taking one step at a time and practicing what's been demonstrated along the way.
But let's look at the result of running code 03. It's shown in the image just below.
Figure 03
Once again, I'm highlighting some information present in the image. This highlighted information is very important to fully understand.
So, let's break down what's happening here. In line eight, we're specifying that the array will have seven elements. Then, in lines eleven through fifteen, we're initializing some of these elements. You can see that in line 18, the array's contents contain some zero values. This is normal because in line nine, we're clearing the memory where the array was allocated.
It's precisely due to these zeros that it was necessary to add an extra ternary operator in line 30. Without it, the function result would be zero. However, this isn't the key point of interest. The real point of interest is in line 32. Notice that here, we're assigning a value to a specific element which is the last element in the array in this case.
But why am I doing this, and why in this sequence of demonstrations? The reason is that there's a way of working with arrays in MQL5 that allows us to do something that wouldn't naturally be possible. Unless you, dear reader, understand certain concepts related to array usage. At the same time, you'll start to notice certain things happening here that will make other concepts feel much more natural when we explore them shortly.
Great. We've already added a good dose of complexity to our system. However, we can still make things even more interesting. Honestly, if it were up to me, the article would end here to allow you to practice what has already been shown. And, let's be honest, that's already quite a lot, and many of these concepts are quite challenging to understand at first. But let's make one final effort to visualize something that can be interesting and is directly related to what has been covered here. Since it's a much more complicated concept, let's proceed to a new section.
Remote Initialization
At this point, I ask you, dear and esteemed reader and friend, the following: pause and study what has been covered so far. Once you've fully understood everything, leaving no doubts or confusion in your mind and ensuring you understand the previous codes, only then should you proceed to this section. This part becomes far too complex to handle without the proper preparation.
What we've seen so far shows that we can send data using arrays. After that, we learned that we can modify data in an array within another function or procedure. This must be done with great care, especially when you are creating a more elaborate application. Next, we saw that we can work with an array by adjusting where we will modify its value. Now it's time to do something else. This is shown in the next code.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char Infos[]; 07. 08. InitArray(Infos); 09. 10. Print("Based on the values:"); 11. ArrayPrint(Infos); 12. PrintFormat("There are %d possibilities.\nValue after call:", Possibilities(Infos)); 13. ArrayPrint(Infos); 14. 15. ArrayFree(Infos); 16. } 17. //+------------------------------------------------------------------+ 18. void InitArray(uchar &arg[]) 19. { 20. ArrayResize(arg, 7); 21. ZeroMemory(arg); 22. 23. arg[0] = 2; 24. arg[1] = 3; 25. arg[2] = 3; 26. arg[3] = 5; 27. arg[4] = 8; 28. } 29. //+------------------------------------------------------------------+ 30. ushort Possibilities(uchar &arg[]) 31. { 32. ushort info = 0; 33. 34. for (uchar c = 0; c < arg.Size(); c++) 35. info = (c ? (arg[c] ? info * arg[c] : info) : arg[c]); 36. 37. arg[arg.Size() - 1] = 9; 38. 39. return info; 40. } 41. //+------------------------------------------------------------------+
Code 04
And here we are, dear reader. This is the climax of what we'll be covering in this article. However, don't be fooled by the apparent simplicity of code 04. Despite the fact that its output is what we see just below, it's much more complex than it seems.
Figure 04
Notice that the output is exactly the same as what we saw in Figure 03, identical in every way. But here, in code 04, I'm showing something in a simple manner that, if handled incorrectly, can become extremely complex. However, when implemented correctly and in an appropriate manner, this approach can solve a range of issues. Especially things that some so-called "experienced programmers" claim are impossible to do in MQL5. At least in a pure form and without relying on any resources outside of what MQL5 itself provides.
I've made these changes gradually, precisely so you could follow what’s happening here. Although you might think that code 04 is just a simple variation of what's being done in code 03, code 04, deep down, is demonstrating that we can indeed do something that isn't possible without thoroughly understanding certain concepts.
One of these concepts - and it's exactly what we're exploring in code 04 - is the fact that every array is passed by reference. And when we do this, applying concepts covered in previous articles, we can indeed achieve something many would claim isn't possible. Namely: initializing or even modifying arrays outside the scope where they were declared.
We can take this idea even further, without increasing the level of difficulty we've covered so far. To explain this, let's subtly modify code 04.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char Infos[]; 07. 08. if (ArraySize(Infos)) 09. { 10. Print("Based on the values:"); 11. ArrayPrint(Infos); 12. }else 13. Print("Array has not been initialized yet."); 14. PrintFormat("There are %d possibilities.\nValue after call:", Possibilities(Infos)); 15. ArrayPrint(Infos); 16. 17. ArrayFree(Infos); 18. } 19. //+------------------------------------------------------------------+ 20. void InitArray(uchar &arg[]) 21. { 22. const char init [] = {2, 3, 3, 5, 8}; 23. 24. ArrayCopy(arg, init); 25. } 26. //+------------------------------------------------------------------+ 27. ushort Possibilities(uchar &arg[]) 28. { 29. ushort info = 0; 30. 31. InitArray(arg); 32. 33. for (uchar c = 0; c < arg.Size(); c++) 34. info = (c ? (arg[c] ? info * arg[c] : info) : arg[c]); 35. 36. ArrayResize(arg, arg.Size() + 1); 37. arg[arg.Size() - 1] = 9; 38. 39. return info; 40. } 41. //+------------------------------------------------------------------+
Code 05
When you run the above code 05, you will see what is shown in the image below.
Figure 05
Well then, now you might be asking yourself: How is this possible? This sort of thing doesn't make much sense. But here, dear reader, I'm just having a bit of fun with the same code we've been examining since the beginning of this article. There's absolutely no reason for panic or despair.
The key detail is that, unlike what most people usually do, here we're pushing the limits of concepts that are perfectly explorable within MQL5. However, in these last few code samples, we're taking things a bit beyond what many are able to understand. That's because they don't truly understand the concepts behind the implementation. They just copy and paste code snippets without actually understanding why the code works. And that's not what I want. I want you, dear reader, to truly understand the concepts involved. If you can achieve that, then it will have been completely worth writing these articles and demonstrating these things.
Alright. Since I don't yet want to delve into other details, especially in this article, let's dedicate the remainder of it to explaining code 05 in detail. After all, code 04 is a bit simpler than code 05.
So, let's see how code 05 works. First, we have, on line 6, the declaration of a purely dynamic array. This will need to be allocated at some point, so we can use memory as a storage space.
Suppose we're not sure whether the array from line 6 has been initialized or not. We use line 8 to check if there are any elements in the array. Remember: it's a purely dynamic array. If you initialize it directly on line 6, this line 8 will catch it. Even if it is initialized between lines 6 and 8, it will still be detected by line 8.
However, if the initialization is done exactly on line 6, where the array is declared, you'll encounter other issues. Try this out for yourself to see what kind of problems arise. It will help you practice and gain experience with arrays.
In any case, if there is at least one element in the array, lines 10 and 11 will execute, showing that elements exist in the array. If it hasn't been initialized, the message seen on line 13 will be displayed in the terminal.
Now comes the part that makes things really interesting. This happens due to line 14, which calls the function on line 27. Notice that up to this point, the array STILL HASN'T BEEN INITIALIZED. It will only be initialized when line 31 executes. That's because it's exactly on this line that the procedure from line 20 is called.
This procedure will, on line 24, initialize the dynamic array created on line 6. Now, notice that to do this, we'll use a static ROM-type array, shown on line 22. I know this may seem very confusing and hard to understand. But if you take a look at the ArrayCopy function from the standard library, you'll understand why this works. Basically, what this function does is copy one array into another. And there are countless scenarios where this can be extremely useful in real applications.
After that, we enter the loop on line 33, where calculations are performed to produce a return value for line 14.
However, before returning to line 14, we use line 36 to add a new element to the array declared on line 6, but which was initialized back on line 24. And this is the interesting part. Because if you don't initialize the array due to removing line 31 from the code, this 'Possibilities' function will return a value equal to what's on line 29. That's because the loop on line 33 won't execute.
However, when you check the contents of the array declared on line 6, you'll find that there's an element in the array. And this element has the value specified on line 37.
In fact, you'll need to experiment with this in practice to fully understand the explanation. So, as I have mentioned before, practice and use the content in the attachment to gain a deeper understanding of how everything works.
Final Considerations
In this article, things got a lot more exciting than in the previous ones because we're beginning to explore new possibilities using concepts that have been explained since the very first article. I know that for those who've jumped in here without having read the previous articles, much of what's been shown might seem extremely complex and confusing. However, for those who've been practicing and studying each article along the way, you already know how interesting and valuable this content is. It also opens the door to many possibilities, including one that was briefly touched upon in an older article.
That said, in the next article, we'll make something we've seen before so common in your daily programming that you won't even notice you're becoming a better programmer than many out there. By having the right concepts and knowing how to apply them, you'll free yourself from the limitations that many people get stuck in - simply because they don't understand the underlying programming concepts. They just copy and paste code. So, have fun with the files in the attachment, and I'll see you in the next article.
Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/15473





- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use