C pointer to a const - how to quieten warnings - c

I have an array like this:
const uint8_t Translations[] =
{
0xA6,0xA0,1,0,
0xA1,0x87,2,0,
0xA0,0xBE,3,0,
0,
};
and I would like to access it with a pointer:
uint8_t * p_translations = Translations;
I get the warning:
warning: assigning to 'uint8_t *' from 'const uint8_t *const' discards qualifiers.
I'm fine with that as I only read from and never write to *p_translations.
Is there a way to quieten the compiler (Microchip XC8) without suppressing warnings?

There is usually never a good reason to ever discard the const qualifier, as the warning states. Some possible reasons in the wild are while passing to APIs that expect raw unqualified pointers. If the API doesn't need to change the data pointed to by the pointer, it should have a const in the declaration.
Also, compiler warnings are usually something to aid the developer and are mostly warnings that should be heeded to.
For your case too, there's no reason why p_translations should not be a uint8_t const* if you never intend to modify what p_translations points to. If on the other hand, you had to modify Translations, then you should drop the const altogether from Translations. Use const to indicate entities that shall remain unchanged throughout the program lifecycle. It's perfectly fine to have a non-const array that would be modified, else adopt const. Here is one of many links to read on why const can be helpful apart from indicating the const-ness of the data, namely possible opportunity for compiler optimizations and avoiding writing code that accidentally modifies the data.

You should make your pointer const too, const with pointers mean that the memory region they are pointing to is read-only. That tells compiler that it can do or not do some stuff. In your case(not using const with pointer) compiler understands that as a regular pointer accessing a read-only memory part, and raises a warning, the reason for the warning is you can actually change the const variables using pointers, but you should avoid it at all costs because it's UB and your program might do stuff that it shouldn't. But when you declare your pointer as const it tells compiler to treat it like a const and doesn't allow any writing to that memory region. And keep in mind the declaration you use while creating pointer only tells the compiler what type of data you're pointing to. So it doesn't mean your pointer is not changeable. If you want to make your pointer const you declare it like this type* const ptr. I hope i provided enough help.

You have a warning because you store the address of a const array into a pointer that is not const qualified.
Use const uint8_t *p_translations = Translations; to fix the problem.
Note that const uint8_t *p_translations is equivalent to uint8_t const *p_translations, but different from uint8_t * const p_translations, which is a pointer that cannot be changed and points to data that can be changed through the pointer.
Defining p_translations as const uint8_t *p_translations is a way to tell the compiler that you do not intend to modify the data pointed to by p_translations using this pointer. Since Translations is itself defined as const data, it should only be manipulated through a const qualified pointer to detect code that would try and modify it.

Related

Difference in const qualifiers warning

I am getting a bunch of warning messages telling me that
Warning C4090 '=': different 'const' qualifiers cA4 c:\users\kyle\documents\visual studio 2015\projects\ca4\ca4\ca4.c 313
I need to compile my code to have no warnings for this school assignment. Everything works okay but I want to know why this is happening. I am assigning a "string" which is really a char* to a value from a const array to keep track of what city they are currently in.
Like so:
char* currentCityName = cityNames[currentCity];
The warning is occurring on this line later in the program:
currentCityName = cityNames[currentCity];
Declaration of cityNames:
const char* cityNames[kNumberOfCities] = { "Toronto", "Atlanta", "Austin", "Santa Fe", "Denver", "Chicago", "Buffalo" };
Clearly some way that I am setting this variable is incorrect and the compiler isnt liking it.
Im looking for an explanation of why this is happening and what I should be doing instead of this.
You say
I am assigning a "string" which is really a char* to a value from a const array
From that and the error message I presume you mean that you have
const char *cityNames[<some bound>];
which is an array of pointers to strings whose contents are const. If you also have
char *currentCityName;
and you later try to perform
currentCityName = cityNames[currentCity];
then the resulting pointer to modifiable characters in fact points to characters that elsewhere are declared const. This mismatch may lead to the program, without any other warning, attempting to modify const data, with undefined results.
You could clear up that warning by instead declaring currentCityName like so:
const char *currentCityName;
to match the true type of the value you want to assign to it. Alternatively, you can make a modifiable copy of the city name, and point currentCityName at that. Making a copy is necessary if you want to (safely) modify the pointed-to data.
I'm assuming that cityNames is declared as const so when you're assigning the value(does not copy contents) it discards the const qualifier.
In other words you cannot change anything in cityNames because of const but you have another pointer, non-const this time and allow you to change data although it was originally marked as const

Declaring a pointer to const or a const pointer to const as a formal parameter

I was recently making some adjustments to code wherein I had to change a formal parameter in a function. Originally, the parameter was similar to the following (note, the structure was typedef'd earlier):
static MySpecialStructure my_special_structure;
static unsigned char char_being_passed; // Passed to function elsewhere.
static MySpecialStructure * p_my_special_structure; // Passed to function elsewhere.
int myFunction (MySpecialStructure * p_structure, unsigned char useful_char)
{
...
}
The change was made because I could define and initialize my_special_structure before compile time and myFunction never changed the value of it. This led to the following change:
static const MySpecialStructure my_special_structure;
static unsigned char char_being_passed; // Passed to function elsewhere.
static MySpecialStructure * p_my_special_structure; // Passed to function elsewhere.
int myFunction (const MySpecialStructure * p_structure, unsigned char useful_char)
{
...
}
I also noticed that when I ran Lint on my program that there were several Info 818's referencing a number of different functions. The info stated that "Pointer parameter 'x' (line 253) could be declared as pointing to const".
Now, I have two questions in regards to the above. First, in regards to the above code, since neither the pointer nor the variables within MySpecialStructure is changed within the function, is it beneficial to declare the pointer as constant as well? e.g. -
int myFunction (const MySpecialStructure * const p_structure, unsigned char useful_char)
My second question is in regards to the Lint information. Are there any benefits or drawbacks to declaring pointers as a constant formal parameter if the function is not changing its value... even if what you are passing to the function is never declared as a constant? e.g. -
static unsigned char my_char;
static unsigned char * p_my_char;
p_my_char = &my_char;
int myFunction (const unsigned char * p_char)
{
...
}
Thanks for your help!
Edited for clarification -
What are the advantages of declaring a pointer to const or a const pointer to const- as a formal parameter? I know that I can do it, but why would I want to... particularly in the case where the pointer being passed and the data it is pointing to are not declared constant?
What are the advantages of declaring a pointer as a const - as a formal parameter? I know that I can do it, but why would I want to... particularly in the case where the pointer being passed and the data it is pointing to are not declared constant?
I assumed you meant a pointer to const.
By have a pointer to const as a parameter, the advantage is you document the API by telling the programmer your function does not modify the object pointed by the pointer.
For example look at memcpy prototype:
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
It tells the programmer the object pointed to by s2 will not be modified through memcpy call.
It also provides compiler enforced documentation as the implementation will issue a diagnostic if you modify a pointee from a pointer to const.
const also allows to indicate users of your function that you won't modify this parameter behind their back
If you declare a formal parameter as const, the compiler can check that your code does not attempt to modify that parameter, yielding better software quality.
Const correctness is a wonderful thing. For one, it lets the compiler help keep you from making mistakes. An obvious simple case is assigning when you meant to compare. In that instance, if the pointer is const, the compiler will give you an error. Google 'const correctness' and you'll find many resources on the benefits of it.
For your first question, if you are damn sure of not modifying either the pointer or the variable it points to, you can by all means go ahead and make both of them constant!
Now, for your Qn as to why declare a formal pointer parameter as const even though the passed pointer is not constant, A typical use case is library function printf(). printf is supposed to accept const char * but the compiler doesn't complain even if you pass a char* to it. In such a case, it makes sense that printf() doesn't not build upon the user's mistake and alter user's data inadvertantly! Its like printf() clearly telling- Whether you pass a const char * or char*, dont worry, I still wont modify your data!
For your second question, const pointers find excellent application in the embedded world where we generally write to a memory address directly. Here is the detailed explanation
Well, what are the advantages of declaring anything as a const while you have the option to not to do so? After all, if you don't touch it, it doesn't matter if it's const or not. This provides some safety checks that the compiler can do for you, and it gives some information of the function interface. For example, you can safely pass a string literal to a function that expects a const char *, but you need to be careful if the parameter is declared as just a char *.

Using 'const' in C, what porting trouble might that cause?

I would like to use 'const' in C interface functions to note that certain char * arguments are not modified by the function.
What trouble might this cause in porting this code to various platforms? Is support of 'const' in C code pretty standard? When did this become officially in C standard?
I can't imagine const not being supported by any compilers, so porting should be a non-issue. If you were to find such a beast, you could just put
#define const
Somewhere in a common header file to make all of the const keywords vanish. The runtime semantics of your program won't change at all (since your compiler didn't support the keyword anyway).
It's pretty standard. I think it came with C89.
It works in MSVC, which is the biggest obstacle to overcome with C portability.
Pretty much any modern compiler should handle const correctly. The popular choices definitely will support it. It's been in the standard since C89, IIRC.
As the other answers say, const is standard. The only issues you will run into is using it incorrectly. Pointer const can be tricky. Make sure you are consting the correct thing:
See the wikipedia article on const-correctness:
For pointer and reference types, the syntax is slightly more subtle. A pointer object can be declared as a const pointer or a pointer to a const object (or both). A const pointer cannot be reassigned to point to a different object from the one it is initially assigned, but it can be used to modify the object that it points to (called the "pointee"). Reference variables are thus an alternate syntax for const pointers. A pointer to a const object, on the other hand, can be reassigned to point to another object of the same type or of a convertible type, but it cannot be used to modify any object. A const pointer to a const object can also be declared and can neither be used to modify the pointee nor be reassigned to point to another object. The following code illustrates these subtleties:
void Foo( int * ptr,
int const * ptrToConst,
int * const constPtr,
int const * const constPtrToConst )
{
*ptr = 0; // OK: modifies the pointee
ptr = 0; // OK: modifies the pointer
*ptrToConst = 0; // Error! Cannot modify the pointee
ptrToConst = 0; // OK: modifies the pointer
*constPtr = 0; // OK: modifies the pointee
constPtr = 0; // Error! Cannot modify the pointer
*constPtrToConst = 0; // Error! Cannot modify the pointee
constPtrToConst = 0; // Error! Cannot modify the pointer
}
The implementation may place a const object that is not volatile in a read-only region of
storage.
(WG14/N1336 sec 6.7.3, footnote 117)
Some (cross) compilers are quirky about this; they treat non-const variables as those to be placed in RAM, and const variables as those to be placed in a real read-only memory device (EEPROM or Flash)
You will run into trouble in such cases, as type* and const type* will refer to different memory regions.
Consider:
void foo(const char* arg); /* intent is not to modify anything through arg, but arg refers to a memory location in ROM */
/* ... */
char bar[] = "abc";
const char baz[] = "def";
foo(bar); /* behavior is undefined! */
foo(baz); /* should be ok */
I'm not aware of a PC-based compiler that does this, but this seems common in microcontroller cross-compilers. I recently faced this issue when porting FatFs on ImageCraft's compiler for PSoC1 and had to #define away consts as Carl suggested.

Protecting a variable's data by const qualifier is of no use !!!?

#include<stdio.h>
int main()
{
const char const_cvar1 = 'X';
const char* ptr_to_const_char = &const_cvar1;
// Simply a typecast of "ptr to a const char" to a "ptr to char" disables security !!!
*(char *)ptr_to_const_char = 'S';
printf("\n Value of const char changed from X to : %c \n", const_cvar1);
return 0;
}
Outputs:-
Value of const char changed from X to : S
I wonder if we can never rely on the const qualifier in C ? is it the fact ?
It's C. You can do a lot of things that are stupid, causes undefined behavior, is compiler/platform dependent etc.
casting away the const modifier is one of them. You are able to do so, but you get little guarantees as to what the result will be.
You could even cast it to just about anything, at your own risk.
*(float *)ptr_to_const_char = 2.18;
However, const is not useless.
It provides documentation, a programmer reading code where a pointer is declared const
can assume he can/should not modify what the pointer points to
It can enable the compiler to do further optimizations
Just keep in mind that C does not protect you against yourself or others, it also gives you the ability to not honor the type system.
It is indeed a fact. In fact, the compiler turns off const optimizations (few as there are) the moment it sees you casting away const, treating it like any other variable.
const does not protect a variable. In fact, const does not even mean the variable will never change.
What it means is that you, the programmer, declare that you won't be writing to that part of memory. A memory-mapped input-only pin on a microchip might be represented by a variable qualified const, but it can still change if the voltage on the pin changes. (In fact, it will most likely be declared const volatile, which blew my mind the first time I saw it.) But the const means that you aren't allowed to write to it.
So if you break that promise — that you have no intention of writing to it — well then yes, of course that'll mess things up.
You are programming in C and its always like that. While playing with pointers you need to be careful always.
I believe you want
char * const ptr_to_const_char
which is a constant pointer, not a pointer to a constant.
But that may only work in C++.
Modifying the content of a variable defined as const using such means is clearly UB. Avoid that.

Assignment of const to non-const in C

In the following:
struct adt { void * A; };
int new_adt(const void * const A)
{
struct adt * r = malloc(sizeof(struct adt));
r->A = A;
}
I get:
warning: assignment discards qualifiers from pointer target type
I know I can use
memcpy(&(r->A), &A, sizeof(void *));
to workaround it, but I must ask: Is there any alternative?
By using const void * const I pretend to say that no changes will be made to the input. Also, now that I think of it, const void * would suffice, wouldn't it? (Since I can't change the pointer so that it affects the caller)
Thanks for taking the time to read.
By using const void * const I pretend to say that no changes will be made to the input.
You have not pretended to say that no changes will be made to the input, you have explicitly told the compiler that you have guaranteed that no changes will be made to the input.
You MUST NOT do what you are doing.
If you never want to modify A through its pointer from adt, then you should make that pointer const as well, i.e.:
struct adt {
const void * A;
};
That will make the error go away.
If you do want to modify A through adt, then new_adt should take a non-const pointer.
Edit: A more general note, which may help:
The const keyword, in general, applies to the type immediately to its left. However, if there is no type to its left, it will apply to the type to the right. So:
const int * A is the same as int const * A (the const applies to the int, not to the pointer), but in int * const A, the const applies to the pointer, not to the int.
There is a more comprehensive explanation on Wikipedia: const-correctness. The 'Pointers and References' section of that page includes an example which covers the various combinations of const and non-const that you can apply to a pointer.
You can cast the const away:
r->A = (void *) A;
The issue, though, is less about how to avoid the warning and more about what you're trying to do. The compiler is warning you because you're telling the compiler that "A" is not writable, but you're trying to store it in a location that defines it as writable. That's generally not OK, semantically.
Imagine that "A" points to a location in your binaries data section; trying to write to it, after casting the const away, will most probably cause a segfault.
So, really, think about what you're trying to do more closely before trying to work around the compiler warning. They're warnings for a good reason.

Resources