
From Basic to Intermediate: Arrays and Strings (I)
Introduction
The content presented here is intended solely for educational purposes. Under no circumstances should the application be viewed for any purpose other than to learn and master the concepts presented.
In the previous article, "From Basic to Intermediate: Operator Precedence", we talked a little about the precautions we should take when using factorization in our codes. It's not uncommon to come across code that appears correct at first glance but ends up producing completely unexpected results in certain situations. This type of issue is often directly related to how the factorizations are implemented. Getting a piece of code to deliver consistent results may seem like a trivial task. However, without the proper knowledge (as demonstrated in that article), the likelihood of catastrophic failures increases as more and more poorly implemented factorizations are introduced into the code.
Since the theoretical part of properly creating factorizations can be quite boring and tedious, we won't dwell on it. Instead, let's focus on the practical side of things. That said, I encourage you, dear reader, to experiment with the sample code provided whenever possible. Try altering the factorizations or even modifying the control flow to differ slightly from what is presented in the attached files.
This practice is extremely important, as it helps you learn how to achieve the same result by following a different path, one that you've created yourself. Though many might view this as a waste of time, it is, in fact, the most effective way to learn. That's because the attachment contains code that is functional and produces a valid result. Your task is to develop a different version of that code that still produces the same outcome. With practice, you will be able to create your own solutions, even if at first they don't seem professional.
With that brief introduction, let’s move on to the main topic of this article. There is one prerequisite for understanding the content ahead. You should already have a solid understanding of declaring and using variables and constants. Although the focus here is much more on operators than on anything else, the ability to declare, initialize, and use variables and constants is essential to follow what will be explained. Here, we will begin discussing some special data types. I will make every effort to avoid getting stuck in theory alone. This topic, alongside operator precedence, which we covered in the previous article, is probably one of the more complex areas. However, for now, we'll approach it from the simplest angle possible in order to introduce the subject clearly. Still, this foundational knowledge is necessary before we dive into an even more challenging topic. So, let's get started.
Arrays and Strings
Without a doubt, this is one of the topics that causes the most confusion and often discouragement for new programmers working with statically typed languages. This is because dynamically typed languages like Python and JavaScript handle these topics almost transparently, allowing programmers to use them without worrying too much about definitions or structures.
In contrast, languages like C and C++ make this topic significantly more complex, due to how these two entities are handled internally. In these languages, arrays and strings are not treated as distinct entities. In fact, depending on how you work with them, you may encounter what seems like a third or even fourth type of entity within the same context. In short, it's a complex subject for beginners to understand.
However, in MQL5, we find ourselves in a middle ground between statically typed languages like C/C++ and dynamically typed ones like Python or JavaScript. Why do I say this? Unlike C and C++, where it's often possible to disregard the data type of an array and, in some cases, of strings as well, MQL5 does not allow for this kind of flexibility. At least not without specific kinds of manipulation. This actually simplifies the learning process somewhat. But without a solid foundation, it becomes nearly impossible to perform certain types of data manipulation in MQL5, especially the kind you could easily do in C or C++. That said, it is far less likely that you, as a beginner, will write something in MQL5 that spirals out of your control. Unlike in C or C++, where even a small mistake can turn into a ticking time bomb ready to go off at the slightest misstep.
But between us, learning MQL5 is considerably easier than learning how to accomplish the same tasks in C or C++. That's why I'm pleased to share and translate my knowledge of C and C++ to help make it applicable to MQL5. Perhaps, I can help some of you reach a strong level of programming expertise along the way.
Let's now s get to the main topic. To start, let's make one thing clear. Arrays and strings are, in a sense, the same thing. At least from the perspective of how data is stored in memory. More precisely, a string is nothing more than a special kind of array. I call it "special" because of one specific detail: a string contains a marker that indicates where it ends. This marker doesn't exist in a regular array.
Let me draw a quick comparison with other programming languages to clarify. Some languages, such as those based on BASIC (yes, the old-school BASIC from the MS-DOS era) store the length of the string in its first character. This allows any character to be used within the string, since the length is explicitly provided up front. This first character, which can occupy more than one byte, is never displayed. It remains invisible to the user, accessible only through code. This method gives you the freedom to use any character or numeric value within the string. But it limits the maximum length of the string to the bit-width of that initial character.
C and C++-based languages use a different approach. In these languages, a special character or value is used to indicate where the string ends. The concept of a string as a native data type doesn't truly exist in C or C++. Typically, a null or zero value is used as a terminator. When this value appears in the sequence, it marks the end of the string. On one hand, this approach allows you to create a string that can occupy virtually all available memory. On the other hand, it means you cannot use the null character within the string's content.
Since MQL5 is based on C and C++, it follows this same logic. However, unlike in C and C++, MQL5 does include a string type. As a result, we can't always manipulate just strings. In those cases, using an array would be more appropriate. Of course, doing so introduces other problems. Fortunately, the MQL5 library is robust enough to offer workarounds for most of these issues. Some are simple, others are more complex.
Why am I explaining all this? The reason is simple: without a proper understanding of how strings and arrays are modeled, you'll be limited to a narrow set of options and won't be able to perform certain tasks. Especially more advanced programming operations. It's not uncommon to hear people complain that "you can't do this or that in MQL5". But when you dig deeper, you often find that these programmers are stuck within a limited conceptual framework that prevents them from going beyond the basic offerings of the standard library.
It's not that using the standard library reflects a lack of knowledge. Quite the opposite, in fact. But if you don't understand how things are truly interconnected, you'll constantly find yourself complaining that certain things can't be done.
So, dear reader, understand this: Arrays can be thought of as a kind of generic string, capable of holding virtually anything. A string, on the other hand, is a more constrained construct that comes with limitations and, at times, outright restrictions. If you genuinely want to build something but aren't aware of the constraints imposed by arrays or strings, you simply won't be able to do it. One of the reasons arrays can be a bit difficult to master is that a string is essentially an array of type uchar or ushort, depending on the character encoding being used. A pure array, however, can be of any type: from a class to a boolean. This undoubtedly complicates things for beginners. But since I aim to show you a practical path forward, we'll begin with the most basic type. So, let's start with strings.
String Data Types
When we create or declare a variable as a string, we're essentially telling the compiler to create an array. This is a special kind of array that includes a character or value to indicate where the string ends. At a deeper level, when using a string variable, we don't need to manage memory allocation manually. That's handled automatically by the system as more or less memory is required.
This allows us to do a wide variety of things without too much concern. On the other hand, there are some limitations, but we won't worry about those for now. Let's focus on what can be done. Remember that true mastery comes only through consistent practice. Here, we're just going to scratch the surface. So, let's begin with our first piece of code, shown below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string sz0 = "Hello world !!!", 07. sz1 = "My first operation with strings...", 08. sz2; 09. 10. sz2 = sz0 + sz1; 11. 12. Print(sz2); 13. } 14. //+------------------------------------------------------------------+
Code 01
Now pay close attention, dear reader. Here we have three string variables being created. Two of them are declared and initialized at the moment of creation. The third one receives the result of combining the other two, thereby creating a new string. Line 12 simply prints the result to the terminal, as shown below:
Figure 01
The key detail here is the following: Notice that on line 10, we are using the addition (+) operator. This is one of the few (if not the only) operators that, by default, performs a defined operation on strings. In this case, it concatenates one string with another. However, be very careful with this kind of concept because operators don't always behave the way we might expect, especially when used with different data types. But we will cover this topic in more detail later. For now, just be cautious when working with code that you're not familiar with.
So, we have a sentence being displayed in the terminal. But what if we want to split it into multiple lines? How would we do that? One way would be to use two separate Print statements. But if you want to insert a line break directly within the string, you can do so by using certain special characters.
There are various special characters that can be included in a string. All of them - at least those supported in MQL5 - are derived from C/C++. So, if you want to add a line break at a specific point, you just need to include something like this:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string sz0 = "Hello world !!!", 07. sz1 = "My first operation with strings...", 08. sz2; 09. 10. sz2 = sz0 + "\n" + sz1; 11. 12. Print(sz2); 13. } 14. //+------------------------------------------------------------------+
Code 02
Here we have an example of one such marker being used. These markers, like "\n", are commonly referred to as escape sequences. If you do a bit of research, you'll find many of these small codes. When you include an escape sequence within a string, the output changes, allowing us to achieve results like the one shown below:
Figure 02
However, escape sequences don't stop there. For instance, we can truncate a string by using a NULL marker, as you can see in the following example:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string sz0 = "Hello world !!!", 07. sz1 = "My first operation \0with strings...", 08. sz2; 09. 10. sz2 = sz0 + "\n" + sz1; 11. 12. Print(sz2); 13. } 14. //+------------------------------------------------------------------+
Code 03
When this Code 03 is executed, the MetaTrader 5 terminal will display the following output:
Figure 03
Notice how easy and simple this is. But beyond that, we can also create strings that contain numeric values. In such cases, we'll need to rely on functions from the MQL5 standard library to make the conversion easier and more efficient. Perhaps at some point in the future, I'll show you how to build your own custom version for a similar purpose. But that's a topic for another time. For now, by using the built-in conversion functions available in MQL5, we can convert numbers to strings and strings back into numbers.
This functionality is incredibly useful and often essential, especially when building data analysis systems. A simple example is shown below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int i_value = 65538; 07. double d_value = 0.25; 08. color cor = clrRed; 09. string sz0; 10. 11. sz0 = "Color: " + ColorToString(cor) + "\nInteger: " + IntegerToString(i_value) + "\nDouble: " + DoubleToString(d_value); 12. 13. Print(sz0); 14. } 15. //+------------------------------------------------------------------+
Code 04
When you run Code 04, you'll see an output similar to the one shown below:
Figure 04
Notice that we were able to create a string containing various pieces of information. However, in some cases, we need the string we're constructing to follow a very specific format. This can be due to the nature of the information we're working with or the formatting requirements of the data itself. In such scenarios, we need to approach things a bit differently than what we've seen so far. Given the nature of what's coming next, I believe it's best to separate this into a new topic. That way, it will be easier for you to study and apply the knowledge presented here.
Formatting Strings
When we talk about text formatting, many people immediately think of word processors. But in programming, formatting refers to the need for certain pieces of information to meet specific criteria often before they're even used or displayed.
These criteria are what constitute string formatting. At first glance, this seems relatively simple and easy to implement. It allows us to build strings with highly specific content in a fairly simple way. However, there are a few important considerations that you, as a programmer, need to pay attention to. One of these details involves the construction and use of output parameters. When properly configured, these parameters can make life much easier even for beginner programmers since they largely replace the manual string construction approach we used in the previous topic. At the same time, they offer great flexibility in how we format our output, enabling us to build strings with precise structures.
Now, you may have already seen the use of the PrintFormat procedure in some code. This function allows us to display formatted output in the MetaTrader 5 terminal in the form of a string. However, there are times when we don't want, or it simply isn't suitable, to send this output directly to the terminal. In many cases, we might want to store the formatted data in a file or use it in a graphical object on a chart. In such situations, PrintFormat isn't always the right tool. Fortunately, MQL5 offers another function that's far better suited for these use cases: StringFormat.
The StringFormat function uses the same parameter structure as PrintFormat. But unlike PrintFormat, its output is returned as a string. This makes it extremely useful in a variety of situations.
So, we can take the output logic from Code 04 and adapt it to produce a formatted string. I understand that at this stage, some of the parameters might be a bit confusing. So I recommend you take the time to study each parameter carefully. Read the official documentation for the PrintFormat function. It provides detailed information about each formatting option and how to structure your desired string output. To illustrate this, let's modify Code 04 to include string formatting. This leads us to the version shown below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int i_value = 65538; 07. double d_value = 0.25; 08. color cor = clrRed; 09. string sz0; 10. 11. sz0 = StringFormat("Color: %s\nInteger: %d\nDouble : %.2f", ColorToString(cor), i_value, d_value); 12. 13. Print(sz0); 14. } 15. //+------------------------------------------------------------------+
Code 05
By running code 05, we will see a result in the MetaTrader 5 terminal similar to the following figure:
Figure 05
Look at the differences between Figure 04 and Figure 05. Even though both examples use very similar code, the information displayed in Image 05 appears in a format that was explicitly defined in our code, specifically during the implementation of line 11. Now, you might argue that formatting like this isn't really necessary in this case. However, consider the possibility that at some point, you'll be working with hexadecimal values. Many programs make use of such values, especially when dealing with bitwise operations. In these scenarios, if you wanted to visualize those values to ensure everything is working as expected, how do you do that?
Well, that depends. But generally speaking, the formatting approach shown in Code 05 can be very useful for displaying hexadecimal values. Some might even say it's worth writing a custom piece of code just for that purpose. In my opinion, though, doing so is more about personal satisfaction than actual necessity. In any case, every programmer is free to make their own choices. But since you're still getting started in programming, I recommend sticking with the functions and procedures provided by the standard library. In this case, that means using the StringFormat function to create a hexadecimal representation of values you may want to display later. To do this, we will continue using Code 05, but we will change the string formatting used to construct the output. Fortunately, this is very easy and direct: all you need to do is replace line 11 with the version shown below.
sz0 = StringFormat("Color: 0x%X\nInteger: 0x%X\nDouble : %.2f", cor, i_value, d_value);
When you run this modified code, you'll see the following output:
Figure 06
Interesting, isn't it? But we’ve hit a small problem here. And this is exactly why I mentioned earlier that you need to pay attention to the details involved in string formatting. The issue, in this case, lies with the color value. Notice that it's displayed in hexadecimal format. However, the value shown in Figure 06 doesn't necessarily represent the color red. In fact, it could be representing something entirely different. Remember, color values are typically formatted as RGB, or sometimes ARGB. So just by looking at the hex value, it can be hard to tell what it really means. But with a small adjustment, we can change the output to be more intuitive and human-readable. To do this, simply change the code as shown below:
sz0 = StringFormat("Color: 0x%06X\nInteger: 0x%X\nDouble : %.2f", cor, i_value, d_value);
Once you execute the code with this new modification, the result will look something like this:
Figure 07
Wait! This is definitely not red. What appears here is blue. So what's going on here? Could the problem be that we're adding zeros in front of the value to match a specific format? And that's somehow messing it up? Well, yes and no. What's really happening here is a bit more subtle. To explain it properly, we'll need to show another example that uses the same formatting concept, but applied to a different kind of value.
Let's modify the value of the variable i_value and ask the program to format it in the same way as we formatted the color. That is, depending on the value being displayed, there may be leading zeros. The following example should make things clearer:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int i_value = 0xF; 07. double d_value = 0.25; 08. color cor = clrRed; 09. string sz0; 10. 11. sz0 = StringFormat("Color :\t0x%06X\t=>\t%s\n" + 12. "Integer:\t0x%06X\t=>\t%d\n" + 13. "Double :\t%.2f", cor, ColorToString(cor), i_value, i_value, d_value); 14. 15. Print(sz0); 16. } 17. //+------------------------------------------------------------------+
Code 06
When we run Code 06, we will see something like this:
Figure 08
Here, I've structured the output to make it easier for you to understand what I'm trying to demonstrate. Note that in lines 11 and 12 of the code we're printing two values. This is done to visually demonstrate why the hexadecimal color value is being displayed the way it is. As you can see, the i_value result seems perfectly fine. Now, let's change the i_value to a larger number. Once you do that (as shown in the next version of the code), the output will remain consistent, as you'll see in the next figure:
int i_value = 0xDA09F;
Figure 09
So yes, the formatting works perfectly. But why doesn't it show the color value correctly in hexadecimal? Because even though we often expect color values to follow the RGB format, they're actually stored internally in BGR format. That means the byte order is reversed. So even though the value in the hex field looks wrong, it is correct. Can we fix this to make the output match the more intuitive format? Yes, we can do it. And with the knowledge gained from the previous examples, making this adjustment is actually quite simple. The revised code that achieves this is shown below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int i_value = 0xDA09F; 07. double d_value = 0.25; 08. color cor = clrRoyalBlue; 09. string sz0; 10. 11. sz0 = StringFormat("Color :\t0x%06X\t=>\t%s\n" + 12. "Integer:\t0x%06X\t=>\t%d\n" + 13. "Double :\t%.2f", cor, ColorToString(cor), i_value, i_value, d_value); 14. 15. Print(sz0); 16. PrintFormat("Color value in hexadecimal (RGB format): %s", ColorToStrHex(cor)); 17. } 18. //+------------------------------------------------------------------+ 19. string ColorToStrHex(const color arg) 20. { 21. return StringFormat("0x%02X%02X%02X", (arg & 0xFF), ((arg >> 8) & 0xFF), (arg >> 16) & 0xFF); 22. } 23. //+------------------------------------------------------------------+
Code 07
Notice that we're defining a small function on line 19. This function is quite simple to understand based on the concepts presented in my previous articles. When we execute it, the function returns a hexadecimal string. However, this returned value will now reflect the expected RGB format when we analyze color values. When you run Code 07, you'll see something similar to the output shown in Figure 10.
Figure 10
Now here’s something interesting. In the first line of Figure 10, you can see the color value displayed in hexadecimal format. But that value does not match what we were expecting. However, if you take a look at the last line of the same image, you'll find exactly the value we were expecting, assuming the color is being read as RGB. Those values you see on the first line are actually reversed in byte order, but we can flip them so the result appears in the expected format. Important note: the value shown in the last line of Figure 10 does not represent the actual color defined back in line 8 of Code 07. The formatted string here is simply showing how the ColorToString function rearranges the bytes for proper representation. So, don't worry. I changed the color value to ensure the explanation would make sense visually and logically.
Final Considerations
In this article, we've introduced the first part of a topic that is far deeper and more advanced than it may initially appear. I understand that some readers may be feeling a little confused about today's explanations. But I assure you, that if you take the time to carefully review and practice, you'll soon be capable of developing very useful and powerful applications from a user communication standpoint.
Even though what we've explored today only scratches the surface of what's possible, you already have plenty of material to study and apply in your projects. In the next article, we'll dive a bit deeper into more advanced formatting and memory handling, which will be essential for explaining future topics. So until then, enjoy the files attached and practice what you've learned. See you in the next article!
Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/15441





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