Write-protecting objects addressed via indirect pointers

  • Follow


Hello everybody,

this question came up recently in a local C NG, and it's about
how to correctly apply the "const" qualifier, when the object
(i.e., a variable) is "double-referenced" (pointer to pointer
to object).

Example: say, we have a character array,
   char mystring[100];
and a pointer to some character in that array:
   char *ptr = &mystring[42];

Now we'd like to define a function, say "myparse()", that
takes "&ptr" as a parameter, so that it can advance "*ptr"
to the end of the current token.  However, we do *not* want
"myparse()" to modify the characters (i.e., the compiler should
emit an error message if things like "**ptr = 'x';" occurr),
*and* we don't want the compiler to emit a warning when
calling "myparse (&ptr);" due to incompatible pointer types.

I will intentionally _not_ show you all the variants I already
tried, in order to not confuse or "bias" you.  To summarize:

   void
   myparse ("type of pointer to pointer to constant chars" x)
   {
     ++(*x);  // should be allowed
     ++(**x); // should produce an error message
   }

   void
   foo (void)
   {
      char mystring[100];
      char *ptr = &mystring[42];

      myparse (&ptr); // should work w/out any cast or warning!
   }

Any ideas how to accomplish this in ISO-C (FWIW, I'm using gcc
4.3.2 and the options "-c -Wall -std=c99 -pedantic")?


Thanks,
mike

0
Reply misc_ (22) 10/20/2009 1:33:51 PM

Michael Schumacher <misc_@gmx.de> writes:

> Hello everybody,
>
> this question came up recently in a local C NG, and it's about
> how to correctly apply the "const" qualifier, when the object
> (i.e., a variable) is "double-referenced" (pointer to pointer
> to object).
>
> Example: say, we have a character array,
>    char mystring[100];
> and a pointer to some character in that array:
>    char *ptr = &mystring[42];
>
> Now we'd like to define a function, say "myparse()", that
> takes "&ptr" as a parameter, so that it can advance "*ptr"
> to the end of the current token.  However, we do *not* want
> "myparse()" to modify the characters (i.e., the compiler should
> emit an error message if things like "**ptr = 'x';" occurr),
> *and* we don't want the compiler to emit a warning when
> calling "myparse (&ptr);" due to incompatible pointer types.
>
> I will intentionally _not_ show you all the variants I already
> tried, in order to not confuse or "bias" you.  To summarize:
>
>    void
>    myparse ("type of pointer to pointer to constant chars" x)
>    {
>      ++(*x);  // should be allowed
>      ++(**x); // should produce an error message
>    }

maybe 

const char *  *  x


>
>    void
>    foo (void)
>    {
>       char mystring[100];
>       char *ptr = &mystring[42];
>
>       myparse (&ptr); // should work w/out any cast or warning!
>    }
>
> Any ideas how to accomplish this in ISO-C (FWIW, I'm using gcc
> 4.3.2 and the options "-c -Wall -std=c99 -pedantic")?
>
> Thanks,
> mike
>

-- 
"Avoid hyperbole at all costs, its the most destructive argument on
the planet" - Mark McIntyre in comp.lang.c
0
Reply rgrdev_ (1087) 10/20/2009 1:53:23 PM


In <9599504.5n5abhvfET@misc.albasani.net>, Michael Schumacher wrote:

> 
> Hello everybody,
> 
> this question came up recently in a local C NG, and it's about
> how to correctly apply the "const" qualifier, when the object
> (i.e., a variable) is "double-referenced" (pointer to pointer
> to object).
> 
> Example: say, we have a character array,
>    char mystring[100];
> and a pointer to some character in that array:
>    char *ptr = &mystring[42];
> 
> Now we'd like to define a function, say "myparse()", that
> takes "&ptr" as a parameter, so that it can advance "*ptr"
> to the end of the current token.  However, we do *not* want
> "myparse()" to modify the characters (i.e., the compiler should
> emit an error message if things like "**ptr = 'x';" occurr),
> *and* we don't want the compiler to emit a warning when
> calling "myparse (&ptr);" due to incompatible pointer types.

int myparse(const char **tokenptrptr, size_t max)
{
  int rc = default_return_code;

  const char *endptr = *tokenptrptr;
  /* move endptr as desired, and update return code -
     when done, do this: */
  *tokenptrptr = endptr;
  return rc;
}

