Vaguely when I should and should not use "const" in C

I have a dictionary that looks something like this:

typedef struct dictNode { int key; char *value; struct dictNode *next; } Dict; 

And the get () function, which looks something 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; } } 

When compiling this code, the following error occurs:
dict.c: 85: warning: initialization discards qualifiers from target pointer type

This warning applies to the line:

 Dict *currPtr = dict; 

If I add "const" before this line, for example:

 const Dict *currPtr = dict; 

The warning goes away ...

1) . The first thing I don’t understand is: I added "const" to the dict argument in get () , so the compiler warns me if I try to change the address pointed to by dict, otherwise I won’t be able to access the dictionary in the main program because I lost the address I pointed to. Now I am creating a new currPtr pointer that points to the same place as the dict . So I use this pointer instead to traverse the dictionary and keep the dict pointer intact. Why do I also need to have const for currPtr ?

2) Secondly, I do not understand what it is: string currPtr = currPtr-> next; the currPtr pointer changes , so why did the compiler not warn me about this if I added const to currPtr ?

Then I have a del () function that looks something 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 know that this code of the delete function is not complete, it does not work correctly if I want, for example, to delete the first element. It doesn’t matter, I’ll finish later, just demonstrate my problem ...

3) So, in the get () function, I needed to add const to currPtr , but in this del () function , I did not need to add const to currPtr or prevPtr ? In both functions, I change the currPtr pointer, and in the case of the del () function, I change the prevPtr pointer. Why does the get () function require me to add const to currPtr , and the del () function does not require me to add const to currPtr and prevPtr ?

I can basically resume this whole post: When and where exactly in my get () and del () functions should I use const and why, and when and where should I not?

+6
c pointers const
source share
11 answers

Without a pointer you will have

 const Dict currPtr 

which is a constant dict. Now, if you make a pointer, you have

 const Dict *currPtr 

which is a pointer to a constant dict. This does not mean that the pointer is constant. But this means that the pointer denoted by Dick is considered constant

 currPtr->key = 10; // error, dict is treated as constant. 

But the pointer is not

 currPtr = otherPtr; // possible: the pointer is not constant 

You will get an error for the second case if the pointer is constant. Keeping the dict pointer constant, it will look like this:

 const Dict * const currPtr = init; 

Now you cannot establish that currPtr points to something else, because the pointer is now constant, and not just what the pointer points to. Some people like to look if const is always right on the things that const creates it. It will look like

 Dict const * const currPtr = init; 

which matches the previous snippet. If you then read it from right to left, it tells you that it is a "const pointer to const dict." If you have a type, it doesn't matter how you order the specifiers

 int const a = 10; const int b = 10; 

Both are constant integers. This is why we could put const directly from a Dict type specifier.

Now, if you have a pointer, you can always pretend to point to a constant object, although the object was not declared as const. But you cannot pretend to be working with a non-constant object if you point to a const object:

 int const *p = NULL; // doesn't work without a cast. Will at least provoke a warning int *pn = p; int *p = NULL; // always works: pretending to point to something const doesn't harm. int const *pc = p; 

Note that if you make the const pointer itself, the rules will be different from this. They are similar to the constant applied to other types:

 int const i = 0; int j = i; // works. we only read the value of i. its const doesn't matter. int * const p = NULL; int * q = p; // works: we only read the value of p (a null pointer). 

After copying the value into a new variable (whether it be a pointer or not), the new variable is not connected in any way with another variable, because the value read has no associativity with how the value was created in the first place. The constant of another variable does not matter.

+13
source share

"I added const to the dict argument in get () so that the compiler warns me if I try to change the dict address by pointing to"

In this case, you meant Dict *const dict , not const Dict *dict . You declared a struct as const, not a pointer to it.

"Why do I also need to have const for currPtr"

Because otherwise you could use currPtr to change the very structure of the dictionary, which should be const in this function.

"The string currPtr = currPtr-> next; changes the currPtr pointer, so why the compiler did not warn me about this if I added a constant to currPtr"

For the same reason: it is not a mistake if the constant is in the place where you put it. It would be if the constant was elsewhere for currPtr.

otherwise, I will not be able to access the dictionary in the main program, because I lost the address that I also pointed to

No - when the main program calls are received, it passes the value of the pointer to the procedure, and this becomes the dict value in the get function. But get has its own pointer variable, separate from the one in the main program. Even if they just happen to both, called "dict", changing one pointer will not change the other.

Both variables point to the same structure, therefore, if you use a variable to change any of the fields in the structure, then, of course, the results will affect both bits of the code. Usually, a function called "get" will work read-only, so you can make the const Dict * parameter. But I think the reason that this is correct is a little different from what you think. This is to protect the contents of the dictionary from being changed, and not to protect your record of its address.

