By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,838 Members | 1,623 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,838 IT Pros & Developers. It's quick & easy.

Implicit addition of const qualifiers

P: n/a
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

A lot of functions use const pointer arguments. If I have a non-const
pointer, it is transparently made const when I pass it to the
function, e.g. char * -> const char *. However, this does not appear
to work when I add another level of indirection:

void test1 (char **value)
{}

void test2 (const char **value)
{}

void test3 (const char * const *value)
{}

void test4 (char * const *value)
{}

int
main (void)
{
char **v;

test1(v);
test2(v);
test3(v);
test4(v);

return 0;
}

$ gcc -Wall -pedantic -o test1 test.c
test.c: In function ‘main’:
test.c:19: warning: passing argument 1 of ‘test2’ from
incompatible
pointer type
test.c:20: warning: passing argument 1 of ‘test3’ from
incompatible pointer type

Why is this sort of conversion requiring an explicit cast? Adding
(const char * const *) casts all over the place doesn't really improve
code readability, even if it does give me const correctness!
Thanks,
Roger

- --
Roger Leigh
Printing on GNU/Linux? http://gimp-print.sourceforge.net/
Debian GNU/Linux http://www.debian.org/
GPG Public Key: 0x25BFB848. Please sign and encrypt your mail.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Processed by Mailcrypt 3.5.8 <http://mailcrypt.sourceforge.net/>

iD8DBQFCzGCVVcFcaSW/uEgRArUGAKDoU72gbXjcIj+vTl3p6G1HD0B5lACgvwxV
ih47/4KIkBWTCY8lVM5nndk=
=v5Ep
-----END PGP SIGNATURE-----
Nov 15 '05 #1
Share this Question
Share on Google+
8 Replies


P: n/a
In article <87************@hardknott.home.whinlatter.ukfsn.or g>,
Roger Leigh <${*******@invalid.whinlatter.ukfsn.org.invalid> wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

A lot of functions use const pointer arguments. If I have a non-const
pointer, it is transparently made const when I pass it to the
function, e.g. char * -> const char *. However, this does not appear
to work when I add another level of indirection:

void test1 (char **value)
{}

void test2 (const char **value)
{}

void test3 (const char * const *value)
{}

void test4 (char * const *value)
{}

int
main (void)
{
char **v;

test1(v);
test2(v);
test3(v);
test4(v);

return 0;
}

$ gcc -Wall -pedantic -o test1 test.c
test.c: In function main:
test.c:19: warning: passing argument 1 of test2 from
incompatible
pointer type
test.c:20: warning: passing argument 1 of test3 from
incompatible pointer type

Why is this sort of conversion requiring an explicit cast? Adding
(const char * const *) casts all over the place doesn't really improve
code readability, even if it does give me const correctness!


Lets say you have a char* (points to chars that can be modified), and a
function that takes a const char* parameter (points to chars that cannot
be modified). The language allows an automatic cast, because no harm
will be done: All that happens is that the function cannot easily modify
the chars, even though it would be ok (from the callers point of view)
to modify them. It is also obvious that in the opposite situation, an
automatic cast shouldn't be allowed by the C language: Such a cast would
mean that the function could modify chars that are actually const; a
very dangerous thing to do.

Now what you have is a char** (points to pointers to modifiable chars),
and the function takes an argument const char** (points to pointers to
unmodifiable chars). Now lets say I have an array somewhere

static const array [10];

If I have a const char**, lets say const char** p, then an assignment

*p = &array [0];

would be perfectly legal. However, if I have another pointer char** q
(note: Not const char**) then if the language allowed me to write

*q = &array [0];

that would be a very dangerous thing: I cannot use *p to modify a char,
but I can use *q, for example (*q) [2] = '\0'; . That is why the
compiler doesn't allow the assignment *q = &array [0] - allowing this
assignment would mean that I could modify a const char without using a
cast. That cannot be allowed.

Now to your problem: You have a pointer char** q. Your function takes an
argument const char** p. If you could pass the char** q to the function
without a cast, then the assignment

*p = &array [0];

could be performed within the function, which would do effectively the
same thing as writing

*q = &array [0];

outside the function, which as we have seen must not be allowed. The
automatic conversion from char* to const char* is safe - it doesn't let
you do anything that you couldn't do without the conversion. The
automatic conversion from char** to const char** is NOT safe: Such a
conversion would allow you to store a const char* where a (non const)
char* is expected, and consequently to modify a const char which must
not be allowed.

If you think carefully about it, you will find that the following
conversions are safe resp. unsafe (and therefore are allowed /
disallowed to happen automatically):

char* -> const char* is safe
const char* -> char* is unsafe

char** -> const char** is unsafe
const char** -> char** is safe

char*** -> const char*** is safe
const char*** -> char*** is unsafe

and so on.
Nov 15 '05 #2

P: n/a
Me
> A lot of functions use const pointer arguments. If I have a non-const
pointer, it is transparently made const when I pass it to the
function, e.g. char * -> const char *. However, this does not appear
to work when I add another level of indirection: *snip* test.c:19: warning: passing argument 1 of 'test2' from
incompatible
pointer type
test.c:20: warning: passing argument 1 of 'test3' from
incompatible pointer type

Why is this sort of conversion requiring an explicit cast?
This is taken directly from the C++ standard:

[Note: if a program could assign a pointer of type T** to a pointer of
type const T** (that is, if line //1 below was allowed), a program
could inadvertently modify a const object (as it is done on line //2).
For example,
int main () {
const char c = 'c';
char * pc;
const char ** pcc = & pc ; // 1: not allowed
*pcc = & c;
*pc = 'C'; // 2: modifies a const object
}
-end note]
Adding
(const char * const *) casts all over the place doesn't really improve
code readability, even if it does give me const correctness!


Don't do that. The types are considered not to be compatible and if you
access it, you break aliasing rules (which GCC is strict about and can
generate code that gives you incorrect results or crashes). More info
about aliasing in plain English:

http://mail-index.netbsd.org/tech-ke...8/11/0001.html

Nov 15 '05 #3

P: n/a
On Thu, 07 Jul 2005 00:47:06 +0100, Christian Bau wrote:

....
If you think carefully about it, you will find that the following
conversions are safe resp. unsafe (and therefore are allowed /
disallowed to happen automatically):

char* -> const char* is safe
const char* -> char* is unsafe
OK
char** -> const char** is unsafe
const char** -> char** is safe
These are both unsafe.
char*** -> const char*** is safe
const char*** -> char*** is unsafe


As are these. C allows

char ** -> char *const *
char *** -> char **const *

I believe C++ also allows:

char *** -> char *const *const *
char *** -> const char *const *const *
char (*)[N] -> const char (*)[N]

I.e. if you add const all "higher" intermediate levels must also be const
qualified. For whatever reasons the C committee decided not to support
this, which is a pity.

Lawrence

Nov 15 '05 #4

P: n/a
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Lawrence Kirby <lk****@netactive.co.uk> writes:
On Thu, 07 Jul 2005 00:47:06 +0100, Christian Bau wrote:

...
If you think carefully about it, you will find that the following
conversions are safe resp. unsafe (and therefore are allowed /
disallowed to happen automatically):

char* -> const char* is safe
const char* -> char* is unsafe
OK
char** -> const char** is unsafe
const char** -> char** is safe


These are both unsafe.


For the top example, I have a pointer to pointer-to-char being cast
to pointer to pointer-to-const-char. I don't see how any addition of
const qualifiers makes it unsafe. I now can't modify the char through
the pointer (**p = 'c'), but I fail to see how there are other issues.
char*** -> const char*** is safe
const char*** -> char*** is unsafe


As are these. C allows


I'm afraid I still fail to see why the first is unsafe.
char ** -> char *const *
char *** -> char **const *

I believe C++ also allows:

char *** -> char *const *const *
char *** -> const char *const *const *
char (*)[N] -> const char (*)[N]

I.e. if you add const all "higher" intermediate levels must also be const
qualified. For whatever reasons the C committee decided not to support
this, which is a pity.


In my code, I have "char *type[]" passed to functions as "char **".
This is an argv-like array of pointers-to-strings. Most of the
functions don't modify the pointed-to objects. They might iterate
through them, copy them, etc.. In this case, I'm not modifying the
string array nor the strings themselves, so "const char * const *"
would be ideal, so it's obvious to the caller that it's read-only.
It's also useful to indicate functions that would not be safe to use
with truly const arrays of strings.

My only problem here is the compiler warning about the conversion
(char ** -> const char * const *), but I'm afraid I still don't
understand why this it, even with your examples.
Regards,
Roger

- --
Roger Leigh
Printing on GNU/Linux? http://gimp-print.sourceforge.net/
Debian GNU/Linux http://www.debian.org/
GPG Public Key: 0x25BFB848. Please sign and encrypt your mail.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Processed by Mailcrypt 3.5.8 <http://mailcrypt.sourceforge.net/>

iD8DBQFC2p5yVcFcaSW/uEgRAp/dAJwP54PPxBLIpDIai+pUkbxH63UJsACdFgov
8/LnVcHfJrihmxLIo9y7Tpo=
=bvV6
-----END PGP SIGNATURE-----
Nov 15 '05 #5

P: n/a
On Sun, 17 Jul 2005 19:07:48 +0100, Roger Leigh wrote:
Lawrence Kirby <lk****@netactive.co.uk> writes:
On Thu, 07 Jul 2005 00:47:06 +0100, Christian Bau wrote:
<snip>
const char** -> char** is safe
These are both unsafe.


For the top example, I have a pointer to pointer-to-char being cast
to pointer to pointer-to-const-char. I don't see how any addition of
const qualifiers makes it unsafe. I now can't modify the char through
the pointer (**p = 'c'), but I fail to see how there are other issues.


You didn't examine Me's example closely enough. Here it is again with
commentary:

int main (void) {
const char c = 'c'; /* we declare c to be const-protected */
char * pc;
const char ** pcc = & pc ; /* we assign the address of a non-const
* char pointer to a doubly indirected const
* char pointer. You want want this cast
* conversion to be automatic */
*pcc = & c; /* now we assign the address of our
* const-protected variable c to the pointer
* that pcc points to, which is pc.
* i.e. this line is equivalent to pc = &c
* which plainly should not be allowed because
* it removes the const-protection from c. */
*pc = 'C'; /* now we can assign to our supposedly
* const-protected c variable by dereferencing
* our non-const pc pointer. Why? Because the
* cast from char ** to const char ** was
* automatically allowed. */
}
char*** -> const char*** is safe
const char*** -> char*** is unsafe


As are these. C allows


I'm afraid I still fail to see why the first is unsafe.


By extension for the same reason as Me's example shows.

<snip>
In my code, I have "char *type[]" passed to functions as "char **".
This is an argv-like array of pointers-to-strings. Most of the
functions don't modify the pointed-to objects. They might iterate
through them, copy them, etc.. In this case, I'm not modifying the
string array nor the strings themselves, so "const char * const *"
would be ideal, so it's obvious to the caller that it's read-only.
It's also useful to indicate functions that would not be safe to use
with truly const arrays of strings.

My only problem here is the compiler warning about the conversion
(char ** -> const char * const *), but I'm afraid I still don't
understand why this it, even with your examples.


That one semantically is still safe, since it would disallow the
problem assignment *pcc = &c in the above example.

It is not allowed by C though, I don't know why.

Nov 15 '05 #6

P: n/a
Netocrat <ne******@dodo.com.au> writes:
On Sun, 17 Jul 2005 19:07:48 +0100, Roger Leigh wrote:

For the top example, I have a pointer to pointer-to-char being cast
to pointer to pointer-to-const-char. I don't see how any addition of
const qualifiers makes it unsafe. I now can't modify the char through
the pointer (**p = 'c'), but I fail to see how there are other issues.


You didn't examine Me's example closely enough. Here it is again with
commentary:


Thanks, that makes much more sense.
--
Roger Leigh
Printing on GNU/Linux? http://gimp-print.sourceforge.net/
Debian GNU/Linux http://www.debian.org/
GPG Public Key: 0x25BFB848. Please sign and encrypt your mail.
Nov 15 '05 #7

P: n/a
On Mon, 18 Jul 2005 18:20:03 +1000, Netocrat <ne******@dodo.com.au>
wrote:

<snip>
My only problem here is the compiler warning about the conversion
(char ** -> const char * const *), but I'm afraid I still don't
understand why this it, even with your examples.


That one semantically is still safe, since it would disallow the
problem assignment *pcc = &c in the above example.

It is not allowed by C though, I don't know why.


AIUI because it was simpler to think of, explain, and prove safe, the
single simple rule T * -> qualified T * . Remember that qualification
itself was new in C90 and there wasn't widespread experience with
implementation and use. C++, with the benefit of hindsight and perhaps
more time to analyze, does allow it.

- David.Thompson1 at worldnet.att.net
Nov 15 '05 #8

P: n/a
Dave Thompson <da*************@worldnet.att.net> wrote:

AIUI because it was simpler to think of, explain, and prove safe, the
single simple rule T * -> qualified T * . Remember that qualification
itself was new in C90 and there wasn't widespread experience with
implementation and use. C++, with the benefit of hindsight and perhaps
more time to analyze, does allow it.


Correct. Unfortunately, the C++ standard describes the language
sufficiently differently from the way C did that it isn't possible to
just pick up the words from the C++ standard and plug them into the C
standard. C also adopted the additional "restrict" qualifier, which
will require a revision of the rules since it doesn't seem to behave the
same as const and volatile.

-Larry Jones

Is it too much to ask for an occasional token gesture of appreciation?!
-- Calvin
Nov 15 '05 #9

This discussion thread is closed

Replies have been disabled for this discussion.