(Using the temp like that makes the code much easier to read...)

<snip>

-- 
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
0
Reply rjh (10789) 10/20/2009 2:19:54 PM

Richard Heathfield wrote:

> In <9599504.5n5abhvfET@misc.albasani.net>, Michael Schumacher wrote:
> 
>> 
>> Hello everybody,
>> 
>> this question came up recently in a local C NG, and it's about
>> how to correctly apply the "const" qualifier, when the object
>> (i.e., a variable) is "double-referenced" (pointer to pointer
>> to object).
>> 
>> Example: say, we have a character array,
>>    char mystring[100];
>> and a pointer to some character in that array:
>>    char *ptr = &mystring[42];
>> 
>> Now we'd like to define a function, say "myparse()", that
>> takes "&ptr" as a parameter, so that it can advance "*ptr"
>> to the end of the current token.  However, we do *not* want
>> "myparse()" to modify the characters (i.e., the compiler should
>> emit an error message if things like "**ptr = 'x';" occurr),
>> *and* we don't want the compiler to emit a warning when
>> calling "myparse (&ptr);" due to incompatible pointer types.
> 
> int myparse(const char **tokenptrptr, size_t max)
> {
>   int rc = default_return_code;
> 
>   const char *endptr = *tokenptrptr;
>   /* move endptr as desired, and update return code -
>      when done, do this: */
>   *tokenptrptr = endptr;
>   return rc;
> }
> 
> (Using the temp like that makes the code much easier to read...)

Yep (well, maybe), but the question is whether/how it's possible
to declare "tokenptrptr" straight-forward, with no intermediate
steps, such that the two required constraints (error message when
modifying "**tokenptr", and no warning for "myparse (&tokenptr);")
will be satisfied.  Your solution sorta works around this, but it
still causes a warning for the function call.  Any other ideas?


mike

0
Reply misc_ (22) 10/20/2009 2:55:55 PM

Michael Schumacher <misc_@gmx.de> writes:

> Richard Heathfield wrote:
>
>> In <9599504.5n5abhvfET@misc.albasani.net>, Michael Schumacher wrote:
>> 
>>> 
>>> Hello everybody,
>>> 
>>> this question came up recently in a local C NG, and it's about
>>> how to correctly apply the "const" qualifier, when the object
>>> (i.e., a variable) is "double-referenced" (pointer to pointer
>>> to object).
>>> 
>>> Example: say, we have a character array,
>>>    char mystring[100];
>>> and a pointer to some character in that array:
>>>    char *ptr = &mystring[42];
>>> 
>>> Now we'd like to define a function, say "myparse()", that
>>> takes "&ptr" as a parameter, so that it can advance "*ptr"
>>> to the end of the current token.  However, we do *not* want
>>> "myparse()" to modify the characters (i.e., the compiler should
>>> emit an error message if things like "**ptr = 'x';" occurr),
>>> *and* we don't want the compiler to emit a warning when
>>> calling "myparse (&ptr);" due to incompatible pointer types.
>> 
>> int myparse(const char **tokenptrptr, size_t max)
>> {
>>   int rc = default_return_code;
>> 
>>   const char *endptr = *tokenptrptr;
>>   /* move endptr as desired, and update return code -
>>      when done, do this: */
>>   *tokenptrptr = endptr;
>>   return rc;
>> }
>> 
>> (Using the temp like that makes the code much easier to read...)
>
> Yep (well, maybe), but the question is whether/how it's possible
> to declare "tokenptrptr" straight-forward, with no intermediate
> steps, such that the two required constraints (error message when
> modifying "**tokenptr", and no warning for "myparse (&tokenptr);")
> will be satisfied.  Your solution sorta works around this, but it
> still causes a warning for the function call.  Any other ideas?
>
> mike
>

My previous suggestion worked for me. Did it not for you?

-- 
"Avoid hyperbole at all costs, its the most destructive argument on
the planet" - Mark McIntyre in comp.lang.c
0
Reply rgrdev_ (1087) 10/20/2009 2:58:33 PM

Richard wrote:

