LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 10-25-2010, 08:19 PM   #1
lesca
Member
 
Registered: Sep 2010
Posts: 58

Rep: Reputation: 0
Smile Strange cast; (char * const *) Why two asterisks ?


I saw a very strange cast(in red) in an example program given below:

Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int cmpstringp(const void *p1, const void *p2)
{
   /* The actual arguments to this function are "pointers to
	  pointers to char", but strcmp(3) arguments are "pointers
	  to char", hence the following cast plus dereference */

   return strcmp(* (char * const *) p1, * (char * const *) p2);
}

int main(int argc, char *argv[])
{
   int j;

   if (argc < 2) {
	fprintf(stderr, "Usage: %s <string>...\n", argv[0]);
	exit(EXIT_FAILURE);
   }

   //fprintf(stdout, "%s %d\n", argv[1], sizeof(argv[1]));

   qsort(&argv[1], argc - 1, sizeof(argv[1]), cmpstringp);

   for (j = 1; j < argc; j++)
	   puts(argv[j]);
   exit(EXIT_SUCCESS);
}
I can understand "pointers to pointers to char". I just can't figure out why there are two asterisk.

The common use seems like (const char *)p1

Can anyone give me some tips?

Thanks!
 
Old 10-25-2010, 11:44 PM   #2
neonsignal
Senior Member
 
Registered: Jan 2005
Location: Melbourne, Australia
Distribution: Debian Bookworm (Fluxbox WM)
Posts: 1,391
Blog Entries: 54

Rep: Reputation: 360Reputation: 360Reputation: 360Reputation: 360
Quote:
Originally Posted by lesca View Post
I can understand "pointers to pointers to char". I just can't figure out why there are two asterisk.
With pointers you have to distinguish between a "constant pointer to an item" and "a pointer to a constant item". The position of the const changes the meaning.

Your example is a "pointer to pointer to char". The placement of the const means that it is a "pointer to a constant pointer to char".

The reason for casting in this way is that the comparison function takes a pointer to a constant item. The constant item in this case is a string, so the cast is turning the 'constant void' into a 'constant pointer to char'.

Last edited by neonsignal; 10-25-2010 at 11:47 PM.
 
1 members found this post helpful.
Old 10-26-2010, 04:40 AM   #3
lesca
Member
 
Registered: Sep 2010
Posts: 58

Original Poster
Rep: Reputation: 0
Smile

Quote:
Originally Posted by neonsignal View Post
With pointers you have to distinguish between a "constant pointer to an item" and "a pointer to a constant item". The position of the const changes the meaning.
I think this is my problem.

A point to a constant item declares like this: const char* p1;
Am I right?

