Sign in to follow this  
Beyond_Repair

C99 const keyword - indirection

Recommended Posts

Beyond_Repair    116
I did quick googling on this but the explanations found are too simple (eg. "a const object is unmodifiable"). Ok, say I have a struct and two functions like this:
typedef struct
{
   otherstruct_t* ptr;
} somestruct_t;

void do_something(const somestruct_t* s)
{
   s->ptr->... = ...;
}

somestruct_t* return_something(const somestruct_t* array, int index)
{
   return &array[index];
}





What gives in these cases? I thought that const implies I can't write directly in the type qualified (not counting indirection) in the scope of the function, but I get GCC warning me about constness getting discarded by functions similar to the two above. I've probably misunderstood the keyword. I'm not completely inexperienced with C but I didn't bother with this qualifier before, this time however I'd like to help the compiler's optimizer unless it gives me too much of a headache.

Share this post


Link to post
Share on other sites
phresnel    953
Above code is not compilable, and I can't really imagine what you mean exactly; please post real code, not pseudo code.

Wikipedia has a nice page about const correctness, as well as the C++ FAQ Lite.

Share this post


Link to post
Share on other sites
Beyond_Repair    116
It's pseudo in that I have not written out a definition for otherstruct_t, which is another structure. Nor have I written out the assignment. But it shouldn't matter for the understanding of the problem.

Don't know if reading about C++ constness will help, considering there are differences in how C deals with constants compared to C++.

Will check out wiki, thanks.

Share this post


Link to post
Share on other sites
phresnel    953
Just checked some code that I think you really mean (C++ though):

struct somestruct_t
{
somestruct_t* ptr;
};

somestruct_t* return_something(const somestruct_t* array, int index)
{
return array + index;
}


This won't compile, because gcc spits an error for trying to implicitly discard the const qualifier.

Share this post


Link to post
Share on other sites
phresnel    953
Quote:
Original post by Beyond_Repair
It's pseudo in that I have not written out a definition for otherstruct_t, which is another structure. Nor have I written out the assignment. But it shouldn't matter for the understanding of the problem.


It matters, because

somestruct_t* return_something(const somestruct_t* array, int index)
{
return array[index];
}


is invalid code. Function return_something accepts a const somestruct_t* as a parameter, and by signature returns a somestruct_t*; but actually your return statement tries to return a somestruct_t, i.e. invalid code.

Share this post


Link to post
Share on other sites
Beyond_Repair    116
It does compile, but I'm allowing warnings (not like it matters as I always fix them and never lead them to be, like in this case). :P

Edit: Uggh, I forgot the address of operator in my example. Sorry-.

Share this post


Link to post
Share on other sites
phresnel    953
Quote:
Original post by Beyond_Repair
It does compile, but I'm allowing warnings (not like it matters as I always fix them and never lead them to be, like in this case). :P


Okay, now I am really in C. Let me dig ...

Share this post


Link to post
Share on other sites
Beyond_Repair    116
Aha, Wikipedia probably answers my question:

Quote:
Another loophole applies both to C and C++. Specifically, the languages dictate that member pointers and references are "shallow" with respect to the const-ness of their owners — that is, a containing object that is const has all const members except that member pointees (and referees) are still mutable.


Weird GCC is giving me a warning then, but I guess it's because it's not "recommended".

Share this post


Link to post
Share on other sites
CmpDev    100
Quote:
Original post by Beyond_Repair
Aha, Wikipedia probably answers my question:

Quote:
Another loophole applies both to C and C++. Specifically, the languages dictate that member pointers and references are "shallow" with respect to the const-ness of their owners — that is, a containing object that is const has all const members except that member pointees (and referees) are still mutable.


Weird GCC is giving me a warning then, but I guess it's because it's not "recommended".

What is the warning and what gcc version?
The following produces no warnings using:
$ gcc -ansi -pedantic -Wall -Wextra const_aware.c

#include <malloc.h>
typedef struct
{
int i;
} otherstruct_t;

typedef struct
{
otherstruct_t* ptr;
} somestruct_t;

void do_something(const somestruct_t* s)
{
s->ptr->i = 2;
}

const somestruct_t* return_something(const somestruct_t* array, int index)
{
return array + index;
}