> 
> Michael Schumacher <misc_@gmx.de> writes:
> 
>> this question came up recently in a local C NG, and it's about
>> how to correctly apply the "const" qualifier, when the object
>> (i.e., a variable) is "double-referenced" (pointer to pointer
>> to object).
>>
>> Example: say, we have a character array,
>>    char mystring[100];
>> and a pointer to some character in that array:
>>    char *ptr = &mystring[42];
>>
>> Now we'd like to define a function, say "myparse()", that
>> takes "&ptr" as a parameter, so that it can advance "*ptr"
>> to the end of the current token.  However, we do *not* want
>> "myparse()" to modify the characters (i.e., the compiler should
>> emit an error message if things like "**ptr = 'x';" occurr),
>> *and* we don't want the compiler to emit a warning when
>> calling "myparse (&ptr);" due to incompatible pointer types.
>>
>> I will intentionally _not_ show you all the variants I already
>> tried, in order to not confuse or "bias" you.  To summarize:
>>
>>    void
>>    myparse ("type of pointer to pointer to constant chars" x)
>>    {
>>      ++(*x);  // should be allowed
>>      ++(**x); // should produce an error message
>>    }
> 
> maybe
> 
> const char *  *  x

Close, but no cigar: it causes a warning for the function call:

>>       myparse (&ptr); // should work w/out any cast or warning!

It should be perfectly okay for a compiler to emit a warning if
some constant value is passed as an lvalue, but why should it
gripe if a function voluntarily resigns (is that the right term?)
its right to modify an otherwise perfectly writeable object?


Cheers,
mike

0
Reply misc_ (22) 10/20/2009 3:09:12 PM

Michael Schumacher <misc_@gmx.de> writes:

> Richard wrote:
>
>> 
>> Michael Schumacher <misc_@gmx.de> writes:
>> 
>>> this question came up recently in a local C NG, and it's about
>>> how to correctly apply the "const" qualifier, when the object
>>> (i.e., a variable) is "double-referenced" (pointer to pointer
>>> to object).
>>>
>>> Example: say, we have a character array,
>>>    char mystring[100];
>>> and a pointer to some character in that array:
>>>    char *ptr = &mystring[42];
>>>
>>> Now we'd like to define a function, say "myparse()", that
>>> takes "&ptr" as a parameter, so that it can advance "*ptr"
>>> to the end of the current token.  However, we do *not* want
>>> "myparse()" to modify the characters (i.e., the compiler should
>>> emit an error message if things like "**ptr = 'x';" occurr),
>>> *and* we don't want the compiler to emit a warning when
>>> calling "myparse (&ptr);" due to incompatible pointer types.
>>>
>>> I will intentionally _not_ show you all the variants I already
>>> tried, in order to not confuse or "bias" you.  To summarize:
>>>
>>>    void
>>>    myparse ("type of pointer to pointer to constant chars" x)
>>>    {
>>>      ++(*x);  // should be allowed
>>>      ++(**x); // should produce an error message
>>>    }
>> 
>> maybe
>> 
>> const char *  *  x
>
> Close, but no cigar: it causes a warning for the function call:
>
>>>       myparse (&ptr); // should work w/out any cast or warning!
>
> It should be perfectly okay for a compiler to emit a warning if
> some constant value is passed as an lvalue, but why should it
> gripe if a function voluntarily resigns (is that the right term?)
> its right to modify an otherwise perfectly writeable object?

It's a warning. Want to lose it? Then:

