qsort man page


B

Bill Cunningham

This is from the qsort man page. I don't quite understand why there is
a comment here saying that arguments to this generic function to pass
to qsort is a pointer to pointer. It looks to me like cmpstringp takes
a generic pointer. Not pointer to pointer. Why does the comment say
pointer to pointer? This is confusing me also with the strcmp
parameters which are cast as a pointer to pointer and dereferenced. I
hope I'm clear. THanks.

Bill

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static 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);
}
qsort(&argv[1], argc - 1, sizeof(char *), cmpstringp);
for (j = 1; j < argc; j++)
puts(argv[j]);
exit(EXIT_SUCCESS);
}
 
Ad

Advertisements

B

Ben Bacarisse

Bill Cunningham said:
This is from the qsort man page. I don't quite understand why there is
a comment here saying that arguments to this generic function to pass
to qsort is a pointer to pointer. It looks to me like cmpstringp takes
a generic pointer. Not pointer to pointer. Why does the comment say
pointer to pointer?

Yup, cmpstringp takes two generic pointers, but in order to do anything
useful with them it must know to what kind of data they really point.
If the array being sorted is an array of ints, then the two pointers are
really pointing at int data, so casting to int * and dereferencing would
be the right thing to do (you'd write *(const int *)p1).

But, argv -- the array being sorted here -- is an array of character
pointers so the comparisson function gets passed pointers to a pair of
these character pointers, converted to "generic" (void *) pointers.
This is confusing me also with the strcmp
parameters which are cast as a pointer to pointer and dereferenced. I
hope I'm clear. THanks.

Bill

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static 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);
}
qsort(&argv[1], argc - 1, sizeof(char *), cmpstringp);
for (j = 1; j < argc; j++)
puts(argv[j]);
exit(EXIT_SUCCESS);
}
 
B

Bill Cunningham

Bill Cunningham &lt;[email protected]&gt; writes:

&gt; This is from the qsort man page. I don't quite understand why there is
&gt; a comment here saying that arguments to this generic function to pass
&gt; to qsort is a pointer to pointer. It looks to me like cmpstringp takes
&gt; a generic pointer. Not pointer to pointer. Why does the comment say
&gt; pointer to pointer?

Yup, cmpstringp takes two generic pointers, but in order to do anything
useful with them it must know to what kind of data they really point.

I'm not quite sure I understand. I wanted to try this,

char name[]={"fred","suzy","mike","jennifer"};

And sort that using qsort. I know there are other functions that can be used with qsort like OT posix function but just speaking of C strcmp is the function to use.

Bill
If the array being sorted is an array of ints, then the two pointers are
really pointing at int data, so casting to int * and dereferencing would
be the right thing to do (you'd write *(const int *)p1).

But, argv -- the array being sorted here -- is an array of character
pointers so the comparisson function gets passed pointers to a pair of
these character pointers, converted to &quot;generic&quot; (void *) pointers.

&gt; This is confusing me also with the strcmp
&gt; parameters which are cast as a pointer to pointer and dereferenced. I
&gt; hope I'm clear. THanks.
[snip]
 
B

Ben Bacarisse

pete said:
I think that the way that the const keyword
is used in the return statement,
shows some confusion on the part of the author.

It causes the result of the dereference
in the strcmp function call, to be const qualified,
which is neither required nor consistent
with the parameters of strcmp
being pointers to const qualified types.

I am not so sure. The const that is there is a Good Const. Omitting it
might cause a warning (gcc will oblige) about "casting away" const. The
question as to whether there should be another:

*(const char *const *)p1

is not clear cut to me. The cast, as written, is a correct reflection
of the object being sorted, and there is some documentary merit in doing
that -- cast only to the type of the thing being sorted.

Your suggestion, to cast to the type needed by strcmp, is not wrong but,
in general, I would avoid it. If you cast to the type of the actual
data, the compiler will warn you if this type is not suitable for the
call being made. In essence, you get a little more checking.

No big deal in this case because strcmp takes "safer" arguments that
those being passed, but if I were forced to used some odd compare
function like

int caseless_compare_that_alters_the_data(char *s1, char *s2);

to sort an array of const char **, my rule would show up a problem. You
cast to the type of the actual data, and the call to this function will
be flagged up as dangerous.
 
B

Ben Bacarisse

pete said:
There is no const in the type of the data being sorted.
The array being sorted by cmpstringp in the example,
is argv, which is an array of (char *) type elements.

The const is introduced by the parameter type of cmpstringp.

That level of const and pointer is removed by the *. maybe I should
have said "cast to a const pointer to the type thing being pointed to"
but it was all getting a bit wordy.

You see my point, I hope. One level of pointer and const comes from
using qsort, all the rest comes from the type of thing being sorted.
 
M

Mark Storkamp

I'm not quite sure I understand. I wanted to try this,

char name[]={"fred","suzy","mike","jennifer"};

And sort that using qsort. I know there are other functions that can be used
with qsort like OT posix function but just speaking of C strcmp is the
function to use.

Bill

Last November you were trying to sort numbers. Why don't you post the
code you got to work from back then. Then maybe someone can show you
what needs to be changed to make it work for your char array.
 
Ad

Advertisements

I

Ike Naar

I'm not quite sure I understand. I wanted to try this,

char name[]={"fred","suzy","mike","jennifer"};

The types of the array and the initializer do not match.
You probably want

char *name[] = {"fred","suzy","mike","jennifer"};
 
B

Bill Cunningham

Last November you were trying to sort numbers. Why don't you post the
code you got to work from back then. Then maybe someone can show you
what needs to be changed to make it work for your char array.

You have a very good memory. I remember asking about qsort but I didn't realize it was in November. Maybe I can look back and see what I got to work.

Bill
 
B

Bill Cunningham

The types of the array and the initializer do not match.
You probably want

char *name[] = {&quot;fred&quot;,&quot;suzy&quot;,&quot;mike&quot;,&quot;jennifer&quot;};

Ok I see what your saying. I've never went as far as to get into pointers to pointers before.

B
 
B

Barry Schwarz

On Monday, July 23, 2012 10:43:16 AM UTC-4, Ike Naar wrote:

&gt; The types of the array and the initializer do not match.
&gt; You probably want
&gt;
&gt; char *name[] = {&amp;quot;fred&amp;quot;,&amp;quot;suzy&amp;quot;,&amp;quot;mike&amp;quot;,&amp;quot;jennifer&amp;quot;};

Ok I see what your saying. I've never went as far as to get into pointers to pointers before.

B



On Monday, July 23, 2012 10:43:16 AM UTC-4, Ike Naar wrote:

&gt; The types of the array and the initializer do not match.
&gt; You probably want
&gt;
&gt; char *name[] = {&amp;quot;fred&amp;quot;,&amp;quot;suzy&amp;quot;,&amp;quot;mike&amp;quot;,&amp;quot;jennifer&amp;quot;};

Ok I see what your saying. I've never went as far as to get into pointers to pointers before.

Since there are no pointers to pointers in the above code, why are you bringing it up as an additional confusion factor?
 
B

Bill Cunningham

Since there are no pointers to pointers in the above code, why are you bringing it up as an additional confusion factor?

Barry the answer your looking for you answered in your own question just "....bringing it up as an additional confusion factor." Actually this code tome being char *name[]; looks equivalent to a char ** and a protoype of char ** would probably take char *name[];

B
 
Ad

Advertisements

A

Alan Curry

I don't know.
Maybe I am being wrongly influenced by the warnings
generated by the compiler that I am using.

When I rewrite and compile cmpstringp to do the type conversions
through assignment to intermediate objects onstead of by casts,
I get warnings when the intermediate objects are of the same type
as the cast from the original version of cmpstringp, (char *const *).

That's strange. The assignments aren't any worse than the cast. I think
they're slightly better because they're more constrained (the cast will
convert any pointer to any other pointer; the assignment requires compatible
types or void).
I don't get warnings when the intermediate objects are
of type (const char **).


/* BEGIN new.c */

#include <string.h>

int
cmpstringp(const void *p1, const void *p2)
{
return strcmp(* (char * const *) p1, * (char * const *) p2);
}

int
cmpstringp_2(const void *p1, const void *p2)
{
/*
** This function is OK
*/
const char **pp1;
const char **pp2;

pp1 = p1;
pp2 = p2;

gcc warns here.
new.c: In function 'cmpstringp_2':
new.c:20: warning: assignment discards qualifiers from pointer target type
new.c:21: warning: assignment discards qualifiers from pointer target type

It didn't even need -Wall to produce those warnings. They're pretty severe.
And they're right. After the assignments, *pp1 = "oops" would be allowed
according to the type of pp1, but it's pointing to something that the caller
of the function didn't give you permission to modify.
return strcmp(*pp1, *pp2);
}

int
cmpstringp_3(const void *p1, const void *p2)
{
/*
** This function generates warnings
*/
char *const *pp1;
char *const *pp2;

pp1 = p1;
pp2 = p2;
/*
** (35) : warning C4090: '=' : different 'const' qualifiers
** (36) : warning C4090: '=' : different 'const' qualifiers
*/
return strcmp(*pp1, *pp2);
}

/* END new.c */

gcc doesn't find anything wrong with cmpstringp_3 even with more warnings
enabled. Could you try your compiler with typedefs for the inner pointer?

typedef char *string;
typedef const char *fixedstring;

int
cmpstringp_4(const void *p1, const void *p2)
{
fixedstring *pp1;
fixedstring *pp2;

pp1 = p1;
pp2 = p2;

return strcmp(*pp1, *pp2);
}

int
cmpstringp_5(const void *p1, const void *p2)
{
const string *pp1;
const string *pp2;

pp1 = p1;
pp2 = p2;
return strcmp(*pp1, *pp2);
}

cmpstringp_4 and cmpstringp_5 should be exactly equivalent to cmpstringp_2
and cmpstringp_3 respectively, unless I messed up. gcc treats them the same.
Warnings on cmpstringp_4, none on cmpstringp_5.
 
N

Noob

Bill said:
[...] char *name[]; looks equivalent to a char ** and a protoype
of char ** would probably take char *name[];

"Array of foo" and "pointer to foo" are only "equivalent"
when used as function parameters, because only then does
an array "decay" into a pointer to its first element.

In other contexts, arrays and pointers are NOT interchangeable!!

Take a look at the recent thread:
"Difference of extern short *x and extern short x[]?"
(Date: Tue, 17 Jul 2012 14:10:45 +0200)
 
B

Ben Bacarisse

That's strange.

I echo that. gcc 4.6.3 gives the warnings for cmpstringp_2 as one would
expect and not for cmpstringp_3 (again as one would expect).
The assignments aren't any worse than the cast. I think
they're slightly better because they're more constrained (the cast will
convert any pointer to any other pointer; the assignment requires compatible
types or void).

With the extra flexibility (over plain compatibility) that:

"the type pointed to by the left has all the qualifiers of the type
pointed to by the right"

There should be a name for this -- maybe "assignment compatible". It
applies to plain assignment and to argument passing which happens "as if
by assignment".

<snip>
 
K

Keith Thompson

Noob said:
Bill said:
[...] char *name[]; looks equivalent to a char ** and a protoype
of char ** would probably take char *name[];

"Array of foo" and "pointer to foo" are only "equivalent"
when used as function parameters, because only then does
an array "decay" into a pointer to its first element.

Incorrect.

There are actually two different things going on here.

An expression of array type is converted to a pointer to the array
object's first element in *most* contexts, not just when used as
a function argument (i.e., an expression in a function call). The
exceptions are:

- When the array expression is the operand of a unary "&" operator
(which yields the address of the entire array object, not the address
of the address of the first element -- which wouldn't even make
sense);
- When the array expression is the operand of a unary "sizeof" operator
(which yields the size of the entire array, not the size of a pointer;
and
- When the array expression is a string literal in an initializer, used
to initialize an array object (for example,
char s[6] = "hello";
copies the entire array into s).

The other thing is that a function parameter defined as "array
of foo" is "adjusted" to a pointer to foo. This is a compile-time
transformation, not a conversion. Perhaps you were using the word
"decay" to refer to this adjustment, but that word is more often
used to refer to the conversion I described -- though the standard
doesn't use the word "decay", at least not in that sense.

These two rules conspire to make people *think* that arrays and pointers
are interchangeable.

See also, as always, section 6 of the comp.lang.c FAQ,
In other contexts, arrays and pointers are NOT interchangeable!!

Correct.

[...]
 
B

Barry Schwarz

On Monday, July 23, 2012 4:29:15 PM UTC-4, Barry Schwarz wrote:

&gt; Since there are no pointers to pointers in the above code, why are you bringing it up as an additional confusion factor?

Barry the answer your looking for you answered in your own question just&quot;...bringing it up as an additional confusion factor.&quot; Actually this code to me being char *name[]; looks equivalent to a char ** and a protoype of char ** would probably take char *name[];

Your comments make no sense and add further support to the conclusion you just add things at random.

For the benefit of any newcomer reading this thread, the object definitions:

char **x means x is a scalar object.

char *y[5] means y is an aggregate object.

parameter declarations in a function definition behave slightly differently due to special rules regarding arrays but that is a different topic.
 
Ad

Advertisements

B

Bill Cunningham

29:15 PM UTC-4, Barry Schwarz wrote:
&gt;
&gt; &amp;gt; Since there are no pointers to pointers in the above code, why are you bringing it up as an additional confusion factor?
&gt;
&gt; Barry the answer your looking for you answered in your own questionjust &amp;quot;...bringing it up as an additional confusion factor.&amp;quot; Actually this code to me being char *name[]; looks equivalent to a char** and a protoype of char ** would probably take char *name[];

Your comments make no sense and add further support to the conclusion youjust add things at random.

For the benefit of any newcomer reading this thread, the object definitions:

char **x means x is a scalar object.

char *y[5] means y is an aggregate object.

parameter declarations in a function definition behave slightly differently due to special rules regarding arrays but that is a different topic..

Scalar or aggregate object? Huh? I'm not touching this one. This post isn'taddressed to me anyway it might be best just to abandon this thread.
 
T

tom st denis

You have a very good memory. I remember asking about qsort but I didn't realize it was in November. Maybe I can look back and see what I got to work..

Bill

I'd like to state for the fact of stating that I knew how to call
qsort() when I was 12 years old [18 something years ago...]. You're
not stupider than a 12 year old are you?

Tom
 
J

John Gordon

In said:
I'd like to state for the fact of stating that I knew how to call
qsort() when I was 12 years old [18 something years ago...]. You're
not stupider than a 12 year old are you?

Some 12 year olds know how to milk a cow.

I don't know how to milk a cow, and I'd wager many of the folks in this
newsgroup don't either.

Does that make us stupider than a 12 year old?
 
Ad

Advertisements

T

tom st denis

In said:
I'd like to state for the fact of stating that I knew how to call
qsort() when I was 12 years old [18 something years ago...].  You're
not stupider than a 12 year old are you?

Some 12 year olds know how to milk a cow.

I don't know how to milk a cow, and I'd wager many of the folks in this
newsgroup don't either.

Does that make us stupider than a 12 year old?

If I spent 15 years trying to milk a cow and some 12 year old learned
how to do it in a weekend I might consider myself in need of help.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top