int main()
{
somestruct_t* s = malloc(sizeof(somestruct_t));
s->ptr = malloc(sizeof(otherstruct_t));
s->ptr->i = 1;
do_something(s);
free(s->ptr);
free(s);

return 0;
}


Share this post


Link to post
Share on other sites
phresnel    953
Quote:
Original post by Beyond_Repair
Edit: Uggh, I forgot the address of operator in my example. Sorry-.


Better do actual copy+paste next time ;)


Hmm, as far as I can tell, the C99 standard (http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf) allows such conversion:

Quote:
6.3.2.3 Pointers
7. A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned57) for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. [...]


Though I am unsure if it allows such conversion to be implicit as in your example. digs further

edit: Hmm, unsure whether that was the right item. Another item says

Quote:
6.3.2.3 Pointers
2. For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.


So you would be allowed to convert T* to const T*, but that item does not, imho, allow const T* to T*. The wording is a bit unsharp for the german in me, but I think in those two quotes, "object" means "object type", "incomplete" means "incomplete type", even if only "object"/"incomplete" is written down. Now I am confused whether item 7 or item 2 is relevant.


Quote:
Original post by Beyond_Repair
Aha, Wikipedia probably answers my question:
Quote:
Another loophole applies both to C and C++. Specifically, the languages dictate that member pointers and references are "shallow" with respect to the const-ness of their owners — that is, a containing object that is const has all const members except that member pointees (and referees) are still mutable.

Unfortunately not, array elements are not member pointers or references.

Share this post


Link to post
Share on other sites
phresnel    953
Quote:
6.5.4 Cast Operators
3. Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.


Quote:
6.5.16.1 Simple Assignment
1.
[...]
— both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
[...]


If it is allowed to see the returned value as the left operand, then implicit conversion of const T* to T* is not allowed.

skim skim skim ...

Share this post


Link to post
Share on other sites
Beyond_Repair    116
Quote:
What is the warning and what gcc version?
The following produces no warnings using:
$ gcc -ansi -pedantic -Wall -Wextra const_aware.c


AFAIK most recent stable. Didn't have extra warning options on, enabled them now, but with -stc=c99.

Anyway, the new warning message imply I messed something else up, and it's not the fault of the compiler.

Quote:
Unfortunately not, array elements are not member pointers or references.


Ok, well it explained the first question. ;) You already more or less explained the second one.

Anyway, I've decided to stay away from const in the cases where anything might be modified by the use of a pointer to const object (even if indirectly through a member pointer of that object where the language allows this). Thanks for the help clearing this up!

And the case of the array was just bad programming at my part...

Share this post


Link to post
Share on other sites
phresnel    953
Meh, even with "-Wall -Wextra -pedantic -std=c99", I fail to get an error. Maybe gcc is wrong, or the standard really allows implicit conversions like "const T* -> void*- > T*".

Share this post


Link to post
Share on other sites
phresnel    953
Quote:
Original post by CmpDev
The following produces no warnings using:
$ gcc -ansi -pedantic -Wall -Wextra const_aware.c
*** Source Snippet Removed ***


Minor nitpick: -ansi is the same as -std=c89, even in the current GCC 4.4.

Share this post


Link to post
Share on other sites
phresnel    953
Quote:
Original post by Beyond_Repair
Ok, well it explained the first question.

Ah okay, sorry :D



Quote:
Thanks for the help clearing this up!

No problem :)

Share this post


Link to post
Share on other sites
CmpDev    100
Quote:
Original post by phresnel
Meh, even with "-Wall -Wextra -pedantic -std=c99", I fail to get an error. Maybe gcc is wrong, or the standard really allows implicit conversions like "const T* -> void*- > T*".


There are not type conversions happening here. The const which makes pointer to constant structure does not make the data pointed to by the pointer inside the structure constant.

Quote:
Minor nitpick: -ansi is the same as -std=c89, even in the current GCC 4.4.

Yes I know but c99 is not fully supported and ansi with pedantic is required for some extra checking.

[Edited by - CmpDev on May 4, 2009 11:26:25 AM]

Share this post


Link to post
Share on other sites
phresnel    953
Quote:
Original post by CmpDev
Quote:
Original post by phresnel
Meh, even with "-Wall -Wextra -pedantic -std=c99", I fail to get an error. Maybe gcc is wrong, or the standard really allows implicit conversions like "const T* -> void*- > T*".


There are not type conversions happening here. The const which makes pointer to constant structure does not make the data pointed to by the pointer inside the structure constant.


Heh, glad to see that yet another poster mixed up the two OP cases (like myself), as I was really digging on the second example (somestruct_t* return_something(const somestruct_t* array, int index)).

Share this post


Link to post
Share on other sites
LessBread    1415
Quote:
Original post by Beyond_Repair
What gives in these cases? I thought that const implies I can't write directly in the type qualified


It looks to me that the type qualified is a pointer. So as long as you're not modifying the pointer, you're o.k. If the struct member modified in do_something is declared const, then you'd have a problem. And if you really needed to modify that member, typecasting that member as a non const might mollify your compiler.

Share this post


Link to post
Share on other sites
CmpDev    100
Quote:
Original post by phresnel
Quote:
Original post by CmpDev
Quote:
Original post by phresnel
Meh, even with "-Wall -Wextra -pedantic -std=c99", I fail to get an error. Maybe gcc is wrong, or the standard really allows implicit conversions like "const T* -> void*- > T*".


There are not type conversions happening here. The const which makes pointer to constant structure does not make the data pointed to by the pointer inside the structure constant.


Heh, glad to see that yet another poster mixed up the two OP cases (like myself), as I was really digging on the second example (somestruct_t* return_something(const somestruct_t* array, int index)).

Hmm I did miss that was your point, but in the snippet I posted I corrected that. gcc does give a warning about it and changing the data pointed to by the pointer is undefined, this is why gcc is warning.
The one quote which you did miss is the following which allows it, but it is an accident waiting to happen:
Quote:

6.5.16.1 Simple assignment
Constraints
1 One of the following shall hold:93)
— the left operand has qualified or unqualified arithmetic type and the right has arithmetic type;
— the left operand has a qualified or unqualified version of a structure or union type compatible with the type of the right;
— both operands are pointers to qualified or unqualified versions of compatible types,
and the type pointed to by the left has all the qualifiers of the type pointed to by the
right;
— one operand is a pointer to an object or incomplete type and the other is a pointer to a
qualified or unqualified version of void, and the type pointed to by the left has all
the qualifiers of the type pointed to by the right; or
— the left operand is a pointer and the right is a null pointer constant.
— the left operand has type _Bool and the right is a pointer.

Share this post


Link to post
Share on other sites
phresnel    953
Quote:
Original post by CmpDev
Quote:
Original post by phresnel
Quote:
Original post by CmpDev
Quote:
Original post by phresnel
Meh, even with "-Wall -Wextra -pedantic -std=c99", I fail to get an error. Maybe gcc is wrong, or the standard really allows implicit conversions like "const T* -> void*- > T*".


There are not type conversions happening here. The const which makes pointer to constant structure does not make the data pointed to by the pointer inside the structure constant.


Heh, glad to see that yet another poster mixed up the two OP cases (like myself), as I was really digging on the second example (somestruct_t* return_something(const somestruct_t* array, int index)).

Hmm I did miss that was your point, but in the snippet I posted I corrected that. gcc does give a warning about it and changing the data pointed to by the pointer is undefined, this is why gcc is warning.
The one quote which you did miss is the following which allows it, but it is an accident waiting to happen:
Quote:

6.5.16.1 Simple assignment
Constraints
1 One of the following shall hold:93)
— the left operand has qualified or unqualified arithmetic type and the right has arithmetic type;
— the left operand has a qualified or unqualified version of a structure or union type compatible with the type of the right;
— both operands are pointers to qualified or unqualified versions of compatible types,
and the type pointed to by the left has all the qualifiers of the type pointed to by the
right;
— one operand is a pointer to an object or incomplete type and the other is a pointer to a
qualified or unqualified version of void, and the type pointed to by the left has all
the qualifiers of the type pointed to by the right; or
— the left operand is a pointer and the right is a null pointer constant.
— the left operand has type _Bool and the right is a pointer.


Interesting. Thanks.

I have mailed the GCC-help mailing list. Andrew Haley of redhat answered the following:

Quote:
Andrew Haley
One thing that you may have missed is that the standard doesn't say
anything about warnings or errors. When it says a translator must
issue a diagnostic, then that can be either a warning or an error.


The whole thread can be found here: http://gcc.gnu.org/ml/gcc-help/2009-05/msg00022.html.

I am going to ref your post.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this