const, typedef, and warning

M

Michael Press

The following code compiles with a warning this way

cc -c -DWHICH=1 try.c

try.c: In function 'foo':
try.c:12: warning: initialization discards qualifiers from pointer target type

and without a warning this way.

cc -c -DWHICH=2 try.c

What gives? Why can I not use my typedef pst?


___________________BEGIN___________________
typedef struct
{
int a;
int b;
} st;

typedef st *pst;

int foo(const void *x)
{
#if WHICH == 1
const pst s = x;
#elif WHICH == 2
const st *s = x;
#endif
}

int main(void)
{
pst arr;

foo(arr);
return 0;
}
____________________END____________________
 
M

Michael Press

Peter Nilsson said:
This defines a constant pointer to a non-constant struct.


This defines a non-constant pointer to a constant struct.

<snip>

Thanks, I see. The following compiles without a warning,
for instance.

typedef const st *pcst;

int foo(const void *x)
{
pcst s = x;

....
 
P

Peter Nilsson

The following code compiles with a warning this way

    cc -c -DWHICH=1 try.c

try.c: In function 'foo':
try.c:12: warning: initialization discards qualifiers from
pointer target type

and without a warning this way.

    cc -c -DWHICH=2 try.c

What gives? Why can I not use my typedef pst?

___________________BEGIN___________________
typedef struct
{
    int a;
    int b;
} st;

typedef st *pst;

int foo(const void *x)
{
#if   WHICH == 1
    const pst  s = x;

This defines a constant pointer to a non-constant struct.
#elif WHICH == 2
    const  st *s = x;

This defines a non-constant pointer to a constant struct.
<snip>
 
I

Ian Collins

Thanks, I see. The following compiles without a warning,
for instance.

typedef const st *pcst;

int foo(const void *x)
{
pcst s = x;

Your post is a good example why typedefs for pointer types are seldom a
good idea!
 
M

Michael Press

Ian Collins said:
Your post is a good example why typedefs for pointer types are seldom a
good idea!

Yes. When are they a good idea?
You may have guessed that my
difficulty arose passing a
function pointer to qsort.
 
B

BartC

Ian Collins said:
Your post is a good example why typedefs for pointer types are seldom a
good idea!

Or use of 'const', which seems to be causing the problems.
 
K

Keith Thompson

BartC said:
Or use of 'const', which seems to be causing the problems.

It's the mis-use of 'const' that's causing the problems. const
can be difficult to use correctly, but it's very useful when used
correctly.
 
M

Michael Press

"BartC said:
Or use of 'const', which seems to be causing the problems.

I have to use it calling the library qsort.
No reason why I cannot write my own sort.
Heapsort is simple to write but slower than
quicksort, and quicksort is more difficult
to write. Now I still have the problem of
a compare routine that modifies what is to
be sorted, and that is solved with const.
Or I could turn off warnings, but that is
against my religion.
 
I

ImpalerCore

I have to use it calling the library qsort.
No reason why I cannot write my own sort.
Heapsort is simple to write but slower than
quicksort, and quicksort is more difficult
to write. Now I still have the problem of
a compare routine that modifies what is to
be sorted, and that is solved with const.
Or I could turn off warnings, but that is
against my religion.

To avoid dealing with the issue you encountered, as a rule I have
found it best not to define typedefs to pointer types at all. I value
being able to use const properly more than defining shortcuts to
declare pointer types.

However, I still like using typedefs for function pointer types.
 
P

Peter Nilsson

Michael Press said:
I have to use it calling the library qsort.

A better example would have made this clear.
No reason why I cannot write my own sort.

Here's a contrived program that sorts words in the command
line via qsort, and shows how many times qsort compared each
word...

% type wordsort.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct word_t
{
const char *word_m;
size_t count_m;
};

size_t word_count_g;
size_t capacity_g;

struct word_t **word_g;

struct word_t *add_word(const char *word_p)
{
struct word_t *word;

if (word_count_g == capacity_g)
{
struct word_t **new_word_g;

if (capacity_g == 0)
capacity_g = 16;
else if (capacity_g > (size_t) -1 / sizeof *word_g)
return 0;
else capacity_g *= 2;

new_word_g = realloc(word_g, capacity_g * sizeof *new_word_g);
if (!new_word_g) return 0;
word_g = new_word_g;
}

word = malloc(sizeof *word);
if (!word) return 0;

word->word_m = word_p;
word->count_m = 0;

return word_g[word_count_g++] = word;
}

int compare(const void *lv, const void *rv)
{
struct word_t * const *lwp = lv;
struct word_t * const *rwp = rv;
(*lwp)->count_m++;
(*rwp)->count_m++;
return strcmp((*lwp)->word_m, (*rwp)->word_m);
}

int main(int argc, char **argv)
{
int i;
size_t n;

for (i = 1; i < argc; i++)
if (!add_word(argv))
{ fputs("out of memory\n", stderr);
exit(EXIT_FAILURE); }

qsort(word_g, word_count_g, sizeof *word_g, compare);

for (n = 0; n < word_count_g; n++)
printf("%s: %lu\n",
word_g[n]->word_m,
(unsigned long) word_g[n]->count_m);

return 0;
}

% acc wordsort.c -o wordsort.exe

% wordsort this is a small sentence of few words
a: 3
few: 6
is: 6
of: 5
sentence: 5
small: 5
this: 7
words: 1

%
 
E

Eric Sosman

They can be useful for opaque types, e.g.:

struct internal_data;
typedef struct internal_data *handle_t;

I've used this -- and then I've backed away from it. It seems
to me that the function

handle_t getInternalData(const whatnot*);

.... often needs to be able to return an "I couldn't find one" result.
The "natural" thing to do in C is to return NULL (see malloc, fopen,
getenv, strstr, ...), but telling the caller to compare the returned
value to NULL "leaks the abstraction."

Yes, one *could* supply

#define HANDLE_NONE ( (handle_t)NULL )

.... and tell the callers to compare against HANDLE_NONE, or one *could*
provide

#define HANDLE_IS_VALID(handle) ((handle) != NULL)

.... and tell them to use it to test the returned value -- but, I mean,
gosh, guys, how many self-invented hoops do you want to jump through
just so you can show off your insteps?

For opaque types, I find

typedef struct_internal ThroughAGlassDarkly
ThroughAGlassDarkly * whosTheFairestOfThemAll(void);

.... to be far more "natural." YMMV.
 
I

Ian Collins

I've used this -- and then I've backed away from it. It seems
to me that the function

handle_t getInternalData(const whatnot*);

.... often needs to be able to return an "I couldn't find one" result.
The "natural" thing to do in C is to return NULL (see malloc, fopen,
getenv, strstr, ...), but telling the caller to compare the returned
value to NULL "leaks the abstraction."

Yes, one *could* supply

#define HANDLE_NONE ( (handle_t)NULL )

.... and tell the callers to compare against HANDLE_NONE, or one *could*
provide

#define HANDLE_IS_VALID(handle) ((handle) != NULL)

.... and tell them to use it to test the returned value -- but, I mean,
gosh, guys, how many self-invented hoops do you want to jump through
just so you can show off your insteps?

For opaque types, I find

typedef struct_internal ThroughAGlassDarkly
ThroughAGlassDarkly * whosTheFairestOfThemAll(void);

.... to be far more "natural." YMMV.

The problem there is you can't declare an instance of a
ThroughAGlassDarkly, only a pointer to one. I guess this is why opaque
types tend to be pointers.
 
E

Eric Sosman

The problem there is you can't declare an instance of a
ThroughAGlassDarkly, only a pointer to one. I guess this is why opaque
types tend to be pointers.

What I'm saying is that the approach I prefer is to make the
opaque type a struct (most likely) and to have all the functions
that manage it deal in struct pointers. See, for example, the FILE
type: Functions like fopen() that "create" such objects return
pointers to them; functions that "consume" them accept pointers.
You can't create a free-standing FILE instance -- well, you *can*
because of what I suspect is an unintentional slip of the Standard,
but even so, the free-standing FILE can't be used for I/O.

That's the pattern I feel fits best with C -- except that I don't
feel bound to imitate the Standard's accident and allow free-standing
instances. One advantage of prohibiting free-standing instances is
that you can change the implementation, including changing the size of
an instance, without even recompiling the clients: All they get is a
pointer, and they don't know or need to know how many bytes are on the
other end of it.
 
M

Michael Press

Peter Nilsson said:
A better example would have made this clear.

That was the best example, from my point of view.
It elicited exactly the help I needed at that time:
namely that I need to parse const as I parse other
tokens in declarators; rather than trying to cobble
together something that works by trial and error,
as if const were an impediment to doing what I want.
Here's a contrived program that sorts words in the command
line via qsort, and shows how many times qsort compared each
word...

Read, understood, and filed. Thank you.
 

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

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top