However, const-safety is a little inconvenient for linked lists like this. Even if you have a const dict, its “next” field still does not point to const. Therefore, although you cannot change the first node, the const system will not protect you from changing other nodes in the list. C ++ addresses this with function overloading, so you can have a getnext function that returns const output if the input is const, but non-constant output if the input is not const. You can achieve a similar thing in C with "getnext" and "getnext_c", the latter having a const parameter and returns. But most people don't care, including standard library functions like strchr, which in C accept const char * but return not const const char *. Therefore strchr can be accidentally used in C to convert a const string to a non-constant string, without any warnings. Or intentionally, in which case it is a bit like money laundering through a “legitimate” business; -)

does the del () function not require me to add a constant before currPtr and prevPtr?

I confess I'm at a standstill. I would expect the same warning as in "get" - are you sure that it is not there? But the to del parameter should not be const any, because you are potentially changing the fields.

+7
source share

const dict * dict means it's a pointer to constant dict values

you can change the pointer, but not the values;

+3
source share

I think your main confusion comes from a misunderstanding of the meaning

 const char* p; 

Defines a pointer to a constant character. The address in p can change, but the pointer of the value cannot. The compiler forbids changing * p, not p.

+2
source share

This may help read the pointer declaration back:

 const Dict *dict 

means

'dict' is a pointer to a constant dict.

You can move the address it points to, but the structure it points to is treated as a constant.

+2
source share

A const is a kind of programmer documentation. It tells itself that you will not try to change anything that was passed to const .

Your get function should accept const Dict * , while your del function should not. currPtr is a walker - it does not change the logical state of the Dict object in your get , so it must also be const . However, in your del it changes the structure, therefore, there is no const .

+1
source share

Declaration 1 and 2: const in the prototype refers to Dict , which means

 char *get(const Dict *dict, int key) 

reads as ... a function with an argument (not const) of a type pointer to a constant Dict ..., which means that you can change the Dict , but not for example. dict->next .

If you want the effect that you probably intended, declare it as follows

 char *get(Dict * const dict, int key) 

Rule: const is applied to the word to the left of it, except when it is first in the type name, and then it is applied to the next word.

In addition, changing the Dict does not change the pointer in the main program, since the pointer is passed by value.

When you assign currDict pointer currDict compiler checks for type compatibility, which in this case means that you can only add const top-level qualifiers, and not delete like you.

Where you should use const:

In the get() function, you should use const in all Dict (like you), since you probably don't want to change the dictionary there.

In the del() function, I would not use any const s, since the data there must be changed. A good signature for him would be:

 char *get(Dict ** dict, int key) 

so that you can change the pointer of the calling function to remove the first element.

+1
source share

Keep in mind that const can appear on one or both sides * in a pointer declaration and is semantically significant ...

 const char * foo = bar; 

does not match

 char * const foo = bar; 

The first one says that the content is const , and the second one says that the pointer is const .

For the maximum constant:

 const char * const foo = bar; 
+1
source share

The compiler keeps track of what is and is not a constant. Getting them right can be difficult at first.

First of all, you go through const Dict *dict , which means that you promise that you won't change anything with dict . Then you assign it to Dict * currptr . Since currptr not const , you can change it to *currptr . The compiler should at some point warn you that you are trying to change something, even if it is const, and the right place is when you assign the const pointer to a non-const pointer.

For your second point you are missing exactly what const . If you have a const Dict * currptr , you describe currptr as a pointer to a const Dict , which means you can change currptr , but not *currptr . You can make currptr const using Dict * const currptr or make it a constant pointer to the value of const using const Dick * const currptr .

I really do not understand your third point. You should get a compiler warning from del() as you placed it, since you are assigning a pointer to a const value to indicate the value. You must have a non-const prevptr as you use it to change the value. The signature of the function should be Dict *del(Dict *dict, int key) , since you promise not to change anything, and the dict points to this and does it anyway.

+1
source share

I get most of the things you say that I don’t get now, this is ...

Consider the following list () function:

 void list(Dict *dict) { if(!dict) return; while(dict) { printf("KEY: %d\n", dict->key); printf("VALUE: %s\n", dict->value); dict = dict->next; } } 

(easier to use this instead of get () method)

In my main I have two calls to this function, like:

 list(dict); list(dict); 

Of course, dict has some entries entered ...

As far as I understand, only the first list () should output something, because the dict pointer changes in the list () and will eventually point to NULL (otherwise we would be in an infinite loop in the list () call). It happens that calling the second list () also outputs the same dictionary when I think it should not output anything, because the dict should be NULL, no?

0
source share

Dummy Guide for const:

Use it when you can.

0
source share

All Articles