,----
|    void myparse (const char * * x)
|    {
|      ++(*x);  // should be allowed
|      *(*x) =1; // should produce an error message
|    }
| 
|    int main ()
|    {
|       const char mystring[100];
|       const char *ptr = &mystring[42];
|       myparse (&ptr); // should work w/out any cast or warning!
|    }
| 
`----


>
> Cheers,
> mike
>

-- 
"Avoid hyperbole at all costs, its the most destructive argument on
the planet" - Mark McIntyre in comp.lang.c
0
Reply rgrdev_ (1087) 10/20/2009 3:20:39 PM

Michael Schumacher <misc_@gmx.de> writes:
> this question came up recently in a local C NG, and it's about
> how to correctly apply the "const" qualifier, when the object
> (i.e., a variable) is "double-referenced" (pointer to pointer
> to object).
>
> Example: say, we have a character array,
>    char mystring[100];
> and a pointer to some character in that array:
>    char *ptr = &mystring[42];
>
> Now we'd like to define a function, say "myparse()", that
> takes "&ptr" as a parameter, so that it can advance "*ptr"
> to the end of the current token.  However, we do *not* want
> "myparse()" to modify the characters (i.e., the compiler should
> emit an error message if things like "**ptr = 'x';" occurr),
> *and* we don't want the compiler to emit a warning when
> calling "myparse (&ptr);" due to incompatible pointer types.
>
> I will intentionally _not_ show you all the variants I already
> tried, in order to not confuse or "bias" you.  To summarize:
>
>    void
>    myparse ("type of pointer to pointer to constant chars" x)
>    {
>      ++(*x);  // should be allowed
>      ++(**x); // should produce an error message
>    }
>
>    void
>    foo (void)
>    {
>       char mystring[100];
>       char *ptr = &mystring[42];
>
>       myparse (&ptr); // should work w/out any cast or warning!
>    }
>
> Any ideas how to accomplish this in ISO-C (FWIW, I'm using gcc
> 4.3.2 and the options "-c -Wall -std=c99 -pedantic")?

Get yourself a copy of the "cdecl" program.

% cdecl
Type `help' or `?' for help
cdecl> declare x as const pointer to pointer to char
char ** const x
cdecl> exit
% 

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21474) 10/20/2009 3:30:46 PM

Richard wrote:

> Michael Schumacher <misc_@gmx.de> writes:
> 
>> Richard Heathfield wrote:
>>
>>> In <9599504.5n5abhvfET@misc.albasani.net>, Michael Schumacher wrote:
>>> 
>>>> 
>>>> Hello everybody,
>>>> 
>>>> this question came up recently in a local C NG, and it's about
>>>> how to correctly apply the "const" qualifier, when the object
>>>> (i.e., a variable) is "double-referenced" (pointer to pointer
>>>> to object).
>>>> 
>>>> Example: say, we have a character array,
>>>>    char mystring[100];
>>>> and a pointer to some character in that array:
>>>>    char *ptr = &mystring[42];
>>>> 
>>>> Now we'd like to define a function, say "myparse()", that
>>>> takes "&ptr" as a parameter, so that it can advance "*ptr"
>>>> to the end of the current token.  However, we do *not* want
>>>> "myparse()" to modify the characters (i.e., the compiler should
>>>> emit an error message if things like "**ptr = 'x';" occurr),
>>>> *and* we don't want the compiler to emit a warning when
>>>> calling "myparse (&ptr);" due to incompatible pointer types.
>>> 
>>> int myparse(const char **tokenptrptr, size_t max)
>>> {
>>>   int rc = default_return_code;
>>> 
>>>   const char *endptr = *tokenptrptr;
>>>   /* move endptr as desired, and update return code -
>>>      when done, do this: */
>>>   *tokenptrptr = endptr;
>>>   return rc;
>>> }
>>> 
>>> (Using the temp like that makes the code much easier to read...)
>>
>> Yep (well, maybe), but the question is whether/how it's possible
>> to declare "tokenptrptr" straight-forward, with no intermediate
>> steps, such that the two required constraints (error message when
>> modifying "**tokenptr", and no warning for "myparse (&tokenptr);")
>> will be satisfied.  Your solution sorta works around this, but it
>> still causes a warning for the function call.  Any other ideas?
> 
> My previous suggestion worked for me. Did it not for you?

It's not that the code wouldn't produce functional code, it's
that it still produces a warning for the function call (see my
comment to your other posting), which we'd like to get rid of
w/out using a cast or whatever.  I guess the problem boils down
to why "&char *" isn't perfectly compatible to "const char **"
if passed as a parameter to a function.  The other way round
(passing "&const char *" as a "char **" parameter) should of
course trigger a warning, as it would allow a function to
potentionally modify a constant value, but that's a different
story.


Cheers,
mike

0
Reply misc_ (22) 10/20/2009 3:34:47 PM

Keith Thompson <kst-u@mib.org> writes:

> Michael Schumacher <misc_@gmx.de> writes:
>> this question came up recently in a local C NG, and it's about
>> how to correctly apply the "const" qualifier, when the object
>> (i.e., a variable) is "double-referenced" (pointer to pointer
>> to object).
>>
>> Example: say, we have a character array,
>>    char mystring[100];
>> and a pointer to some character in that array:
>>    char *ptr = &mystring[42];
>>
>> Now we'd like to define a function, say "myparse()", that
>> takes "&ptr" as a parameter, so that it can advance "*ptr"
>> to the end of the current token.  However, we do *not* want
>> "myparse()" to modify the characters (i.e., the compiler should
>> emit an error message if things like "**ptr = 'x';" occurr),
>> *and* we don't want the compiler to emit a warning when
>> calling "myparse (&ptr);" due to incompatible pointer types.
>>
>> I will intentionally _not_ show you all the variants I already
>> tried, in order to not confuse or "bias" you.  To summarize:
>>
>>    void
>>    myparse ("type of pointer to pointer to constant chars" x)
>>    {
>>      ++(*x);  // should be allowed
>>      ++(**x); // should produce an error message
>>    }
>>
>>    void
>>    foo (void)
>>    {
>>       char mystring[100];
>>       char *ptr = &mystring[42];
>>
>>       myparse (&ptr); // should work w/out any cast or warning!
>>    }
>>
>> Any ideas how to accomplish this in ISO-C (FWIW, I'm using gcc
>> 4.3.2 and the options "-c -Wall -std=c99 -pedantic")?
>
> Get yourself a copy of the "cdecl" program.
>
> % cdecl
> Type `help' or `?' for help
> cdecl> declare x as const pointer to pointer to char
> char ** const x
> cdecl> exit
> % 

Except his char being pointed to is const too.


-- 
"Avoid hyperbole at all costs, its the most destructive argument on
the planet" - Mark McIntyre in comp.lang.c
0
Reply rgrdev_ (1087) 10/20/2009 3:38:41 PM

On Tue, 20 Oct 2009 15:33:51 +0200, Michael Schumacher wrote:

> *and* we don't want the compiler to emit a warning when
> calling "myparse (&ptr);" due to incompatible pointer types.

Tough luck. The types really are incompatible, and the compiler is obliged
to generate a diagnostic.

A pointer to immutable (const) data and a pointer to mutable data aren't
required to use the same representation. And while the compiler can
implicitly convert an individual pointer, it can't convert a whole array
of pointers.

If you pass a "char *" to a function expecting a "const char *", the
compiler can simply convert it. But if you pass a "char **" to a function
expecting a "const char **", it can't convert the array of "char *"s to
the array of "const char *"s which the callee is expecting.

0
Reply nobody (4805) 10/20/2009 4:03:42 PM

In <6198829.L34nNpotHS@misc.albasani.net>, Michael Schumacher wrote:

<my demo snipped>
 
> Yep (well, maybe), but the question is whether/how it's possible
> to declare "tokenptrptr" straight-forward, with no intermediate
> steps, such that the two required constraints (error message when
> modifying "**tokenptr", and no warning for "myparse (&tokenptr);")
> will be satisfied.  Your solution sorta works around this, but it
> still causes a warning for the function call.  Any other ideas?

This one works fine, no warnings. Here's a full program, and a 
subsequent make:

#include <stdio.h>

#define DEFAULT_RETURN_CODE 0
#define OVERRUN_AVOIDED 1

int myparse(const char **tokenptrptr, size_t max);

int main(void)
{
  char mystring[100] = "Now is the time for all"
                       " good men to come to the"
                       " aid of their party";
  const char *ptr = &mystring[12];
  printf("Current token: %c\n", *ptr);
  puts("Parsing");
  myparse(&ptr, 10);
  printf("Current token: %c\n", *ptr);
  return 0;
}

int myparse(const char **tokenptrptr, size_t max)
{
  int rc = DEFAULT_RETURN_CODE;
  size_t used = 0;
  const char *endptr = *tokenptrptr;
  /* move endptr as desired, and update return code -
     when done, do this: */
  /* demo */
  ++used;
  if(used >= max)
  {
    rc = OVERRUN_AVOIDED;
  }
  else
  {
    ++endptr;
  }
  *tokenptrptr = endptr;
  return rc;
}

gcc -W -Wall -ansi -pedantic -Wformat-nonliteral -Wcast-align 
-Wpointer-arith -Wbad-function-cast -Wmissing-prototypes 
-Wstrict-prototypes -Wmissing-declarations -Winline -Wundef 
-Wnested-externs -Wcast-qual -Wshadow -Wconversion -Wwrite-strings 
-ffloat-store -O2 -fno-builtin -g -pg -c -o foo.o foo.c
gcc -W -Wall -ansi -pedantic -Wformat-nonliteral -Wcast-align 
-Wpointer-arith -Wbad-function-cast -Wmissing-prototypes 
-Wstrict-prototypes -Wmissing-declarations -Winline -Wundef 
-Wnested-externs -Wcast-qual -Wshadow -Wconversion -Wwrite-strings 
-ffloat-store -O2 -fno-builtin -g -pg -o foo foo.o otdemo.o -lm 
-lncurses -lpcl

(i.e. no warnings)

It may be that you think I'm not pushing the compiler hard enough. 
What flags would you have me add?

-- 
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
0
Reply rjh (10789) 10/20/2009 4:04:14 PM

Michael Schumacher <misc_@gmx.de> writes:

> ...  Your solution sorta works around this, but it
> still causes a warning for the function call.  Any other ideas?

There is no perfect solution.  The type const char ** is the correct
one but the rules of C *require* a diagnostic when you pass an
argument of type char ** to such a function.  If you use a cast:

  myparse((const **)&ptr);

you can usually avoid the warning, but a compiler is permitted to warn
about anything at all and some might choose to do so for that call.

The other option is to use char ** as the parameter's type, but that
is not ideal either.

-- 
Ben.
0
Reply ben.usenet (6515) 10/20/2009 4:19:20 PM

Richard Heathfield <rjh@see.sig.invalid> writes:

> In <6198829.L34nNpotHS@misc.albasani.net>, Michael Schumacher wrote:
>
> <my demo snipped>
>  
>> Yep (well, maybe), but the question is whether/how it's possible
>> to declare "tokenptrptr" straight-forward, with no intermediate
>> steps, such that the two required constraints (error message when
>> modifying "**tokenptr", and no warning for "myparse (&tokenptr);")
>> will be satisfied.  Your solution sorta works around this, but it
>> still causes a warning for the function call.  Any other ideas?
>
> This one works fine, no warnings. Here's a full program, and a 
> subsequent make:
>
<snip>
> int myparse(const char **tokenptrptr, size_t max);
>
> int main(void)
> {
>   char mystring[100] = "Now is the time for all"
>                        " good men to come to the"
>                        " aid of their party";
>   const char *ptr = &mystring[12];
>   printf("Current token: %c\n", *ptr);
>   puts("Parsing");
>   myparse(&ptr, 10);

I don't think this really addresses the problem.  If you already have
a const char * (or const chars to point to as someone else has
suggested) then there is no problem.

I suspect the OP has pointer that is used to modify the string (that
is why it is a char *) and would like to pass it to a function that
promises not to change any.  Are you suggesting bracketing the call
with a new declaration of a const char * temporary and an assignment
back to the original pointer just to avoid a cast?  This is one place
I'd just use a cast.

>   printf("Current token: %c\n", *ptr);
>   return 0;
> }

<snip>
-- 
Ben.
0
Reply ben.usenet (6515) 10/20/2009 4:50:10 PM

Nobody wrote:
> On Tue, 20 Oct 2009 15:33:51 +0200, Michael Schumacher wrote:
> 
>> *and* we don't want the compiler to emit a warning when
>> calling "myparse (&ptr);" due to incompatible pointer types.
> 
> Tough luck. The types really are incompatible, and the compiler is obliged
> to generate a diagnostic.
> 
> A pointer to immutable (const) data and a pointer to mutable data aren't
> required to use the same representation.

Actually, they are: "Similarly, pointers to qualified or unqualified 
versions of compatible types shall have the same representation and 
alignment requirements." 6.2.5p15.

The problem is not that they don't have the same representation, but the 
fact that they're not required to be compatible, as you stated in your 
first paragraph.
0
Reply jameskuyper (5159) 10/20/2009 5:21:15 PM

Richard Heathfield wrote:
> In <6198829.L34nNpotHS@misc.albasani.net>, Michael Schumacher wrote:
> 
> <my demo snipped>
>  
>> Yep (well, maybe), but the question is whether/how it's possible
>> to declare "tokenptrptr" straight-forward, with no intermediate
>> steps, such that the two required constraints (error message when
>> modifying "**tokenptr", and no warning for "myparse (&tokenptr);")
>> will be satisfied.  Your solution sorta works around this, but it
>> still causes a warning for the function call.  Any other ideas?
> 
> This one works fine, no warnings. Here's a full program, and a 
> subsequent make:
> 
> #include <stdio.h>
> 
> #define DEFAULT_RETURN_CODE 0
> #define OVERRUN_AVOIDED 1
> 
> int myparse(const char **tokenptrptr, size_t max);
> 
> int main(void)
> {
>   char mystring[100] = "Now is the time for all"
>                        " good men to come to the"
>                        " aid of their party";

What he wants (and C does not allow) is to be free to leave out the 
'const' on the following declaration:

>   const char *ptr = &mystring[12];
>   printf("Current token: %c\n", *ptr);
>   puts("Parsing");
>   myparse(&ptr, 10);
>   printf("Current token: %c\n", *ptr);
>   return 0;
> }

I understand why he wants it; after all, we can pass a char* pointer to 
a function declared as taking a const char* argument; it's not 
immediately obvious why C couldn't allow passing a char** argument to 
function declared as taking a const char**.

6.5.2.2p2: "Each argument shall have a type such that its value may be 
assigned to an object with the unqualified version of the type of its 
corresponding parameter".

Thus, we must look to the constrains on assignment:
6.5.16.1p1: "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;"

What's not immediately obvious is that the qualifiers referred to here 
are only the top level qualifiers of the pointed-at type. A char* can be 
passed to a function expecting a const char*, because "const char" has 
all of the qualifiers that "char" has (i.e. none). However, a char** 
cannot be passed to a function that expects "const char**", because the 
'const' is a part of pointed-at type, rather than qualifying the 
pointed-at type directly. Since the pointed-at type, char*, is 
incompatible with const char*, assignment of a char** to a const char** 
is a constraint violation.

<http://c-faq.com/ansi/constmismatch.html> provides an explanation for 
this rule, but it's a tricky explanation, and one I always have trouble 
reconstructing from memory.
0
Reply jameskuyper (5159) 10/20/2009 5:27:02 PM

Richard Heathfield wrote:

> In <6198829.L34nNpotHS@misc.albasani.net>, Michael Schumacher wrote:
> 
> <my demo snipped>
>  
>> Yep (well, maybe), but the question is whether/how it's possible
>> to declare "tokenptrptr" straight-forward, with no intermediate
>> steps, such that the two required constraints (error message when
>> modifying "**tokenptr", and no warning for "myparse (&tokenptr);")
>> will be satisfied.  Your solution sorta works around this, but it
>> still causes a warning for the function call.  Any other ideas?
> 
> This one works fine, no warnings. Here's a full program, and a
> subsequent make:
> 
> #include <stdio.h>
> 
> #define DEFAULT_RETURN_CODE 0
> #define OVERRUN_AVOIDED 1
> 
> int myparse(const char **tokenptrptr, size_t max);

Yes, I think this is the correct declaration for "tokenptrptr", as
trying, e.g., "(**tokenptr)++;" gives an error.  First restriction
solved.  But...

> int main(void)
> {
>   char mystring[100] = "Now is the time for all"
>                        " good men to come to the"
>                        " aid of their party";
>   const char *ptr = &mystring[12];
|   ^^^^^^^^^^^^^^^
>   printf("Current token: %c\n", *ptr);
>   puts("Parsing");
>   myparse(&ptr, 10);
|           ^^^^

In this case, you're passing the address of a "const char *" as a
"const char **" parameter, which is obviously perfectly compatible
and thus doesn't cause the compiler to emit a warning.  Omit the
"const" from the "const char *ptr = ..."-declaration, and gcc will
issue a warning for "myparse (&ptr, 10);" -- but that's exactly the
point of my initial posting!  [Maybe your "main()" above would like
to manipulate particular characters in "mystring" via "*ptr = 'x';",
in which case you can't declare "ptr" as a "const char *", but that
is not significant for the issue at hand.]

Again, this is not just about getting functional code -- it is about
achieving this without any warnings, work-arounds, intermediate
steps/variables or casts, by just using a straight-forward ISO-C
declaration for a pointer-to-pointer-parameter, where the function
doesn't wish (or isn't allowed to) modify the underlying objects,
but just the actual pointer to it.  Is that really an impossible
thing to do in C?

> [gcc options]
> It may be that you think I'm not pushing the compiler hard enough.
> What flags would you have me add?

An additional "-std=c99" wouldn't be bad; however, I don't think it
would help in this particular regard, either.  Anyway, thanks a lot
for your (and all the other's) help, I really apprectiate it!


Cheers,
mike

0
Reply misc_ (22) 10/20/2009 5:32:27 PM

Michael Schumacher <misc_@gmx.de> writes:

> [want to use address of a (char*) as a (const char **)]

Someone already explained that this doesn't work, but no one (as
far as I've seen) has explained why.  Consider what happens if it
were legal:

   void foo( const char **x ){
       static const char xyz[] = "non-writeable";
       *x = &xyz[0];
   }

   char *p;

   foo( &p );   // suppose this step were legal
   *p = ' ';    // legal, but now BAD!

The problem is that, after the call to foo(), p has a pointer to
somewhere that can't be modified, but p doesn't "know" that;  hence
p allows storing something into a place that can't have something
stored in it.

If you really want to pass a &(char*) as an argument, rather than
a &(const char*), I think you'll have to resign yourself to using
a (char**) parameter, and arrange the const-ness for code inside
the function using a local variable instead of using the parameter
directly.  It's possible to do this in a way that's safe and not
too bad, although it does of course take a few more lines of code,
and storing back the internal value may look a little strange.
0
Reply txr1 (1213) 10/20/2009 5:45:56 PM

James Kuyper <jameskuyper@verizon.net> writes:

> Nobody wrote:
>> On Tue, 20 Oct 2009 15:33:51 +0200, Michael Schumacher wrote:
>>
>>> *and* we don't want the compiler to emit a warning when
>>> calling "myparse (&ptr);" due to incompatible pointer types.
>>
>> Tough luck. The types really are incompatible, and the compiler is obliged
>> to generate a diagnostic.
>>
>> A pointer to immutable (const) data and a pointer to mutable data aren't
>> required to use the same representation.
>
> Actually, they are: "Similarly, pointers to qualified or unqualified
> versions of compatible types shall have the same representation and
> alignment requirements." 6.2.5p15.
>
> The problem is not that they don't have the same representation, but
> the fact that they're not required to be compatible, as you stated in
> your first paragraph.

The types (char *) and (const char*) have to use the same
representation, but the types (char **) and (const char**)
do not.  Presumably the types being talked about in the
previous posting are these, despite the poor choice
of phrasing in the last sentence.
0
Reply txr1 (1213) 10/20/2009 5:58:46 PM

Tim Rentsch wrote:

> Michael Schumacher <misc_@gmx.de> writes:
> 
>> [want to use address of a (char*) as a (const char **)]
> 
> Someone already explained that this doesn't work, but no one (as
> far as I've seen) has explained why.  Consider what happens if it
> were legal: [...]

Okay, thanks to your and James' explanations, I can now clearly
see *why* this "desire" is impossible -- I can certainly live with
that outcome, as there are various "work-arounds" available, but
it feels a lot better to exactly *know* why these are necessary
in the first place...  :-)

I'd like to thank all of you for your quick help!


Cheers,
mike

0
Reply misc_ (22) 10/20/2009 6:09:11 PM

In <1634208.4Ke4WvTzxD@misc.albasani.net>, Michael Schumacher wrote:

<snip>

> Omit the
> "const" from the "const char *ptr = ..."-declaration, and gcc will
> issue a warning for "myparse (&ptr, 10);" -- but that's exactly the
> point of my initial posting!

*Now* I understand. Sorry to have been so dense.

As far as I can see, what you want is not formally possible - but it 
is very likely that you will do no harm by stepping around the 
problem with a cast. (That probably isn't what you want, but I think 
it's the best - or least worst - answer there is.)

<snip>

-- 
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
0
Reply rjh (10789) 10/20/2009 6:56:39 PM

Michael Schumacher wrote:

<snip>

> w/out using a cast or whatever.  I guess the problem boils down
> to why "&char *" isn't perfectly compatible to "const char **"
> if passed as a parameter to a function.  The other way round
> (passing "&const char *" as a "char **" parameter) should of
> course trigger a warning, as it would allow a function to
> potentionally modify a constant value, but that's a different
> story.

Ah well, that is a FAQ, if you go to http://c-faq.com/ and have a look 
at question 11.10, which explains in detail why the C standard does not 
allow this.
-- 
Flash Gordon
0
Reply smap (838) 10/20/2009 10:12:50 PM

21 Replies
35 Views

(page loaded in 0.265 seconds)


Reply: