‘const’ usage and different case studies
In this article, let’s understand const pointer and will go through a different case study involving const with pointer.
The first case is constant data.
uint8_t const data = 50 is a case of constant data.
What are the use cases of this?
You can use this case to define mathematical constants in the program. Like to define the value of pi or any mathematical constants, which you use in your application.
The second case is using the ‘const’ keyword with pointers.
Case 2 is modifiable pointer and constant data.
Here, you observe uint8_t *pData = (uint8_t*) 0x10000000 statement. *pData is a pointer variable definition and it is initialized to the address 0x40000000.
Just introduce const here.
uint8_t const *pData = (uint8_t*) 0x40000000;
This is the case of modifiable pointer and constant data.
That means, here the pointer pData is modifiable but the data pointed by the pData cannot be modifiable, that is read-only. That means here the data is read-only not a pointer remember.
So, we can say that pData is a pointer pointing to read-only data.
Now let’s understand how to read this statement.
First, identify the variable,*pData is a variable, and move to the left. So, pData is a pointer pointing to constant data of type uint8_t. That’s how you should read it.
Figure 3 shows the allowed and Not-Allowed statements.
In allowed statements, you can change the value of the pointer variable. That means you can assign different addresses to the *pData pointer variable. So that statements are allowed(as shown in Figure 3).
But *pData = 50 is not allowed. That means you cannot be able to modify the data pointed by the pointer.
What is a use case of modifiable pointer and constant data?
Here, look at one example. Consider the copy_src_to_dst function . This function copies data from the source pointer to the destination pointer.
Here when you take a look into the first function implementation, whatever the data which is pointed by the source that is the source pointer needs to be copied into the destination. And then destination pointer should be increased to point to the next location and the source pointer needs to be incremented to point to the next location until all length bytes are copied.
Here, the guy who is implementing this function if he ever tries to do something like this src is equal to some other value. The compiler will not alert him in this case. The compiler will be quiet.
What is the goal of this function?
The goal of this function is to copy whatever there in the source to destination, the data which is pointed by the source pointer should not be modified. But if there is any such activity then it will not be reported to the programmer. So, the first method is prone to mistakes.
That’s why you can change the prototype of that function.
The second method is actually the defensive implementation. What you did was, you just mentioned ‘const’ here.
So, if you just try to do this, then the compiler triggers an error saying that “Hey, the data which is pointed by the source should be constant but you are trying to change it”, that’s a mistake. So, a programmer will be alerted here.
A programmer when sees void copy_src_to_dst(uint8_t *src, uint8_t *dst, uint8_t len) prototype, then there is a possibility that the programmer may change the data which is pointed by the source. Because there is no guard here for the source. He may think that it’s OK to change the data pointed by the source.
But when the programmer sees void copy_src_to_dst(uint8_t const *src, uint8_t *dst, uint8_t len) prototype, then he clearly understands that the data pointed by the source should not be modified. That’s why I would always go with this implementation. I mean the code implementation is the same, but the prototype implementation is different here. So, that’s the only difference compared to this implementation.
You can see the data guarding or pointer guarding in all your professional code implementations.
For example, consider the prototype of an open system call in Linux.
Now, you can see that the const char *path is a case of modifiable pointer and constant data.
This is a prototype of the write() system call.
What write() system call does?
Writes up to count bytes from buffer to file referred by the file descriptor. Buffer is given by the user, that’s user data. So, user data has to be constant. The implementation should not modify that. That’s why it is guarded with const.
Hope you understood the use case scenarios and use const generously wherever you find the chance to use it. And in the following article, let’s understand Case 3. Case 3 is modifiable data and a constant pointer.
Get Microcontroller Embedded C Programming Full course on Here.
FastBit Embedded Brain Academy Courses,
Click here: https://fastbitlab.com/course1