I have a dictionary that goes like this:
typedef struct dictNode { int key; char *value; struct dictNode *next; } Dict;
And a get() function that goes like this:
char *get(const Dict *dict, int key) { if(!dict) return NULL; Dict *currPtr = dict; while(currPtr) { if(currPtr->key == key) { return currPtr->value; } currPtr = currPtr->next; } }
Compiling this code produces the following error:
dict.c:85: warning: initialization discards qualifiers from pointer target type
This warning refers to the line:
Dict *currPtr = dict;
If I add a ‘const’ before that line like this:
const Dict *currPtr = dict;
The warning goes away…
1) First thing I don’t understand is this: I added ‘const’ to the dict argument in get() so that the compiler warns me if I try to change the address dict is pointing to, otherwise I won’t be able to access the dictionary in the main program cause I lost the address I was pointing too. Now, I’m creating a new pointer, currPtr, that points to the same place as dict. This way, I use this pointer instead, to traverse the dictionary and keep dict pointer intact. Why do I also need to have const for currPtr?
2) Second thing I don’t understand is this: The line currPtr = currPtr->next; is changing the currPtr pointer, so, why doesn’t the compile warn me about that if I added a const to currPtr?
Then I have a del() function that goes like this:
Dict *del(const Dict *dict, int key) { if(!dict) return NULL; Dict *currPtr = dict; Dict *prevPtr = dict; while(currPtr) { if(currPtr->key == key) { prevPtr->next = currPtr->next; free(currPtr); } prevPtr = currPtr; currPtr = currPtr->next; } return dict; }
Please note that I’m aware that this delete function code is not complete, it does not correctly work if I want to delete the first element for instance. It doesn’t matter, I’ll finish later, it suffices to demonstrate my problem…
3) So, in the get() function I needed to add const to currPtr but in this del() function, I don’t need to add const to currPtr nor prevPtr? In both functions I am changing the currPtr pointer and in the case of the del() function I’m changing the prevPtr pointer too. Why does the get() function require me to add a const before currPtr and the del() function does not require me to add a const before currPtr and prevPtr?
I can basically resume this whole post to: When and where exactly in my get() and del() functions should I use const and why, and, when and where I shouldn’t?
Without a pointer, you would have
which is a constant Dict. Now, if you make it a pointer you have
which is a pointer to a constant Dict. That does not mean that the pointer is constant. But it does mean that the Dict pointed to is treated as constant
But the pointer is not
You would get an error for the second case if you make the pointer constant. Keeping the pointed dict constant too, this would look like this
Now you can’t set currPtr to point to something different, because the pointer is now constant, not just what the pointer points to is treated so. Some people like the look if the const is always right to the stuff that it makes const. This would look like this
which is the same as the previous snippet. If you then read it from right to left, it tells you what it is ‘const pointer to a const Dict’. If you have a type, it doesn’t matter how you order the specifiers
Both are constant integers. That is why we could put const right of the Dict type specifier.
Now, if you have a pointer, you can always pretend you point to a constant object, even though the object wasn’t declared const. But you can’t pretend to work with a non-const object if what you point to is a const object:
Note that if you make the pointer itself const, the rules are different to that. They are analogous to const applied to other types:
After copying the value into a new variable (whether pointer or not), the new variable is not connected in any way to the other variable, because the value read has no associativity to how the value was created in the first place. The const of the other variable doesn’t matter.