As to a const pointer to an item, it is really familiar (this reminds me of strings, like char s="hello", but the problem is I have no idea how to declare it. Can you give me an example?


Quote:
Originally Posted by neonsignal View Post
Your example is a "pointer to pointer to char". The placement of the const means that it is a "pointer to a constant pointer to char".
Another problem is how to replace the const to "pointer to a constant pointer to char". Maybe I will find the answer if the first question is solved. But if you can show me some explicit steps, it will be more helpful.

Thanks!
 
Old 10-26-2010, 05:43 AM   #4
neonsignal
Senior Member
 
Registered: Jan 2005
Location: Melbourne, Australia
Distribution: Debian Bookworm (Fluxbox WM)
Posts: 1,391
Blog Entries: 54

Rep: Reputation: 360Reputation: 360Reputation: 360Reputation: 360
Quote:
Originally Posted by lesca View Post
A pointer to a constant item declares like this: const char* p1;
Yes.

Quote:
As to a const pointer to an item, it is really familiar (this reminds me of strings, like char s="hello"
Actually, a string literal is also a pointer to a constant char, ie:
Code:
const char *s = "hello";
Note that it is the characters that are constant (cannot be changed). The pointer itself is not constant, and can be assigned a new value:
Code:
const char *s = "hello";
*s = 'g'; // error: assignment of read-only location ‘*s’
s = "goodbye"; // but this works
A constant pointer would be declared like this:
Code:
char * const s = "hello";
If you tried to reassign s, you would get a compiler error:
Code:
char * const s = "hello";
*s = 'g'; // now this works
s = "goodbye"; // error: assignment of read-only variable ‘s’
You can have both too, eg:
Code:
const char * const s = "hello";
This would mean that both the item being referenced and the pointer itself are constant; neither can be changed.

Incidentally, when you are reading declarations, you read them inside out, ie, start at the variable name and it will make more sense.

Last edited by neonsignal; 10-26-2010 at 06:11 AM.
 
1 members found this post helpful.
Old 10-26-2010, 09:05 PM   #5
lesca
Member
 
Registered: Sep 2010
Posts: 58

Original Poster
Rep: Reputation: 0
Hello neonsignal

Thanks a lot for your explanation.

After reading your tips, I consulted some docs. And now I can explain some of them more or less.

`const char *p' declares p as pointer to constant char.
`char * const p' declares p as pointer to constant pointer to char.
`const char * const p' declares p as constant pointer to constant char.

And now I find the rule: read the declaration inversely.
`const char * const p' can be analyzed in the following steps:

(const char) <- ( * const ) <- p // replace '*' with the word 'pointer' always

and then read it inversely:
p is a constant pointer to constant char.

And this rule has effects on many declarations.

Take `char * const * p' for example: declare a pointer to constant pointer to char.
And `const char * const ** p' means: p is a pointer to pointer to constant pointer to constant char.

Am I right?
Can this rule have effects on all pointer declarations?
Do you use some rule when explain a pointer declaration?
 
Old 10-27-2010, 06:02 AM   #6
neonsignal
Senior Member
 
Registered: Jan 2005
Location: Melbourne, Australia
Distribution: Debian Bookworm (Fluxbox WM)
Posts: 1,391
Blog Entries: 54

Rep: Reputation: 360Reputation: 360Reputation: 360Reputation: 360
Quote:
Originally Posted by lesca View Post
Take `char * const * p' for example: declare a pointer to constant pointer to char.
And `const char * const ** p' means: p is a pointer to pointer to constant pointer to constant char.
Yep, exactly right.

Quote:
Can this rule have effects on all pointer declarations?
All C declarations work this way. It was designed this way so that the declarations match the usage in expressions (in contrast for example to Pascal references, where the declarations and the usage work in opposite ways).

It gets a bit more complicated when you have components that fall on both sides of the variable, eg
Code:
char *p[3];
But the rule still works, reading from inside out; you just have to know the precedence order (ie, that '[]' has higher precedence than '*'). So in this case 'p' is an array of pointer to char.
 
1 members found this post helpful.
Old 10-27-2010, 10:17 AM   #7
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by lesca View Post
`const char *p' declares p as pointer to constant char.
Yes, but 'char const *p' has the same meaning and fits better in the general rule.

Quote:
`char * const p' declares p as pointer to constant pointer to char.
No. That p is just a constant pointer to char.

'char* const* p' declares p as pointer to constant pointer to char.

Quote:
`const char * const p' declares p as constant pointer to constant char.
Yes.

You generally read declarations outward from the name being declared (which matches your idea of backward from the name being declared for these basic declarations that end with the name being declared.

The const keyword applies to the item on its left, except for the special case where the const keyword is first and then applies to the item on its right. I prefer to never put the const keyword first, so it always applies to the item on its left.
 
1 members found this post helpful.
Old 10-27-2010, 07:49 PM   #8
lesca
Member
 
Registered: Sep 2010
Posts: 58

Original Poster
Rep: Reputation: 0
Oh. That's my mistake.
And thanks for your correction and practical tips.

Now, I need to come back to the cast.
I tested this example further, and found that
1. argv is an array of pointer to char (no const modifier)
2. argv[1] is a pointer to char
3. &argv[1] is a pointer to pointer to char
4. after passing it to cmpstringp(), it has been cast to `const void *' immediately
to make sure the function is not type-dependent

And we have the cast: ( char * const * )
which make it `a pointer to constant pointer to char'.
Then we add a '*' to dereference the type, and it is now
`a constant pointer'.

However, strcmp(3) arguments are pointers to constant char.

And I edit the line in red on the first floor (my first post)
A. return strcmp( ( const char *) p1, ( const char *) p2);
B. return strcmp(* ( const char * *) p1, * ( const char * *) p2);

The two lines marked 'A' and 'B' both work without any error or warning message.

Now, my question is
a) Is it a mistake in the example, or my mistake when analyzing the arguments type?
b) Is `( const char *)' equivalent to `* ( const char * *)'. They both seem to work well.

Thanks a lot for your patience to read and answer my questions!!
 
Old 10-27-2010, 07:59 PM   #9
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by lesca View Post
A. return strcmp( ( const char *) p1, ( const char *) p2);
B. return strcmp(* ( const char * *) p1, * ( const char * *) p2);

The two lines marked 'A' and 'B' both work without any error or warning message.
They may both compile and run without error or warning, but they don't perform the same comparison.

Quote:
b) Is `( const char *)' equivalent to `* ( const char * *)'.
The data type of the result is the same, but the value isn't.
 
1 members found this post helpful.
Old 10-27-2010, 08:51 PM   #10
neonsignal
Senior Member
 
Registered: Jan 2005
Location: Melbourne, Australia
Distribution: Debian Bookworm (Fluxbox WM)
Posts: 1,391
Blog Entries: 54

Rep: Reputation: 360Reputation: 360Reputation: 360Reputation: 360
Quote:
Originally Posted by lesca View Post
a) Is it a mistake in the example, or my mistake when analyzing the arguments type?
b) Is `( const char *)' equivalent to `* ( const char * *)'. They both seem to work well.
As johnsfine points out, they are very different.

The arguments to the cmpstringp comparison function in the example are of type "pointer to pointer to char". They are passed in as "pointer to void" because that enables qsort to handle different types (ie, not just strings), but in this example, they really are "pointer to pointer to char".

Okay, so when your code has:
Code:
* (const char * *) p1
you have to work out what it is doing. The cast (the bracketed type) is there simply to say 'treat this as a different type'. The cast doesn't perform any action on the pointer, it just forces it to be the correct type. In the example, the cast is simply setting it to the correct "pointer to pointer to char" (I'll examine the 'const' issue later, to keep it simple). Once it is the correct type, the '*' at the beginning causes the value of the pointer to be fetched (and the type of the expression is now a "pointer to char").

You can cast pointers to any type you want; the compiler won't complain because it assumes you know what you are doing. That is why casts are considered to be dangerous.

So when you did this:
Code:
(const char *) p1
what happened here was that you forced the "pointer to pointer to char" to be treated as "pointer to char". Now you satisfied the type checking, but note that this time the contents of the pointer were not fetched before calling strcmp. So this code effectively says "take p1, and treat it as if it were a pointer to char". It isn't a pointer to char, it is a pointer to another pointer to char. So what will happen is that the string comparison will do the wrong thing (since it is comparing pointers as if they were characters), and the array will not be sorted correctly. It will sort in order of the pointers, ie, in order of where the strings are located in memory! (which may match the alphabetical order by sheer fluke).

You have also moved the position of the 'const'; it was '* (char * const *) p1'. In the qsort example, that will not cause a problem, but it no longer matches the intended type being passed in. The compiler cannot pick this up, because the cast will force the type to be whatever you want.

Last edited by neonsignal; 10-27-2010 at 09:02 PM.
 
1 members found this post helpful.
Old 10-29-2010, 08:12 PM   #11
lesca
Member
 
Registered: Sep 2010
Posts: 58

Original Poster
Rep: Reputation: 0
Yes! You are right!
Today, I test the program with other strings, but the output is not correct if the cast is '(const char *)' and no leading '*', i.e.,
return strcmp( ( const char *) p1, ( const char *) p2);.

And I understand the cast is just a seemingly true type to let the complier know, but may not let its value changed as I hope.

But there is still something confusing me:

You mentioned
Quote:
The cast doesn't perform any action on the pointer, it just forces it to be the correct type. In the example, the cast is simply setting it to the correct "pointer to pointer to char.
And then I think these casts here has identical meaning and effect:
(char * const *) (char * *) and (const char * *)
Am I right?

But at last you mentioned
Quote:
You have also moved the position of the 'const'; it was '* (char * const *) p1'. In the qsort example, that will not cause a problem, but it no longer matches the intended type being passed in.
What is the INTENDED type? The '(char * const *)' or my '(const char * *)'?

If it is mine, I think the 'const' is extra here, because they all work even without 'const', like '(char * *)', i.e.,
return strcmp(* (char * *) p1, * (char * *) p2);

But if it is '(char * const *)', why cast it as 'pointer to const pointer to char' rather than 'pointer to pointer to const char' which may satisfy the declaration of strcmp(3) better?

Thanks for your patience and great help!

Last edited by lesca; 10-30-2010 at 12:50 AM.
 
Old 10-31-2010, 07:07 AM   #12
neonsignal
Senior Member
 
Registered: Jan 2005
Location: Melbourne, Australia
Distribution: Debian Bookworm (Fluxbox WM)
Posts: 1,391
Blog Entries: 54

Rep: Reputation: 360Reputation: 360Reputation: 360Reputation: 360
Quote:
Originally Posted by lesca View Post
But if it is '(char * const *)', why cast it as 'pointer to const pointer to char' rather than 'pointer to pointer to const char' which may satisfy the declaration of strcmp(3) better?
First, just to clarify, the const is not a type specifier, but a type qualifier. It is a contract that is made to not change the value, but that does not mean that it is a type to be matched (eg with the parameters to strcmp).

You can call a function like strcmp with a pointer to non-const char, because it is the strcmp that is 'promising' to preserve the characters, not the calling function. You can't do the opposite, ie, call a function that takes a pointer to non-const char using a pointer to const char, because then the called function might break the 'promise' being made by the calling function. However, the error in this second case is a const error, not a type mismatch error.

Quote:
What is the INTENDED type? The '(char * const *)' or my '(const char * *)'?
The intended type is a pointer to a const item, because the declaration of the compar function for qsort takes a pointer to const void. In C, the types cannot be made to match because then you would have to have multiple definitions of qsort depending on the type of items being sorted. So the compromise is to use the void type in the comparison function; but the intention is that it is the type of the item.

So in the example in which the item is a string (ie a pointer to char), then the pointer at least should be const, ie '(char * const *)'. I think it would be fair to argue that the characters themselves should not be changed either, so it would be quite reasonable to cast it as '(const char * const *)'. My point was simply that you shouldn't remove the const qualification on the pointer, because the const qualification is there on the void that will be reinterpreted as a pointer, and you don't intend to override that qualification, just the type.

It isn't an important point, I am quibbling over details here. The reason for not casting away const qualifiers is so that anyone changing the function (perhaps at a later stage) does not accidentally do something that would change the item. Unfortunately in C, since const is not the default, the religious maintenance of const qualifiers can become an annoying detail that is often overlooked.
 
1 members found this post helpful.
Old 10-31-2010, 07:59 PM   #13
lesca
Member
 
Registered: Sep 2010
Posts: 58

Original Poster
Rep: Reputation: 0
Wow! All that you tell me is amazing for me!
I have never read them from any book about C.
And now I understand many details in that example!

Thanks very very much!
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Help fixing C++ invalid conversion from const char* to char primerib Programming 26 03-10-2010 06:56 PM
invalid conversion from `const char*' to `char' error holystinken Programming 7 11-23-2009 06:01 PM
error: invalid conversion from const char* to char* Dahakon Programming 1 08-31-2009 09:33 AM
conversion from ‘const char*’ to ‘unsigned char*’ rubadub Programming 2 02-08-2008 05:45 PM
If I get invalid conversion from `const char*' to `char' what should I be lookin for? RHLinuxGUY Programming 5 03-12-2006 10:35 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:52 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration