gcc: pointer to array

  • Follow


Hi all,

I have a question regarding the gcc behavior (gcc version 3.3.4).

On the following test program it emits a warning:
#include <stdio.h>

int aInt2[6] = {0,1,2,4,9,16};
int aInt3[5] = {0,1,2,4,9};

void print1 (const int* p, size_t cnt)
{
  while (cnt--)
    printf ("%d\n", *p++);
}

void print2 (const int (*p)[5])
{
  size_t cnt;
#if 0
  // prohibited:
  (*p)[0]=0;
#endif
  for (cnt=0; cnt<5; cnt++)
    printf ("%d\n", (*p)[cnt]);
}

int main()
{
  printf ("test begin\n");

  print1 (aInt2, sizeof(aInt2)/sizeof(aInt2[0]));
  print2 (&aInt3); // <-- warns here

  printf ("test end\n");
  return 0;
}

The warning is:
MOD.c: In function `main':
MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type

Why is that? How is this different from using print1 (aInt2, ...)? All I
want to do is to explicitly show that the pointer is to an array of 5
entries and ensure that it's not modified in anyway inside print2(). However
the #if 0'd assignment operator inside print2() is correctly handled by
gcc -- an error is generated:
MOD.c: In function `print2':
MOD.c:: error: assignment of read-only location
What's wrong with gcc?

There's another thing about the pointer to the array is... The compiler
doesn't generate a warning if I change in print2()
  for (cnt=0; cnt<5; cnt++)
to
  for (cnt=0; cnt<6; cnt++)
and lets me access (*p)[5], though I think at least a warning should be
generated here.
It doesn't warn me if I put
  aInt3[6] = 0;
into main(). But in both cases the compiler "knows" the real size of the
array, still no warning...
Is this OK???

Thanks,
Alex
P.S. I compile it as gcc MOD.c -Wall -oMOD.exe


0
Reply alexfru (352) 7/10/2005 3:39:03 PM

    Surely.

    "&alnt3" is the same as "alnt3", since alnt3 is an array.

    So, you pass int* to print2, and it expects an array of int* - i.e. int**.

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com

"Alexei A. Frounze" <alexfru@chat.ru> wrote in message
news:3jctoiFpboltU1@individual.net...
> Hi all,
>
> I have a question regarding the gcc behavior (gcc version 3.3.4).
>
> On the following test program it emits a warning:
> #include <stdio.h>
>
> int aInt2[6] = {0,1,2,4,9,16};
> int aInt3[5] = {0,1,2,4,9};
>
> void print1 (const int* p, size_t cnt)
> {
>   while (cnt--)
>     printf ("%d\n", *p++);
> }
>
> void print2 (const int (*p)[5])
> {
>   size_t cnt;
> #if 0
>   // prohibited:
>   (*p)[0]=0;
> #endif
>   for (cnt=0; cnt<5; cnt++)
>     printf ("%d\n", (*p)[cnt]);
> }
>
> int main()
> {
>   printf ("test begin\n");
>
>   print1 (aInt2, sizeof(aInt2)/sizeof(aInt2[0]));
>   print2 (&aInt3); // <-- warns here
>
>   printf ("test end\n");
>   return 0;
> }
>
> The warning is:
> MOD.c: In function `main':
> MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type
>
> Why is that? How is this different from using print1 (aInt2, ...)? All I
> want to do is to explicitly show that the pointer is to an array of 5
> entries and ensure that it's not modified in anyway inside print2(). However
> the #if 0'd assignment operator inside print2() is correctly handled by
> gcc -- an error is generated:
> MOD.c: In function `print2':
> MOD.c:: error: assignment of read-only location
> What's wrong with gcc?
>
> There's another thing about the pointer to the array is... The compiler
> doesn't generate a warning if I change in print2()
>   for (cnt=0; cnt<5; cnt++)
> to
>   for (cnt=0; cnt<6; cnt++)
> and lets me access (*p)[5], though I think at least a warning should be
> generated here.
> It doesn't warn me if I put
>   aInt3[6] = 0;
> into main(). But in both cases the compiler "knows" the real size of the
> array, still no warning...
> Is this OK???
>
> Thanks,
> Alex
> P.S. I compile it as gcc MOD.c -Wall -oMOD.exe
>
>


0
Reply maxim19 (63) 7/10/2005 3:48:00 PM


"Maxim S. Shatskih" <maxim@storagecraft.com> wrote in message
news:darftd$1843$1@gavrilo.mtu.ru...
>     Surely.
>
>     "&alnt3" is the same as "alnt3", since alnt3 is an array.

It's not. :) The values are identical, but the types aren't, strictly
speaking. If you pass &aInt2 to print1(), you'll have a problem. You won't,
if you pass aInt2 or &aInt2[0]. This is clear. And it's not the problem.

>     So, you pass int* to print2, and it expects an array of int* - i.e.
int**.

int (*p)[5] declares p as a pointer to array of 5 elements of type int. It's
not an array of 5 pointers, nor a pointer to such array!

Alex

> --
> Maxim Shatskih, Windows DDK MVP
> StorageCraft Corporation
> maxim@storagecraft.com
> http://www.storagecraft.com
>
> "Alexei A. Frounze" <alexfru@chat.ru> wrote in message
> news:3jctoiFpboltU1@individual.net...
> > Hi all,
> >
> > I have a question regarding the gcc behavior (gcc version 3.3.4).
> >
> > On the following test program it emits a warning:
> > #include <stdio.h>
> >
> > int aInt2[6] = {0,1,2,4,9,16};
> > int aInt3[5] = {0,1,2,4,9};
> >
> > void print1 (const int* p, size_t cnt)
> > {
> >   while (cnt--)
> >     printf ("%d\n", *p++);
> > }
> >
> > void print2 (const int (*p)[5])
> > {
> >   size_t cnt;
> > #if 0
> >   // prohibited:
> >   (*p)[0]=0;
> > #endif
> >   for (cnt=0; cnt<5; cnt++)
> >     printf ("%d\n", (*p)[cnt]);
> > }
> >
> > int main()
> > {
> >   printf ("test begin\n");
> >
> >   print1 (aInt2, sizeof(aInt2)/sizeof(aInt2[0]));
> >   print2 (&aInt3); // <-- warns here
> >
> >   printf ("test end\n");
> >   return 0;
> > }
> >
> > The warning is:
> > MOD.c: In function `main':
> > MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer
type
> >
> > Why is that? How is this different from using print1 (aInt2, ...)? All I
> > want to do is to explicitly show that the pointer is to an array of 5
> > entries and ensure that it's not modified in anyway inside print2().
However
> > the #if 0'd assignment operator inside print2() is correctly handled by
> > gcc -- an error is generated:
> > MOD.c: In function `print2':
> > MOD.c:: error: assignment of read-only location
> > What's wrong with gcc?
> >
> > There's another thing about the pointer to the array is... The compiler
> > doesn't generate a warning if I change in print2()
> >   for (cnt=0; cnt<5; cnt++)
> > to
> >   for (cnt=0; cnt<6; cnt++)
> > and lets me access (*p)[5], though I think at least a warning should be
> > generated here.
> > It doesn't warn me if I put
> >   aInt3[6] = 0;
> > into main(). But in both cases the compiler "knows" the real size of the
> > array, still no warning...
> > Is this OK???
> >
> > Thanks,
> > Alex
> > P.S. I compile it as gcc MOD.c -Wall -oMOD.exe
> >
> >
>
>


0
Reply alexfru (352) 7/10/2005 3:51:22 PM

Alexei A. Frounze wrote:
>
> void print2 (const int (*p)[5])

arg1 of print2 seems a pointer to a function... isn't it?
-- 
daniele_athome
0
Reply daniele_athomeNOSPAM (3) 7/10/2005 3:54:32 PM

"daniele_athome" <daniele_athomeNOSPAM@fastwebnet.it> wrote in message
news:twbAe.11694$b93.2851@tornado.fastwebnet.it...
> Alexei A. Frounze wrote:
> >
> > void print2 (const int (*p)[5])
>
> arg1 of print2 seems a pointer to a function... isn't it?

It looks very similar, but it's not :) The [] brackets tell the array, not
function, for which they should've been (), like so:
int (*p)(/*argument(s)*/)

Alex


0
Reply alexfru (352) 7/10/2005 4:08:30 PM

daniele_athome wrote:
> Alexei A. Frounze wrote:
> 
>>void print2 (const int (*p)[5])
> 
> 
> arg1 of print2 seems a pointer to a function... isn't it?

No. p is a pointer to array 5 of const int. Alexei gets this wrong too.

-- 
Joe Wright
"Everything should be made as simple as possible, but not simpler."
                     --- Albert Einstein ---
0
Reply joewwright (1737) 7/10/2005 4:22:00 PM

"Joe Wright" <joewwright@comcast.net> wrote in message
news:0uqdnVtZRKW11kzfRVn-1w@comcast.com...
> daniele_athome wrote:
> > Alexei A. Frounze wrote:
> >
> >>void print2 (const int (*p)[5])
> >
> >
> > arg1 of print2 seems a pointer to a function... isn't it?
>
> No. p is a pointer to array 5 of const int. Alexei gets this wrong too.

Excuse me, but what did I get wrong? Isn't what you're trying to say the
same as in my original post (excerpt): "All I
want to do is to explicitly show that the pointer is to an array of 5
entries and ensure that it's not modified in anyway inside print2()."?

Alex


0
Reply alexfru (352) 7/10/2005 4:26:57 PM

"Alexei A. Frounze" <alexfru@chat.ru> wrote in message
news:3jd0i6Fphgt2U1@individual.net...
> "Joe Wright" <joewwright@comcast.net> wrote in message
> news:0uqdnVtZRKW11kzfRVn-1w@comcast.com...
> > daniele_athome wrote:
> > > Alexei A. Frounze wrote:
> > >
> > >>void print2 (const int (*p)[5])
> > >
> > >
> > > arg1 of print2 seems a pointer to a function... isn't it?
> >
> > No. p is a pointer to array 5 of const int. Alexei gets this wrong too.
>
> Excuse me, but what did I get wrong? Isn't what you're trying to say the
> same as in my original post (excerpt): "All I
> want to do is to explicitly show that the pointer is to an array of 5
> entries and ensure that it's not modified in anyway inside print2()."?
>
> Alex

To clarify further, "... and ensure that *the array (not the pointer)* it's
not modified in anyway inside print2()."
Alex


0
Reply alexfru (352) 7/10/2005 4:28:18 PM

print2 expects pointer to constant array so
how about declaring aInt3 to constant or modify print2 parameter list...


- jt




Alexei A. Frounze wrote:
> Hi all,
> 
> I have a question regarding the gcc behavior (gcc version 3.3.4).
> 
> On the following test program it emits a warning:
> #include <stdio.h>
> 
> int aInt2[6] = {0,1,2,4,9,16};
> int aInt3[5] = {0,1,2,4,9};
> 
> void print1 (const int* p, size_t cnt)
> {
>   while (cnt--)
>     printf ("%d\n", *p++);
> }
> 
> void print2 (const int (*p)[5])
> {
>   size_t cnt;
> #if 0
>   // prohibited:
>   (*p)[0]=0;
> #endif
>   for (cnt=0; cnt<5; cnt++)
>     printf ("%d\n", (*p)[cnt]);
> }
> 
> int main()
> {
>   printf ("test begin\n");
> 
>   print1 (aInt2, sizeof(aInt2)/sizeof(aInt2[0]));
>   print2 (&aInt3); // <-- warns here
> 
>   printf ("test end\n");
>   return 0;
> }
> 
> The warning is:
> MOD.c: In function `main':
> MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type
> 
> Why is that? How is this different from using print1 (aInt2, ...)? All I
> want to do is to explicitly show that the pointer is to an array of 5
> entries and ensure that it's not modified in anyway inside print2(). However
> the #if 0'd assignment operator inside print2() is correctly handled by
> gcc -- an error is generated:
> MOD.c: In function `print2':
> MOD.c:: error: assignment of read-only location
> What's wrong with gcc?
> 
> There's another thing about the pointer to the array is... The compiler
> doesn't generate a warning if I change in print2()
>   for (cnt=0; cnt<5; cnt++)
> to
>   for (cnt=0; cnt<6; cnt++)
> and lets me access (*p)[5], though I think at least a warning should be
> generated here.
> It doesn't warn me if I put
>   aInt3[6] = 0;
> into main(). But in both cases the compiler "knows" the real size of the
> array, still no warning...
> Is this OK???
> 
> Thanks,
> Alex
> P.S. I compile it as gcc MOD.c -Wall -oMOD.exe
> 
> 
0
Reply TEJ1 (7) 7/10/2005 5:12:05 PM

Yes, this would work fine if aInt3[] were declared as const array... But the
problem is that the compiler allows pointers to consts to non-const data but
warns on pointers to const array to non-const data. That's why I presented
both things in the test program. Kinda odd, don't you think so? I mean, I
can put there a type cast to get rid of the warning, but this warning in its
very core is stupid. Why "const int*" should be better than or in any other
way differ from "const (*int)[]"? Where's the rationale? I don't see it. Any
idea?

Alex

"Tommi Johnsson" <TEJ@Merlin.Mirrorland> wrote in message
news:dMcAe.3568$ds2.3056@reader1.news.jippii.net...
>
> print2 expects pointer to constant array so
> how about declaring aInt3 to constant or modify print2 parameter list...
>
>
> - jt
>
>
>
>
> Alexei A. Frounze wrote:
> > Hi all,
> >
> > I have a question regarding the gcc behavior (gcc version 3.3.4).
> >
> > On the following test program it emits a warning:
> > #include <stdio.h>
> >
> > int aInt2[6] = {0,1,2,4,9,16};
> > int aInt3[5] = {0,1,2,4,9};
> >
> > void print1 (const int* p, size_t cnt)
> > {
> >   while (cnt--)
> >     printf ("%d\n", *p++);
> > }
> >
> > void print2 (const int (*p)[5])
> > {
> >   size_t cnt;
> > #if 0
> >   // prohibited:
> >   (*p)[0]=0;
> > #endif
> >   for (cnt=0; cnt<5; cnt++)
> >     printf ("%d\n", (*p)[cnt]);
> > }
> >
> > int main()
> > {
> >   printf ("test begin\n");
> >
> >   print1 (aInt2, sizeof(aInt2)/sizeof(aInt2[0]));
> >   print2 (&aInt3); // <-- warns here
> >
> >   printf ("test end\n");
> >   return 0;
> > }
> >
> > The warning is:
> > MOD.c: In function `main':
> > MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer
type
> >
> > Why is that? How is this different from using print1 (aInt2, ...)? All I
> > want to do is to explicitly show that the pointer is to an array of 5
> > entries and ensure that it's not modified in anyway inside print2().
However
> > the #if 0'd assignment operator inside print2() is correctly handled by
> > gcc -- an error is generated:
> > MOD.c: In function `print2':
> > MOD.c:: error: assignment of read-only location
> > What's wrong with gcc?
> >
> > There's another thing about the pointer to the array is... The compiler
> > doesn't generate a warning if I change in print2()
> >   for (cnt=0; cnt<5; cnt++)
> > to
> >   for (cnt=0; cnt<6; cnt++)
> > and lets me access (*p)[5], though I think at least a warning should be
> > generated here.
> > It doesn't warn me if I put
> >   aInt3[6] = 0;
> > into main(). But in both cases the compiler "knows" the real size of the
> > array, still no warning...
> > Is this OK???
> >
> > Thanks,
> > Alex
> > P.S. I compile it as gcc MOD.c -Wall -oMOD.exe
> >
> >


0
Reply alexfru (352) 7/10/2005 5:23:14 PM


Alexei A. Frounze wrote:
>
> I have a question regarding the gcc behavior (gcc version 3.3.4).
> 
> On the following test program it emits a warning:
> #include <stdio.h>
> 
> int aInt2[6] = {0,1,2,4,9,16};
> int aInt3[5] = {0,1,2,4,9};
> 

> void print2 (const int (*p)[5])
> {
>   size_t cnt;
> #if 0
>   // prohibited:
>   (*p)[0]=0;
> #endif
>   for (cnt=0; cnt<5; cnt++)
>     printf ("%d\n", (*p)[cnt]);
> }
> 
> int main()
> {
>   printf ("test begin\n");
> 
>   print1 (aInt2, sizeof(aInt2)/sizeof(aInt2[0]));
>   print2 (&aInt3); // <-- warns here
> 
>   printf ("test end\n");
>   return 0;
> }
> 
> The warning is:
> MOD.c: In function `main':
> MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type
> 
> Why is that? 

Try removing the const keyword.
Example:

#include <stdio.h>

void print2 (int (*p)[5])
{
    size_t i;

    for(i = 0; i < 5; i++)
       printf ("%d\n", (*p)[i]);
    return;
}

int main(void)
{
    int p[5] = {1,2,3,4,5};
    print2(&p);
    return 0;
}


-- 
Al Bowers
Tampa, Fl USA
mailto: xabowers@myrapidsys.com (remove the x to send email)
http://www.geocities.com/abowers822/

0
Reply abowers (46) 7/10/2005 5:57:43 PM

"Al Bowers" <abowers@myrapidsys.com> wrote in message
news:3jd5spFpjotoU1@individual.net...
....
> Try removing the const keyword.
....

But I want to keep it. :)

Alex


0
Reply alexfru (352) 7/10/2005 6:29:03 PM

Alexei A. Frounze wrote:
> "Alexei A. Frounze" <alexfru@chat.ru> wrote in message
> news:3jd0i6Fphgt2U1@individual.net...
> 
>>"Joe Wright" <joewwright@comcast.net> wrote in message
>>news:0uqdnVtZRKW11kzfRVn-1w@comcast.com...
>>
>>>daniele_athome wrote:
>>>
>>>>Alexei A. Frounze wrote:
>>>>
>>>>
>>>>>void print2 (const int (*p)[5])
>>>>
>>>>
>>>>arg1 of print2 seems a pointer to a function... isn't it?
>>>
>>>No. p is a pointer to array 5 of const int. Alexei gets this wrong too.
>>
>>Excuse me, but what did I get wrong? Isn't what you're trying to say the
>>same as in my original post (excerpt): "All I
>>want to do is to explicitly show that the pointer is to an array of 5
>>entries and ensure that it's not modified in anyway inside print2()."?
>>
>>Alex
> 
> 
> To clarify further, "... and ensure that *the array (not the pointer)* it's
> not modified in anyway inside print2()."
> Alex

Sorry Alex, I jumped on you in error. It would seem to be a 'const' 
issue. If I remove the const qualifier from the print2 parameter it 
works. Or if I add const to the array definition..

   const int aInt3[5] = {0, 1, 2, 4, 9};

it works too. Go figure. I don't have a good handle on 'const'.

-- 
Joe Wright
"Everything should be made as simple as possible, but not simpler."
                     --- Albert Einstein ---
0
Reply joewwright (1737) 7/10/2005 6:36:36 PM

Alexei A. Frounze wrote:
> Yes, this would work fine if aInt3[] were declared as const array... But the
> problem is that the compiler allows pointers to consts to non-const data but
> warns on pointers to const array to non-const data. That's why I presented
> both things in the test program. Kinda odd, don't you think so? I mean, I
> can put there a type cast to get rid of the warning, but this warning in its
> very core is stupid. Why "const int*" should be better than or in any other
> way differ from "const (*int)[]"? Where's the rationale? I don't see it. Any
> idea?

i think, it's because types differs from each other...
in print1 you declare `p` as an pointer to const qualified int.
-->> pointed value is const qualified
and
in print2 you declare `p` as an pointer to const qualified array of ints
-->> pointed value is array which elements are const qualified.

i see `const int **p` more like `const int (*p)[5]` than `const int *p` 
in this case, because array decays to pointer in most cases.

- jt


> 
> "Tommi Johnsson" <TEJ@Merlin.Mirrorland> wrote in message
> news:dMcAe.3568$ds2.3056@reader1.news.jippii.net...
> 
>>print2 expects pointer to constant array so
>>how about declaring aInt3 to constant or modify print2 parameter list...
>>
>>
>>- jt
>>
>>
>>
>>
>>Alexei A. Frounze wrote:
>>
>>>Hi all,
>>>
>>>I have a question regarding the gcc behavior (gcc version 3.3.4).
>>>
>>>On the following test program it emits a warning:
>>>#include <stdio.h>
>>>
>>>int aInt2[6] = {0,1,2,4,9,16};
>>>int aInt3[5] = {0,1,2,4,9};
>>>
>>>void print1 (const int* p, size_t cnt)
>>>{
>>>  while (cnt--)
>>>    printf ("%d\n", *p++);
>>>}
>>>
>>>void print2 (const int (*p)[5])
>>>{
>>>  size_t cnt;
>>>#if 0
>>>  // prohibited:
>>>  (*p)[0]=0;
>>>#endif
>>>  for (cnt=0; cnt<5; cnt++)
>>>    printf ("%d\n", (*p)[cnt]);
>>>}
>>>
>>>int main()
>>>{
>>>  printf ("test begin\n");
>>>
>>>  print1 (aInt2, sizeof(aInt2)/sizeof(aInt2[0]));
>>>  print2 (&aInt3); // <-- warns here
>>>
>>>  printf ("test end\n");
>>>  return 0;
>>>}
>>>
>>>The warning is:
>>>MOD.c: In function `main':
>>>MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer
> 
> type
> 
>>>Why is that? How is this different from using print1 (aInt2, ...)? All I
>>>want to do is to explicitly show that the pointer is to an array of 5
>>>entries and ensure that it's not modified in anyway inside print2().
> 
> However
> 
>>>the #if 0'd assignment operator inside print2() is correctly handled by
>>>gcc -- an error is generated:
>>>MOD.c: In function `print2':
>>>MOD.c:: error: assignment of read-only location
>>>What's wrong with gcc?
>>>
>>>There's another thing about the pointer to the array is... The compiler
>>>doesn't generate a warning if I change in print2()
>>>  for (cnt=0; cnt<5; cnt++)
>>>to
>>>  for (cnt=0; cnt<6; cnt++)
>>>and lets me access (*p)[5], though I think at least a warning should be
>>>generated here.
>>>It doesn't warn me if I put
>>>  aInt3[6] = 0;
>>>into main(). But in both cases the compiler "knows" the real size of the
>>>array, still no warning...
>>>Is this OK???
>>>
>>>Thanks,
>>>Alex
>>>P.S. I compile it as gcc MOD.c -Wall -oMOD.exe
>>>
>>>
> 
> 
> 
0
Reply TEJ1 (7) 7/10/2005 8:35:38 PM

"Joe Wright" <joewwright@comcast.net> wrote in message
news:H7SdnX8sYP4p90zfRVn-gA@comcast.com...
> Alexei A. Frounze wrote:
> > "Alexei A. Frounze" <alexfru@chat.ru> wrote in message
> > news:3jd0i6Fphgt2U1@individual.net...
> >
> >>"Joe Wright" <joewwright@comcast.net> wrote in message
> >>news:0uqdnVtZRKW11kzfRVn-1w@comcast.com...
> >>
> >>>daniele_athome wrote:
> >>>
> >>>>Alexei A. Frounze wrote:
> >>>>
> >>>>
> >>>>>void print2 (const int (*p)[5])
> >>>>
> >>>>
> >>>>arg1 of print2 seems a pointer to a function... isn't it?
> >>>
> >>>No. p is a pointer to array 5 of const int. Alexei gets this wrong too.
> >>
> >>Excuse me, but what did I get wrong? Isn't what you're trying to say the
> >>same as in my original post (excerpt): "All I
> >>want to do is to explicitly show that the pointer is to an array of 5
> >>entries and ensure that it's not modified in anyway inside print2()."?
> >>
> >>Alex
> >
> >
> > To clarify further, "... and ensure that *the array (not the pointer)*
it's
> > not modified in anyway inside print2()."
> > Alex
>
> Sorry Alex, I jumped on you in error. It would seem to be a 'const'
> issue. If I remove the const qualifier from the print2 parameter it
> works. Or if I add const to the array definition..
>
>    const int aInt3[5] = {0, 1, 2, 4, 9};
>
> it works too. Go figure. I don't have a good handle on 'const'.

That's what I'm trying to find explanation for. I mean, not the const,
rather the odd warning.
Anyway, const prohibits modification of data. Putting it in different
positions near * results in treatment of different object as
constant/unalterable. For instance:

int *p - modifiable pointer to modifiable int
const int *p - modifiable pointer to constant int
int* const p = initvalue; - constant pointer to modifiable int
const int* const p = initvalue; - constant pointer to constant int

int** pp - modifiable pointer to modifiable pointer to modifiable int
const int** pp - modifiable pointer to modifiable pointer to constant int
int* const* pp - modifiable pointer to constant pointer to modifiable int
int** const pp = initvalue; - constant pointer to modifiable pointer to
modifiable int
etc etc...

Maybe I should look into c99 or better ask gcc guys what's going on.
I can also try the thing with other compilers. By now I know that Borland's
old Turbo C/C++ 16-bit compiler for DOS handles the pointer to const array
w/o the warning. There're other Borland's, Open Watcom and VC++ to try...
Though, VC++ is known to emit even more stupid warnings (like a=b=0; where a
and b are for e.g. short and int, and 0 says nothing to the compiler).

Best regards,
Alex


0
Reply alexfru (352) 7/10/2005 8:42:21 PM

"Tommi Johnsson" <TEJ@Merlin.Mirrorland> wrote in message
news:4LfAe.3602$rb3.782@reader1.news.jippii.net...
> Alexei A. Frounze wrote:
> > Yes, this would work fine if aInt3[] were declared as const array... But
the
> > problem is that the compiler allows pointers to consts to non-const data
but
> > warns on pointers to const array to non-const data. That's why I
presented
> > both things in the test program. Kinda odd, don't you think so? I mean,
I
> > can put there a type cast to get rid of the warning, but this warning in
its
> > very core is stupid. Why "const int*" should be better than or in any
other
> > way differ from "const (*int)[]"? Where's the rationale? I don't see it.
Any
> > idea?
>
> i think, it's because types differs from each other...
> in print1 you declare `p` as an pointer to const qualified int.
> -->> pointed value is const qualified
> and
> in print2 you declare `p` as an pointer to const qualified array of ints
> -->> pointed value is array which elements are const qualified.
>
> i see `const int **p` more like `const int (*p)[5]` than `const int *p`
> in this case, because array decays to pointer in most cases.
>
> - jt

There were no "const int **p" in the code. Besides, "const int **p" declares
a modifiable pointer to a modifiable pointer to a constant int. There's no
double indirection anywhere in the code.

Anyway, can you suggest a declaration of the function's argument that would
simultaneously satisfy:
- being a pointer to an array of a fixed number of elements, say, 5 integers
- this array is to be constant, in other words, its elements can't be
altered by using the pointer

Can it be declared like that provided the compiler does not emit any warning
if as argument I use an array (its address) that isn't constant?

Why can I do it with one value, e.g. pass a pointer to modifiable int or
modifiable array of ints to a function whose argument is of type "const
int*", but I can not do the same with an array of fixed number of ints?

Alex


0
Reply alexfru (352) 7/10/2005 8:53:27 PM


Alexei A. Frounze wrote:
> "Al Bowers" <abowers@myrapidsys.com> wrote in message
> news:3jd5spFpjotoU1@individual.net...
> ...
> 
>>Try removing the const keyword.
> 
> ...
> 
> But I want to keep it. :)
> 

If castration of const is out, do not pout.
Casting, a pollution, is one solution.

(const int (*)[5])

-- 
Al Bowers
Tampa, Fl USA
mailto: xabowers@myrapidsys.com (remove the x to send email)
http://www.geocities.com/abowers822/

0
Reply abowers (46) 7/10/2005 9:07:30 PM

"Al Bowers" <abowers@myrapidsys.com> wrote in message
news:3jdh0iFph494U1@individual.net...
>
>
> Alexei A. Frounze wrote:
> > "Al Bowers" <abowers@myrapidsys.com> wrote in message
> > news:3jd5spFpjotoU1@individual.net...
> > ...
> >
> >>Try removing the const keyword.
> >
> > ...
> >
> > But I want to keep it. :)
> >
>
> If castration of const is out, do not pout.
> Casting, a pollution, is one solution.


Very smart :)

> (const int (*)[5])

This is even smarter. :)

So, I presume nobody knows what's wrong. I haven't found anything on this
particular thing in C99. Maybe it's just gcc's way to do things... I need
more statistics.
Alex


0
Reply alexfru (352) 7/10/2005 9:39:21 PM

On Sun, 10 Jul 2005 19:48:00 +0400, "Maxim S. Shatskih"
<maxim@storagecraft.com> wrote:

>    Surely.
>
>    "&alnt3" is the same as "alnt3", since alnt3 is an array.

The original post defined an array as int aInt3[5].  Ignoring your
typo in the name:

	&aInt3 has type pointer to array of 5 int.

	aInt3 in most contexts, including the question raised in the
original post, will be converted to the address of the first element
with type pointer to element type.  In this case, it is pointer to
int.  

The two types are not compatible.

>
>    So, you pass int* to print2, and it expects an array of int* - i.e. int**.

The original post passed &aInt3 to print2, so it did not pass an int*.
print2 was defined in the original post as
	void print2 (const int (*p)[5])

The argument is declared as type pointer to constant array of 5 int,
not as an array of pointer to int.  While an array of int* would be
passed to a function as an int**, the two are not synonymous at all.


<<Remove the del for email>>
0
Reply schwarzb (662) 7/10/2005 10:08:51 PM

On Mon, 11 Jul 2005 00:53:27 +0400, Alexei A. Frounze wrote:

> "Tommi Johnsson" <TEJ@Merlin.Mirrorland> wrote in message
> news:4LfAe.3602$rb3.782@reader1.news.jippii.net...
>> Alexei A. Frounze wrote:
>> > Yes, this would work fine if aInt3[] were declared as const array...
>> > But
> the
>> > problem is that the compiler allows pointers to consts to non-const
>> > data
> but
>> > warns on pointers to const array to non-const data. That's why I
> presented
>> > both things in the test program. Kinda odd, don't you think so? I
>> > mean,
> I
>> > can put there a type cast to get rid of the warning, but this warning
>> > in
> its
>> > very core is stupid. Why "const int*" should be better than or in any
> other
>> > way differ from "const (*int)[]"? Where's the rationale? I don't see
>> > it.
> Any
>> > idea?

Yes, see below.

>>
>> i think, it's because types differs from each other... in print1 you
>> declare `p` as an pointer to const qualified int. -->> pointed value is
>> const qualified and
>> in print2 you declare `p` as an pointer to const qualified array of ints
>> -->> pointed value is array which elements are const qualified.
>>
>> i see `const int **p` more like `const int (*p)[5]` than `const int *p`
>> in this case, because array decays to pointer in most cases.

Yes, the double indirection is the cause of the warning.

> There were no "const int **p" in the code.

I don't think jt was saying there was.  He was saying that you should be
comparing print2 function's parameter with "const int **p" rather than
with print1's "const int *p" as you have been doing.  This is the source
of your confusion.

> Besides, "const int **p"
> declares a modifiable pointer to a modifiable pointer to a constant int.
> There's no double indirection anywhere in the code.

A pointer to an array *is* double indirection.  So print2's first
parameter is doubly indirected.

> Anyway, can you suggest a declaration of the function's argument that
> would simultaneously satisfy:
> - being a pointer to an array of a fixed number of elements, say, 5
> integers - this array is to be constant, in other words, its elements
> can't be altered by using the pointer

No.  To the best of my understanding it is not possible according to the
standard.

> Can it be declared like that

Like what?

> provided the compiler does not emit any
> warning if as argument I use an array (its address) that isn't constant?
>
> Why can I do it with one value, e.g. pass a pointer to modifiable int or
> modifiable array of ints to a function whose argument is of type "const
> int*", but I can not do the same with an array of fixed number of ints?

This question was answered recently in another thread.  See
message id <871x6b4kfc.fsf@hardknott.home.whinlatter.ukfsn.org>
dated      Wed, 6 Jul 2005 22:54:41 +0000 (UTC)
on         comp.lang.c
subject    Implicit addition of const qualifiers

Hint, try adding a function:

void print3 (const int** p, size_t cnt) {
  while (cnt--)
    printf ("%d\n", *(*p)++);
}

and in your main code:
  int *pInt = aInt2;
  print3 (&pInt, sizeof(aInt2)/sizeof(aInt2[0]));

You'll get the same warning.

The problem is the double indirection - the standard won't allow the
non-const to be automatically converted to const in such cases.

Wouldn't you know it - you'll have to go with the poet.  A cast is
required.

0
Reply netocrat (497) 7/10/2005 11:22:08 PM

On Mon, 11 Jul 2005 01:39:21 +0400, Alexei A. Frounze wrote:

> "Al Bowers" <abowers@myrapidsys.com> wrote in message
> news:3jdh0iFph494U1@individual.net...
>>
>>
>> Alexei A. Frounze wrote:
>> > "Al Bowers" <abowers@myrapidsys.com> wrote in message
>> > news:3jd5spFpjotoU1@individual.net... ...
>> >
>> >>Try removing the const keyword.
>> >
>> > ...
>> >
>> > But I want to keep it. :)
>> >
>> >
>> If castration of const is out, do not pout. Casting, a pollution, is one
>> solution.
> 
> 
> Very smart :)
> 
>> (const int (*)[5])
> 
> This is even smarter. :)
> 
> So, I presume nobody knows what's wrong.

Well it's strange that you haven't had a more direct explanation as to
why the problem is occurring as yet.

> I haven't found anything on this
> particular thing in C99.

I'm not sure if it's in C90 or C99... I haven't looked myself but see the
thread I refer to in my other post which mentions it as being in standard
C++.

> Maybe it's just gcc's way to do things... I need more statistics.

I haven't consulted the standards and don't know whether gcc is complying
with C90 or C99.  But intuitively it is correct behaviour - double
indirection can easily be used to gain access to the supposedly protected
const type by assigning the middle pointer to a non-const pointer. 
There's no way the compiler can detect this, so disallowing automatic
const-conversion for double-indirection parameters makes sense.

0
Reply netocrat (497) 7/10/2005 11:29:27 PM

On Mon, 11 Jul 2005 09:22:08 +1000, Netocrat wrote:

>> Anyway, can you suggest a declaration of the function's argument that
>> would simultaneously satisfy:
>> - being a pointer to an array of a fixed number of elements, say, 5
>> integers - this array is to be constant, in other words, its elements
>> can't be altered by using the pointer
> 
> No.  To the best of my understanding it is not possible according to the
> standard.

Disclaimer: my knowledge of the standard is very patchy and I made this
statement based on the thread that I referenced - but looking at it again
I see that there is no actual mention of C90 or C99 - just C++ standards.
So I really don't know what the standard says about this and I'm not
motivated to go hunting through the drafts to find out.  You say you've
looked and can't find anything: perhaps one of the standards gurus of this
group can clarify. It is intuitively correct behaviour though whether
standards-specified or not.

0
Reply netocrat (497) 7/10/2005 11:35:58 PM

Alexei A. Frounze wrote:

> I have a question regarding the gcc behavior (gcc version 3.3.4).
>
> On the following test program it emits a warning:
> #include <stdio.h>
>
> int aInt3[5] = {0,1,2,4,9};
>
> void print2 (const int (*p)[5])
> {
> }
>
> int main()
> {
>   print2 (&aInt3); // <-- warns here
>   return 0;
> }

> The warning is:
> MOD.c: In function `main':
> MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type

The compiler is correct.  The pointers point to incompatible types;
the element type of one array is const-qualified, and it isn't in the
other.  Type compatibility is recursive, and types with different
qualifiers are not compatible.

Either the function needs to lose the const qualifier, or you need to
add it to  your array.

> There's another thing about the pointer to the array is... The compiler
> doesn't generate a warning if I change in print2()
>   for (cnt=0; cnt<5; cnt++)
> to
>   for (cnt=0; cnt<6; cnt++)
> and lets me access (*p)[5], though I think at least a warning should be
> generated here.

That's hard as it has to follow what your program is doing, not just
the syntax.  It's certainly possible to warn, though; some lint
programs would.

> It doesn't warn me if I put
>  aInt3[6] = 0;
> into main(). But in both cases the compiler "knows" the real size of the
> array, still no warning...

That's easy to warn about and GCC should do it.  I suggest you file an
enhancement request.

0
Reply neil5022 (2) 7/11/2005 12:37:42 AM

Alexei A. Frounze wrote:
>
> I have a question regarding the gcc behavior (gcc version 3.3.4).

You got an unusually high number of garbage responses :)

> On the following test program it emits a warning:
> #include <stdio.h>
>
> int aInt3[5] = {0,1,2,4,9};
>
> void print2 (const int (*p)[5])
> {
>   size_t cnt;
>   for (cnt=0; cnt<5; cnt++)
>     printf ("%d\n", (*p)[cnt]);
> }
>
> int main()
> {
>   print2 (&aInt3); // <-- warns here
>   return 0;
> }
>
> MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type

print2 is expecting a pointer to an array of constant ints.

Note, you can't have a "const array" in C -- you can
only have an array whose members are all const.

aInt3 is an array of ints, and there is no implicit conversion
in C from pointer-to-array-of-T to pointer-to-array-of-const-T
(or an array of anything else, for that matter). This could be
considered a language defect.

But, this does work:
    typedef int (*P5INT)[5];
    void print2(const P5INT p)
    {

AFAIK it's not possible to do this without a typedef.

0
Reply oldwolf (2278) 7/11/2005 1:01:17 AM

On Sun, 10 Jul 2005 18:01:17 -0700, Old Wolf wrote:

> [T]here is no implicit conversion in C from pointer-to-array-of-T to
> pointer-to-array-of-const-T (or an array of anything else, for that
> matter). This could be considered a language defect.

In this case it certainly would be useful.  I don't see how any harm could
be done.

> But, this does work:
>     typedef int (*P5INT)[5];
>     void print2(const P5INT p)
>     {

Not as the OP intended though.  It is an equivalent prototype to:
     void print2(int (*const p)[5])

So it does allow the function to be called as the OP desired - ie with a
non-const array - and without a cast - but it also removes the desired
const protection... you can now assign to (*p)[0] within the function. 
The semantics of your parameter are to make p itself const, rather than
what it points to.  i.e. if you add to the function:

  int pp[5];
  p = &pp;

you will get a warning about attempting to write to a read-only location.

> AFAIK it's not possible to do this without a typedef.

It is though, as per above.

0
Reply netocrat (497) 7/11/2005 2:38:37 AM

On Mon, 11 Jul 2005 09:29:27 +1000, Netocrat wrote:

> [D]ouble indirection can easily be used to gain access to the supposedly
> protected const type by assigning the middle pointer to a non-const
> pointer. There's no way the compiler can detect this, so disallowing
> automatic const-conversion for double-indirection parameters makes sense.

I'm talking nonsense.  You can't assign the middle pointer to a non-const
pointer without a cast.  But you can use the automatic conversion to
violate the const protection: quoting "Me" in another thread:

const char c = 'c';
char * pc;
const char ** pcc = & pc ; /* not allowed */
*pcc = & c;
*pc = 'C'; /* would modify a const object if the conversion above were
            * allowed */

0
Reply netocrat (497) 7/11/2005 2:51:49 AM

On Mon, 11 Jul 2005 12:38:37 +1000, Netocrat wrote:

> On Sun, 10 Jul 2005 18:01:17 -0700, Old Wolf wrote:
> 
>> [T]here is no implicit conversion in C from pointer-to-array-of-T to
>> pointer-to-array-of-const-T (or an array of anything else, for that
>> matter). This could be considered a language defect.
> 
> In this case it certainly would be useful.  I don't see how any harm could
> be done.

OK I take that back after reading another thread - harm could be done. 
It's definitely not a defect but a required safety mechanism.

0
Reply netocrat (497) 7/11/2005 2:53:13 AM

> A pointer to an array *is* double indirection.

No. It is absolutely the same as the pointer to its first element.

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com


0
Reply maxim19 (63) 7/11/2005 10:01:57 AM

On Mon, 11 Jul 2005 14:01:57 +0400, Maxim S. Shatskih wrote:

>> A pointer to an array *is* double indirection.
> 
> No.

An array is one layer of indirection.  Given its similarity to a pointer I
don't see much controversy in that statement.  A pointer to an array is
therefore a second layer of indirection.  What do you find disagreeable
about this reasoning?

> It is absolutely the same as the pointer to its first element.

What is "the pointer" to which you are referring?  Do you mean the
address of its first element?  In that case your statement is plainly
absurd - a pointer to an array is much more than just an address.  So I
doubt that you mean that.  Generally a pointer is a variable... but
we've only got one variable here, and you can't be claiming equivalence
with itself since that is true but meaningless.  So what _do_ you mean?

Your statement is also vague because "first element" could be applied to
the array pointer or to the array that it points to. You don't make it
clear which you mean. i.e. given:

int (*ap)[5] = &a;

it is possible to access "it's first element" as ap[0] or (*ap)[0]. 

Although your statement is vague, I interpret it to be saying that

ap == &(*ap)[0]

which I don't disagree with.  Since you are disagreeing with my double
indirection statement though, you perhaps would also be arguing that

ap[0] == (*ap)[0]

That's true, but a pointer to an object can be treated as an array of
those objects and this is no less true for pointers to arrays - which can
be treated as arrays of arrays. So for index other than 0

ap[index] != (*ap)[index]

In fact accessing ap[1] in this instance is illegal but conceptually it is
an access of the second "5 element integer array" element of ap.  We can
make it valid by doing this: 
ap = malloc(sizeof(a) * 10); 
if (!ap) abort_no_mem();

Now we can write things like ap[7][4] which accesses the 5th (and last)
element of the 8th "5 element integer array" in the array of "5 element
integer arrays" which, given our ability to index a pointer as an array,
we can consider ap as representing.

Again demonstrating that a pointer to an array has two layers of
indirection.  In any case I found your statement unclear so I have made
a few assumptions but I may have missed your point.

0
Reply netocrat (497) 7/11/2005 11:06:45 AM

On Mon, 11 Jul 2005 21:06:45 +1000, Netocrat wrote:

> On Mon, 11 Jul 2005 14:01:57 +0400, Maxim S. Shatskih wrote:
> 
>>> A pointer to an array *is* double indirection.
>> 
>> No.
> 
> An array is one layer of indirection.  Given its similarity to a pointer I
> don't see much controversy in that statement.  A pointer to an array is
> therefore a second layer of indirection.  What do you find disagreeable
> about this reasoning?
> 
>> It is absolutely the same as the pointer to its first element.
> 
> What is "the pointer" to which you are referring?  Do you mean the address
> of its first element?  In that case your statement is plainly absurd - a
> pointer to an array is much more than just an address.  So I doubt that
> you mean that.  Generally a pointer is a variable... but we've only got
> one variable here, and you can't be claiming equivalence with itself since
> that is true but meaningless.  So what _do_ you mean?
> 
> Your statement is also vague because "first element" could be applied to
> the array pointer or to the array that it points to. You don't make it
> clear which you mean. i.e. given:
> 

I left this out:

int a[5];

> int (*ap)[5] = &a;
> 
> it is possible to access "it's first element" as ap[0] or (*ap)[0].
> 
> Although your statement is vague, I interpret it to be saying that
> 
> ap == &(*ap)[0]
> 
> which I don't disagree with.  Since you are disagreeing with my double
> indirection statement though, you perhaps would also be arguing that
> 
> ap[0] == (*ap)[0]
> 
> That's true, but a pointer to an object can be treated as an array of
> those objects and this is no less true for pointers to arrays - which can
> be treated as arrays of arrays. So for index other than 0
> 
> ap[index] != (*ap)[index]
> 
> In fact accessing ap[1] in this instance is illegal but conceptually it is
> an access of the second "5 element integer array" element of ap.  We can
> make it valid by doing this:
> ap = malloc(sizeof(a) * 10);
> if (!ap) abort_no_mem();
> 
> Now we can write things like ap[7][4] which accesses the 5th (and last)
> element of the 8th "5 element integer array" in the array of "5 element
> integer arrays" which, given our ability to index a pointer as an array,
> we can consider ap as representing.
> 
> Again demonstrating that a pointer to an array has two layers of
> indirection.  In any case I found your statement unclear so I have made a
> few assumptions but I may have missed your point.

0
Reply netocrat (497) 7/11/2005 11:10:58 AM

On Sun, 10 Jul 2005 19:51:22 +0400, Alexei A. Frounze wrote:

> "Maxim S. Shatskih" <maxim@storagecraft.com> wrote in message
> news:darftd$1843$1@gavrilo.mtu.ru...
>>     Surely.
>>
>>     "&alnt3" is the same as "alnt3", since alnt3 is an array.
> 
> It's not. :) The values are identical, but the types aren't, strictly
> speaking.

Everything else you've said is spot on, but this is slightly incorrect. 
You are right that the values are identical when the array is not a
function parameter.  i.e. 

#include <stdio.h>
int glob_array[5];

int main(void)
{
	int fn_array[5];

	if ((&glob_array != glob_array) || (&fn_array != fn_array))
		printf("I haven't seen a situation like this before.\n");
}

I don't know however, whether this is mandated by the standard - in
theory it isn't necessary for it to be true, but in practice it would
waste space for it not to be.

However in the context of a function parameter, it is not true.  Given
that in C parameters are passed by value, it's not possible for the
address of the array parameter to equal the address of the original array
and therefore it's not possible for the address of the array parameter to
equal the address of the first element that it points to.  i.e. we can
write:

#include <stdio.h>
void somefunction(int arrayparm[5])
{
	if (arrayparm == &arrayparm)
		printf("Something impossible has occurred.\n");
}

0
Reply netocrat (497) 7/11/2005 11:37:37 AM

Alexei A. Frounze wrote:
> "Tommi Johnsson" <TEJ@Merlin.Mirrorland> wrote in message
> news:4LfAe.3602$rb3.782@reader1.news.jippii.net...
> 
>>Alexei A. Frounze wrote:
>>
>>>Yes, this would work fine if aInt3[] were declared as const array... But
> 
> the
> 
>>>problem is that the compiler allows pointers to consts to non-const data
> 
> but
> 
>>>warns on pointers to const array to non-const data. That's why I
> 
> presented
> 
>>>both things in the test program. Kinda odd, don't you think so? I mean,
> 
> I
> 
>>>can put there a type cast to get rid of the warning, but this warning in
> 
> its
> 
>>>very core is stupid. Why "const int*" should be better than or in any
> 
> other
> 
>>>way differ from "const (*int)[]"? Where's the rationale? I don't see it.
> 
> Any
> 
>>>idea?
>>
>>i think, it's because types differs from each other...
>>in print1 you declare `p` as an pointer to const qualified int.
>>-->> pointed value is const qualified
>>and
>>in print2 you declare `p` as an pointer to const qualified array of ints
>>-->> pointed value is array which elements are const qualified.
>>
>>i see `const int **p` more like `const int (*p)[5]` than `const int *p`
>>in this case, because array decays to pointer in most cases.
>>
>>- jt
> 
> 
> There were no "const int **p" in the code. Besides, "const int **p" declares
> a modifiable pointer to a modifiable pointer to a constant int. There's no
> double indirection anywhere in the code.
> 
> Anyway, can you suggest a declaration of the function's argument that would
> simultaneously satisfy:
> - being a pointer to an array of a fixed number of elements, say, 5 integers
> - this array is to be constant, in other words, its elements can't be
> altered by using the pointer
> 
> Can it be declared like that provided the compiler does not emit any warning
> if as argument I use an array (its address) that isn't constant?
> 

Try this one...
Array decays to pointer while array is passed to the function...

int aInt[5] = {1,2,3,4,5};

void print (const int p[5]) {
     int x = 25;
     if (__builtin_types_compatible_p (typeof (p), const int * ))
         printf ("trallalalaa...");
     printf ("%d %d %d\n", sizeof (p), sizeof (int[5]), sizeof (const 
int *));
     //*p[0] = 3;
     printf ("%d\n", p[0]);
     p = &x;
     printf ("%d\n", *p);
}

int main()
{
   printf ("test begin\n");

   print (aInt);

   printf ("test end\n");
   return 0;
}


- jt


> Why can I do it with one value, e.g. pass a pointer to modifiable int or
> modifiable array of ints to a function whose argument is of type "const
> int*", but I can not do the same with an array of fixed number of ints?
> 
> Alex
> 
> 
0
Reply TEJ1 (7) 7/11/2005 2:26:18 PM

> An array is one layer of indirection.  Given its similarity to a pointer I
> don't see much controversy in that statement.  A pointer to an array is
> therefore a second layer of indirection.  What do you find disagreeable
> about this reasoning?

The C notion of the array is another. Array is the same as pointer to its first
element (in all operations except sizeof(), and so ( p + a ) offset
operations).

> What is "the pointer" to which you are referring?

MyArray and &(MyArray[0]) is the same in all contexts except sizeof().

> absurd - a pointer to an array is much more than just an address.

In C and C++, it is the same.

> doubt that you mean that.  Generally a pointer is a variable...

Pointer is a value. Value != variable.

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com


0
Reply maxim19 (63) 7/11/2005 5:33:32 PM

[Given "int arr[N];" and considering "&arr" vs "&arr[0]")

>On Sun, 10 Jul 2005 19:51:22 +0400, Alexei A. Frounze wrote:
>> It's not. :) The values are identical, but the types aren't, strictly
>> speaking.

In article <pan.2005.07.11.11.37.35.845525@dodo.com.au>,
Netocrat  <netocrat@dodo.com.au> wrote:
>Everything else you've said is spot on, but this is slightly incorrect. 

Actually, it is completely correct. :-)  But it does not tell you
all that much by itself: as I have shown before, the values of
3 and 3.14 are identical too:

    int i = 3;
    double d = 3.14;

    if ((char)i == (char)d)
        puts("3 and 3.14 are identical");
    else
        puts("3 and 3.14 are different");

Obviously they are only "identical" after conversion to a common
type -- in this case "char" -- and the results may (and do) change
if we pick a different common type:

    if ((float)i == (float)d)
        puts("3 and 3.14 are identical");
    else
        puts("3 and 3.14 are different");

To compare &arr against &arr[0], we have to convert to a common
type; and as with the int/double arrangement above, this may change
the value(s) in the process:

>#include <stdio.h>
>int glob_array[5];
>
>int main(void)
>{
>       int fn_array[5];
>
>       if ((&glob_array != glob_array) || (&fn_array != fn_array))
>               printf("I haven't seen a situation like this before.\n");
>}

If you actually compile this, you get the required diagnostic,
and in this particular case, two more diagnostics:

    % cc -O2 -W -Wall -ansi -pedantic -o t t.c
    t.c: In function `main':
    t.c:8: warning: comparison of distinct pointer types lacks a cast
    t.c:8: warning: comparison of distinct pointer types lacks a cast
    t.c:10: warning: control reaches end of non-void function

(the C standard requires only that "at least one diagnostic" come
out, and does not say whether it is a "warning", or an "error", or
even a "kumquat").  We can fix this by inserting a conversion to
some common type: for instance, we could cast both to "char *".
But introducing a conversion gets us back to that 3==3.14 problem.
I think the real question boils down to whether &arr and &arr[0]
will compare equal under *all* "well-defined" conversions -- which
may even be only those to "char *" and "void *" -- and then I think
the answer is "yes", so that we can in fact say that the converted
values are always identical as long as we do a sensible conversion.

But, onward:

>However in the context of a function parameter, it is not true.  Given
>that in C parameters are passed by value, it's not possible for the
>address of the array parameter to equal the address of the original array
>and therefore it's not possible for the address of the array parameter to
>equal the address of the first element that it points to.  i.e. we can
>write:
>
>#include <stdio.h>
>void somefunction(int arrayparm[5])
>{
>       if (arrayparm == &arrayparm)
>               printf("Something impossible has occurred.\n");
>}

The real problem here is "what you see, well, all of it's a lie"[%].
The variable named "arrayparm" has type "pointer to int", not "array
5 of int".  We can expose the lie via various operators:

    /* these sizes will differ (unless you get VERY unlucky) */
    printf("sizeof (int [5]) = %lu\n", (unsigned long)sizeof (int [5]));
    printf("sizeof arrayparm = %lu\n", (unsigned long)sizeof arrayparm);

    /* cannot do this with an array */
    arrayparm = NULL;

and of course, your own example, once we insert some appropriate
conversions to eliminate the need for a diagnostic (and perhaps no
executable program as a consequence of the diagnostic):

    printf("%p != %p, we presume\n", (void *)arrayparm, (void *)&arrayparm);
    if ((void *)arrayparm != (void *)&arrayparm)
        puts("we presumed correctly");
    else
        puts("uh oh");

[% "Wine from the Water", from Try Anything Once]
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40�39.22'N, 111�50.29'W)  +1 801 277 2603
email: forget about it   http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
0
Reply nospam252 (1722) 7/11/2005 5:35:54 PM

On 11 Jul 2005 17:35:54 GMT, Chris Torek 
   <nospam@torek.net> wrote:

> [Given "int arr[N];" and considering "&arr" vs "&arr[0]")
> 
> I think the real question boils down to whether &arr and &arr[0]
> will compare equal under *all* "well-defined" conversions -- which
> may even be only those to "char *" and "void *" -- and then I think
> the answer is "yes", so that we can in fact say that the converted
> values are always identical as long as we do a sensible conversion.

Is that actually defined by the standard?  I remember that in some
pre-standard C compilers arrays were actually implemented as pointers,
so int arr[5]; would actually expand to the equivalent in
pseudo-assember:

  arr:  dw  &_arr
  _arr: dw  ?[5]

(The array pointer itself might be declared in a read-only segment.)

Is this sort of expansion actually banned by the standard, or is it
"just not the done thing"?

In those compilers the effect would be the same as happens with "array
parameters", taking &arr would get you the address of the pointer.

Chris C
0
Reply chris23 (644) 7/11/2005 6:37:33 PM

On Sun, 10 Jul 2005 23:35:38 +0300, Tommi Johnsson wrote:

> Alexei A. Frounze wrote:
>> Yes, this would work fine if aInt3[] were declared as const array... But the
>> problem is that the compiler allows pointers to consts to non-const data but
>> warns on pointers to const array to non-const data. That's why I presented
>> both things in the test program. Kinda odd, don't you think so? I mean, I
>> can put there a type cast to get rid of the warning, but this warning in its
>> very core is stupid. Why "const int*" should be better than or in any other
>> way differ from "const (*int)[]"? Where's the rationale? I don't see it. Any
>> idea?

An array of ints is a fundamentally different type of object to a an int.
So a pointer to an array of ints is quite different to a pointer to an
int, because the properties of a pointer depend very much on the type of
thing they point at.

> i think, it's because types differs from each other...
> in print1 you declare `p` as an pointer to const qualified int.
> -->> pointed value is const qualified
> and
> in print2 you declare `p` as an pointer to const qualified array of ints
> -->> pointed value is array which elements are const qualified.
> 
> i see `const int **p` more like `const int (*p)[5]` than `const int *p` 
> in this case, because array decays to pointer in most cases.

But this is an important example of where that is not the case. In
const int (*p)[5] p is already a pointer, it cannot "decay" further. This
array -> pointer conversion is only applicable at the top level of a type.
It is important to remmeber that arrays and pointers really are
fundamentally different types, although their operation is naturally
connected.

Lawrence
0
Reply lknews (877) 7/11/2005 6:55:42 PM

Alexei A. Frounze wrote:
> ...
> So, I presume nobody knows what's wrong. I haven't found anything on this
> particular thing in C99. Maybe it's just gcc's way to do things... I need
> more statistics.
> ...

What's wrong in the original case is perfectly clear here. The warning is a
consequence of C language's const-correctness rules when they are applied to
arrays (and it's been mentioned here already).

Simply speaking, in accordance with C language's const correctness rules, you
can perform an implicit 'T* -> const T*' conversion. Often people assume that,
say, 'int (*)[10] -> const int (*)[10]' conversion also belongs to that category
and should be performed implicitly without any warnings (that's what you
assumed, apparently). Unfortunately, this is not the case. In C language it is
not possible to const-qualify an array type as a whole. Period. Any attempts to
const-qualify an array type will actually const-qualify the type of the array
_elements_ not the array type itself. In other words, there's no situation in C
when the 'T* -> const T*' can be applied to pointers-to-arrays, since the proper
destination type cannot ever exist in C.

Best regards,
Andrey Tarasevich
0
Reply andreytarasevich (1531) 7/11/2005 8:27:19 PM

Lawrence Kirby wrote:
> On Sun, 10 Jul 2005 23:35:38 +0300, Tommi Johnsson wrote:
> 
> 
>>Alexei A. Frounze wrote:
>>
>>>Yes, this would work fine if aInt3[] were declared as const array... But the
>>>problem is that the compiler allows pointers to consts to non-const data but
>>>warns on pointers to const array to non-const data. That's why I presented
>>>both things in the test program. Kinda odd, don't you think so? I mean, I
>>>can put there a type cast to get rid of the warning, but this warning in its
>>>very core is stupid. Why "const int*" should be better than or in any other
>>>way differ from "const (*int)[]"? Where's the rationale? I don't see it. Any
>>>idea?
> 
> 
> An array of ints is a fundamentally different type of object to a an int.
> So a pointer to an array of ints is quite different to a pointer to an
> int, because the properties of a pointer depend very much on the type of
> thing they point at.
> 
> 
>>i think, it's because types differs from each other...
>>in print1 you declare `p` as an pointer to const qualified int.
>>-->> pointed value is const qualified
>>and
>>in print2 you declare `p` as an pointer to const qualified array of ints
>>-->> pointed value is array which elements are const qualified.
>>
>>i see `const int **p` more like `const int (*p)[5]` than `const int *p` 
>>in this case, because array decays to pointer in most cases.
> 
> 
> But this is an important example of where that is not the case. In
> const int (*p)[5] p is already a pointer, it cannot "decay" further. 

in declaration like const int (*p)[5] it declares only type for p it 
really doesnt decay anything but above in 1.st and 2.nd declaration of 
pointer p, p is needed to indirect before using p pointed value as an 
array, in 3.rd declaration there is no need for that and in later use
p pointed array will decay to the pointer when indirected throught it, i 
suppose.

- jt


> This
> array -> pointer conversion is only applicable at the top level of a type.
> It is important to remmeber that arrays and pointers really are
> fundamentally different types, although their operation is naturally
> connected.
> 
> Lawrence
0
Reply TEJ1 (7) 7/11/2005 8:33:05 PM

Netocrat wrote:
> On Sun, 10 Jul 2005 18:01:17 -0700, Old Wolf wrote:
>
> > But, this does work:
> >     typedef int (*P5INT)[5];
> >     void print2(const P5INT p)
> >     {
>
> Not as the OP intended though.  It is an equivalent prototype to:
>      void print2(int (*const p)[5])

Ah, you are correct.

0
Reply oldwolf (2278) 7/11/2005 10:32:31 PM

"Netocrat" <netocrat@dodo.com.au> wrote in message
news:pan.2005.07.11.02.51.38.950654@dodo.com.au...
> On Mon, 11 Jul 2005 09:29:27 +1000, Netocrat wrote:
>
> > [D]ouble indirection can easily be used to gain access to the supposedly
> > protected const type by assigning the middle pointer to a non-const
> > pointer. There's no way the compiler can detect this, so disallowing
> > automatic const-conversion for double-indirection parameters makes
sense.
>
> I'm talking nonsense.  You can't assign the middle pointer to a non-const
> pointer without a cast.  But you can use the automatic conversion to
> violate the const protection: quoting "Me" in another thread:
>
> const char c = 'c';
> char * pc;
> const char ** pcc = & pc ; /* not allowed */
> *pcc = & c;
> *pc = 'C'; /* would modify a const object if the conversion above were
>             * allowed */

Correct, but I don't think it's a good example. Simply being able to modify
something by pc and being unable to modify it by ppc is just fine, so is in
the simpler example:
char c = 'A';
char *pc = &c;
const char *pc_ = &c;
*pc = 'B'; // OK
*pc_ = 'B'; // prohibited
This is how most of libc's functions are declared -- their arguments are of
type of a pointer to a constant value.
This is what I'd like to have with the array too.
I'm not really sure why enforcing protection in this simple case above is OK
(i.e. pc=&c;) but not OK in case of using the array as an object to be
passed by reference/pointer.
I'm fine with warnings and errors caused by the improper use of a pointer to
a constant object, e.g. attempt to modify the constant object. That's
perfectly understandable. Why cannot I enforce such a protection by treating
some object (not necessarily one defined as constant) as a constant one --
that's something I don't get, no matter if there's double indirection or
not. I'm puzzled. What else am I missing?

Alex


0
Reply alexfru (352) 7/12/2005 6:32:10 AM

<neil@daikokuya.co.uk> wrote in message
news:1121042262.266212.239490@g43g2000cwa.googlegroups.com...
> Alexei A. Frounze wrote:
>
> > I have a question regarding the gcc behavior (gcc version 3.3.4).
> >
> > On the following test program it emits a warning:
> > #include <stdio.h>
> >
> > int aInt3[5] = {0,1,2,4,9};
> >
> > void print2 (const int (*p)[5])
> > {
> > }
> >
> > int main()
> > {
> >   print2 (&aInt3); // <-- warns here
> >   return 0;
> > }
>
> > The warning is:
> > MOD.c: In function `main':
> > MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer
type
>
> The compiler is correct.  The pointers point to incompatible types;
> the element type of one array is const-qualified, and it isn't in the
> other.  Type compatibility is recursive, and types with different
> qualifiers are not compatible.
>
> Either the function needs to lose the const qualifier, or you need to
> add it to  your array.
>
> > There's another thing about the pointer to the array is... The compiler
> > doesn't generate a warning if I change in print2()
> >   for (cnt=0; cnt<5; cnt++)
> > to
> >   for (cnt=0; cnt<6; cnt++)
> > and lets me access (*p)[5], though I think at least a warning should be
> > generated here.
>
> That's hard as it has to follow what your program is doing, not just
> the syntax.  It's certainly possible to warn, though; some lint
> programs would.

I don't think its hard to follow what it is doing. At least, the type is
pointer to the array of 5 integers, not 6 or more. A warning IMO is due in
this case too, not only when I use the array of 5 integers directly (w/o a
pointer to it).

> > It doesn't warn me if I put
> >  aInt3[6] = 0;
> > into main(). But in both cases the compiler "knows" the real size of the
> > array, still no warning...
>
> That's easy to warn about and GCC should do it.  I suggest you file an
> enhancement request.

I guess this enhancement request will be ignored or flamed or something...
The world is envaded by the code like this:
typedef struct tMessage {
  long Type;
  size_t AmountOfAttachedData;
  char aAttachedData[1];
} tMessage;
tMessage* pMsg;
pMsg = GetMyMessage();
Which assumes reading and writing to pMsg->aAttachedData[] at indices
greater than 0. :(

Alex


0
Reply alexfru (352) 7/12/2005 6:39:35 AM

"Old Wolf" <oldwolf@inspire.net.nz> wrote in message
news:1121043677.298459.37990@g49g2000cwa.googlegroups.com...
> Alexei A. Frounze wrote:
> >
> > I have a question regarding the gcc behavior (gcc version 3.3.4).
>
> You got an unusually high number of garbage responses :)
>
> > On the following test program it emits a warning:
> > #include <stdio.h>
> >
> > int aInt3[5] = {0,1,2,4,9};
> >
> > void print2 (const int (*p)[5])
> > {
> >   size_t cnt;
> >   for (cnt=0; cnt<5; cnt++)
> >     printf ("%d\n", (*p)[cnt]);
> > }
> >
> > int main()
> > {
> >   print2 (&aInt3); // <-- warns here
> >   return 0;
> > }
> >
> > MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer
type
>
> print2 is expecting a pointer to an array of constant ints.
>
> Note, you can't have a "const array" in C -- you can
> only have an array whose members are all const.
>
> aInt3 is an array of ints, and there is no implicit conversion
> in C from pointer-to-array-of-T to pointer-to-array-of-const-T
> (or an array of anything else, for that matter). This could be
> considered a language defect.
>
> But, this does work:
>     typedef int (*P5INT)[5];
>     void print2(const P5INT p)
>     {
>
> AFAIK it's not possible to do this without a typedef.

I presume you wanted to write something like this:

typedef int(tArrayType)[5];
tArrayType arr = {0,1,2,3,4};

void print3 (const tArrayType* p)
{
  int i;
  for (i=0; i<5; i++)
    printf ("%d\n", (*p)[i]);
}

int main()
{
  print3 (&arr);
  return 0;
}

But this too issues a warning unless I have arr being const or remove const
from print3().
Same thing, same warning.

Alex


0
Reply alexfru (352) 7/12/2005 6:47:24 AM

"Netocrat" <netocrat@dodo.com.au> wrote in message
news:pan.2005.07.11.11.06.43.292147@dodo.com.au...
....
> That's true, but a pointer to an object can be treated as an array of
> those objects and this is no less true for pointers to arrays - which can
> be treated as arrays of arrays. So for index other than 0
>
> ap[index] != (*ap)[index]
>
> In fact accessing ap[1] in this instance is illegal but conceptually it is
> an access of the second "5 element integer array" element of ap.

OK, I agree with this kind of double indirection.

The prob is though that we have different semantics for pointer to some
object being not an array and for pointer to some object being an array in
the sense that these two objects are treated differently. If this is because
the 2nd pointer is seen as a pointer to pointer (simply because it
unfortunately points to an array) and we have such restrictions on pointers
to pointers, then something is wrong in C or gcc. Like I say in the other
today's post, I don't see a good reason why an attempt to enforce treatment
of some object as being constant (throught the use of a pointer to constant)
is considered bad.
Let's then forget about the array and indeed switch to a pointer to pointer:
char c = 'A';
char *p = &c;
char **pp = &p; // OK
const char **pp_ = &p; // warned
But all what type of pp_ means is that I cannot modify a char that is
somehow doubly dereferenced by pp_. And I think I must be allowed to have
this kind of assignment (pp_ = &p; in the above) because instead of
loosening the protection I try to enforce it. This is the point now.

Alex


0
Reply alexfru (352) 7/12/2005 7:06:33 AM

"Tommi Johnsson" <TEJ@Merlin.Mirrorland> wrote in message
news:QqvAe.3779$c67.3366@reader1.news.jippii.net...
....
> Try this one...
> Array decays to pointer while array is passed to the function...
>
> int aInt[5] = {1,2,3,4,5};
>
> void print (const int p[5]) {
>      int x = 25;
>      if (__builtin_types_compatible_p (typeof (p), const int * ))
>          printf ("trallalalaa...");
>      printf ("%d %d %d\n", sizeof (p), sizeof (int[5]), sizeof (const
> int *));
>      //*p[0] = 3;
>      printf ("%d\n", p[0]);
>      p = &x;
>      printf ("%d\n", *p);
> }
>
> int main()
> {
>    printf ("test begin\n");
>
>    print (aInt);
>
>    printf ("test end\n");
>    return 0;
> }
>
>
> - jt

So, this way I'll pass a pointer to aInt[0], which is OK, but there would
still be no warning no nothing if I try to pass as argument some aInt_
defined as:
int aInt_[6] = {1,2,3,4,5,6};
or as:
int aInt_[4] = {1,2,3,4};
I mean, that [5] in the argument list doesn't mean anything to the compiler.
It wouldn't warn if smaller/bigger array was used. Merely a comment-like
statement :) As if you wrote an operator like this somewhere in the code:
  0;
:)
Alex


0
Reply alexfru (352) 7/12/2005 7:16:58 AM

"Maxim S. Shatskih" <maxim@storagecraft.com> wrote in message
news:dauah1$2odd$1@gavrilo.mtu.ru...
> > An array is one layer of indirection.  Given its similarity to a pointer
I
> > don't see much controversy in that statement.  A pointer to an array is
> > therefore a second layer of indirection.  What do you find disagreeable
> > about this reasoning?
>
> The C notion of the array is another. Array is the same as pointer to its
first
> element (in all operations except sizeof(), and so ( p + a ) offset
> operations).
>
> > What is "the pointer" to which you are referring?
>
> MyArray and &(MyArray[0]) is the same in all contexts except sizeof().
>
> > absurd - a pointer to an array is much more than just an address.
>
> In C and C++, it is the same.
>
> > doubt that you mean that.  Generally a pointer is a variable...
>
> Pointer is a value. Value != variable.

Any value needs storage, including the address that is stored in a special
variable called a pointer. :)

Alex


0
Reply alexfru (352) 7/12/2005 7:20:31 AM

"Andrey Tarasevich" <andreytarasevich@hotmail.com> wrote in message
news:11d5lfj7m2s6hff@news.supernews.com...
> Alexei A. Frounze wrote:
> > ...
> > So, I presume nobody knows what's wrong. I haven't found anything on
this
> > particular thing in C99. Maybe it's just gcc's way to do things... I
need
> > more statistics.
> > ...
>
> What's wrong in the original case is perfectly clear here. The warning is
a
> consequence of C language's const-correctness rules when they are applied
to
> arrays (and it's been mentioned here already).
>
> Simply speaking, in accordance with C language's const correctness rules,
you
> can perform an implicit 'T* -> const T*' conversion. Often people assume
that,
> say, 'int (*)[10] -> const int (*)[10]' conversion also belongs to that
category
> and should be performed implicitly without any warnings (that's what you
> assumed, apparently). Unfortunately, this is not the case. In C language
it is
> not possible to const-qualify an array type as a whole. Period. Any
attempts to
> const-qualify an array type will actually const-qualify the type of the
array
> _elements_ not the array type itself. In other words, there's no situation
in C
> when the 'T* -> const T*' can be applied to pointers-to-arrays, since the
proper
> destination type cannot ever exist in C.
>
> Best regards,
> Andrey Tarasevich

Which means C's types are wrong since the T that is an array is treated
differently from T that is not.
If we define:
typedef int(tArrayType)[5];
then still an object of type tArrayType is pretty much different from say
typedef struct tArrayType {/*some member(s)*/} tArrayType;
In the first case we'll get a warning when passing &T to a function
declaring
argument as "const* T" but we won't get the warning in the second case.

Alex



0
Reply alexfru (352) 7/12/2005 7:30:24 AM

On Mon, 11 Jul 2005 17:35:54 +0000, Chris Torek wrote:

> [Given "int arr[N];" and considering "&arr" vs "&arr[0]")
> 
>>On Sun, 10 Jul 2005 19:51:22 +0400, Alexei A. Frounze wrote:
>>> It's not. :) The values are identical, but the types aren't, strictly
>>> speaking.
> 
> In article <pan.2005.07.11.11.37.35.845525@dodo.com.au>, Netocrat 
> <netocrat@dodo.com.au> wrote:
>>Everything else you've said is spot on, but this is slightly incorrect.
> 
> Actually, it is completely correct. :-)  But it does not tell you all that
> much by itself: as I have shown before, the values of 3 and 3.14 are
> identical too:
> 
>     int i = 3;
>     double d = 3.14;
> 
>     if ((char)i == (char)d)
>         puts("3 and 3.14 are identical");
>     else
>         puts("3 and 3.14 are different");
> 
> Obviously they are only "identical" after conversion to a common type --
> in this case "char" -- and the results may (and do) change if we pick a
> different common type:
> 
>     if ((float)i == (float)d)
>         puts("3 and 3.14 are identical");
>     else
>         puts("3 and 3.14 are different");
> 
> To compare &arr against &arr[0], we have to convert to a common type; and
> as with the int/double arrangement above, this may change the value(s) in
> the process:

Point taken.

>>#include <stdio.h>
>>int glob_array[5];
>>
>>int main(void)
>>{
>>       int fn_array[5];
>>
>>       if ((&glob_array != glob_array) || (&fn_array != fn_array))
>>               printf("I haven't seen a situation like this before.\n");
>>}
>>}
> If you actually compile this,

Which I didn't (obviously).

> you get the required diagnostic, and in this
> particular case, two more diagnostics:
> 
>     % cc -O2 -W -Wall -ansi -pedantic -o t t.c t.c: In function `main':
>     t.c:8: warning: comparison of distinct pointer types lacks a cast
>     t.c:8: warning: comparison of distinct pointer types lacks a cast
>     t.c:10: warning: control reaches end of non-void function
> 
> (the C standard requires only that "at least one diagnostic" come out, and
> does not say whether it is a "warning", or an "error", or even a
> "kumquat").  We can fix this by inserting a conversion to some common
> type: for instance, we could cast both to "char *". But introducing a
> conversion gets us back to that 3==3.14 problem. I think the real question
> boils down to whether &arr and &arr[0] will compare equal under *all*
> "well-defined" conversions -- which may even be only those to "char *" and
> "void *" -- and then I think the answer is "yes", so that we can in fact
> say that the converted values are always identical as long as we do a
> sensible conversion.

You "think" the answer is "yes", Chris Crough in his reply asks "Is that
actually defined by the standard?" and in my original post I wrote "I
don't know however, whether this is mandated by the standard - in theory
it isn't necessary for it to be true".  None of us know for sure...

> But, onward:
> 
>>However in the context of a function parameter, it is not true.  Given
>>that in C parameters are passed by value, it's not possible for the
>>address of the array parameter to equal the address of the original
>>array and therefore it's not possible for the address of the array
>>parameter to equal the address of the first element that it points to. 
>>i.e. we can write:
>>
>>#include <stdio.h>
>>void somefunction(int arrayparm[5])
>>{
>>       if (arrayparm == &arrayparm)
>>               printf("Something impossible has occurred.\n");
>>}
>>}
> The real problem here is "what you see, well, all of it's a lie"[%]. The
> variable named "arrayparm" has type "pointer to int", not "array 5 of
> int".  We can expose the lie via various operators:
> 
>     /* these sizes will differ (unless you get VERY unlucky) */
>     printf("sizeof (int [5]) = %lu\n", (unsigned long)sizeof (int [5]));
>     printf("sizeof arrayparm = %lu\n", (unsigned long)sizeof arrayparm);
> 
>     /* cannot do this with an array */
>     arrayparm = NULL;
> 
> and of course, your own example, once we insert some appropriate
> conversions to eliminate the need for a diagnostic (and perhaps no
> executable program as a consequence of the diagnostic):
> 
>     printf("%p != %p, we presume\n", (void *)arrayparm, (void
>     *)&arrayparm); if ((void *)arrayparm != (void *)&arrayparm)
>         puts("we presumed correctly");
>     else
>         puts("uh oh");
> 
> [% "Wine from the Water", from Try Anything Once]

Right, right, of course... an array decays into a pointer when passed as
a parameter to a function.  I had forgotten that.  So I was treating a
pointer as an array and making the wrong deductions.  It does seem then
that there is no case where (void*)&array is necessarily different to
(void*)array... leaving the question, as Chris Crough puts it, of whether
'pre-standard C compilers [implementing] arrays ... as pointers ... [is a
type of] expansion actually banned by the standard, or [whether it is]
"just not the done thing"?'

0
Reply netocrat (497) 7/12/2005 7:30:11 PM

On Mon, 11 Jul 2005 21:33:32 +0400, Maxim S. Shatskih wrote:

>> An array is one layer of indirection.  Given its similarity to a pointer
>> I don't see much controversy in that statement.  A pointer to an array
>> is therefore a second layer of indirection.  What do you find
>> disagreeable about this reasoning?
> 
> The C notion of the array is another. 

You are just repeating my first statement, because by "array" of course I
meant "the C notion of the array" - we are talking in a C newsgroup after
all.  You seem to be implying that there is a third layer of indirection
different from the two I described... I just don't understand your point.

> Array is the same as pointer to its
> first element (in all operations except sizeof(), and so ( p + a )
> offset operations).

True, but of what relevance is that statement?  You are supposedly, after
all, disagreeing with my statement that "A pointer to an array *is* double
indirection." How does this support your case?

>> What is "the pointer" to which you are referring?
> 
> MyArray and &(MyArray[0]) is the same in all contexts except sizeof().

As Chris Torek pointed out in another part of this thread, that statement
is not actually correct.  They are different types.  But yes, we all
seem to agree that the values will be the same for sensible conversions.

That statement does not, however, answer my question, which was which
pointer you are referring to.
 
>> absurd - a pointer to an array is much more than just an address.
> 
> In C and C++, it is the same.

No, it is not the same.  A pointer is not an address.  It is a type
whose value represents an address.

>> doubt that you mean that.  Generally a pointer is a variable...
> 
> Pointer is a value. Value != variable.

A pointer is a type containing a value.  Container of value != value.

You still haven't explained why you disagree with my statement that a
pointer to an array is double indirection.

0
Reply netocrat (497) 7/12/2005 7:44:20 PM

On Wed, 13 Jul 2005 05:44:20 +1000, Netocrat wrote:

> On Mon, 11 Jul 2005 21:33:32 +0400, Maxim S. Shatskih wrote:
> 
>>> An array is one layer of indirection.  Given its similarity to a
>>> pointer I don't see much controversy in that statement.  A pointer to
>>> an array is therefore a second layer of indirection.  What do you find
>>> disagreeable about this reasoning?
>> 
>> The C notion of the array is another.
> 
> You are just repeating my first statement, because by "array" of course I
> meant "the C notion of the array" - we are talking in a C newsgroup after
> all.  You seem to be implying that there is a third layer of indirection
> different from the two I described... I just don't understand your point.
> 
>> Array is the same as pointer to its
>> first element (in all operations except sizeof(), and so ( p + a )
>> offset operations).
> 
> True, but of what relevance is that statement?  You are supposedly, after
> all, disagreeing with my statement that "A pointer to an array *is* double
> indirection." How does this support your case?
> 
>>> What is "the pointer" to which you are referring?
>> 
>> MyArray and &(MyArray[0]) is the same in all contexts except sizeof().
> 
> As Chris Torek pointed out in another part of this thread, that statement
> is not actually correct.  They are different types.  But yes, we all
> seem to agree that the values will be the same for sensible conversions.

Ack, having just responded to Chris' post I was quick to misread your
statement as comparing MyArray with &MyArray which it obviously isn't.  So
they are compatible types after all.  They aren't identical types though
because assuming that MyArray is declared something like char MyArray[10],
then MyArray is of type "array 10 of char" whereas &(MyArray[0]) is of
type "pointer to char".  Not the same but interchangeable in most
expressions, with some exceptions such as the one you gave - sizeof().

> That statement does not, however, answer my question, which was which
> pointer you are referring to.

I'll cut you some slack here because after re-reading I see that you
intended "the pointer" to refer to the pointer to the array's first
element. But your meaning is still unclear because as I originally
explained, a pointer to an array can itself be indexed and treated as
though it were an array.  So now rather than being unclear about what you
mean by "the pointer" I am confused about which "array" you are talking
about - the "pointer to array" array or the array that that "pointer to
array" points to?

I'll add that whilst you haven't specified the type of your variable
MyArray, its name suggests that it is intended to be an array, whereas
what you actually objected to was my characterisation of a type that was
a pointer to an array.  So why don't you use such a type in your
explanation?

>>> absurd - a pointer to an array is much more than just an address.
>> 
>> In C and C++, it is the same.
> 
> No, it is not the same.  A pointer is not an address.  It is a type
> whose value represents an address.
> 
>>> doubt that you mean that.  Generally a pointer is a variable...
>> 
>> Pointer is a value. Value != variable.

I want to add that I wasn't contending that a pointer is always a
variable, just that generally it is.  It is a type, and obviously it
occurs in contexts other than variables, such as taking the address of an
object, and casting.

> A pointer is a type containing a value.  Container of value != value.

This is a clearer and more accurate wording:

A pointer is a type, not a value.

A pointer is a type whose value represents the address of an object. 

Your statement that a pointer is a value is as wrong as saying that an int
is a value.  An int has a value, but it is a type, not a value.  In the
same way, a pointer has a value, but it is a type, not a value.

> You still haven't explained why you disagree with my statement that a
> pointer to an array is double indirection.

0
Reply netocrat (497) 7/12/2005 9:34:47 PM

On Tue, 12 Jul 2005 10:32:10 +0400, Alexei A. Frounze wrote:

> "Netocrat" <netocrat@dodo.com.au> wrote in message
> news:pan.2005.07.11.02.51.38.950654@dodo.com.au...
>> On Mon, 11 Jul 2005 09:29:27 +1000, Netocrat wrote:
>>
>> > [D]ouble indirection can easily be used to gain access to the
>> > supposedly protected const type by assigning the middle pointer to a
>> > non-const pointer. There's no way the compiler can detect this, so
>> > disallowing automatic const-conversion for double-indirection
>> > parameters makes sense.
>>
>> I'm talking nonsense.  You can't assign the middle pointer to a
>> non-const pointer without a cast.  But you can use the automatic
>> conversion to violate the const protection: quoting "Me" in another
>> thread:
>>
>> const char c = 'c';
>> char * pc;
>> const char ** pcc = & pc ; /* not allowed */ 
>> *pcc = & c;
>> *pc = 'C'; /* would modify a const object if the conversion above were
>>             * allowed */
> 
> Correct, but I don't think it's a good example. 

I think it's a great example for what it's intended to show.  What it
shows is that the const protection you are giving your c variable is
useless if a char ** variable can be automatically cast to const char **. 
So really it's showing: if you want to be able to trust that the const
qualifier actually does what it's supposed to do, then automatic
conversions from char ** to const char ** _cannot_ be allowed.

It may not be as useful in explaining how this relates to your case.  You
seem to be arguing that an array should be treated as though it were a
simple type, rather than as an indirected type, in this case.  Actually I
don't see any problems with doing that.  The trick above can't be used
when pc is declared as a char array rather than char pointer, since then
we can't change its base address.  So perhaps you could argue something
like this:

Since the type char [] is fundamentally different from char * in that the
base address of the variable it defines cannot be modified, the automatic
conversion from char (*)[] to const char (*)[] is not unsafe, as the
automatic conversion from char ** to const char ** is, and should be
allowed.

I don't see any problems with this statement (others may...).  But the
point is, regardless of whether this statement is true or not, it seems to
me (and again I must reiterate that I don't know what the standard says on
this) that C does treat these types equivalently for const-casting
purposes.

> Simply being able to
> modify something by pc and being unable to modify it by ppc is just fine,
> so is in the simpler example:
> char c = 'A';
> char *pc = &c;
> const char *pc_ = &c;
> *pc = 'B'; // OK
> *pc_ = 'B'; // prohibited

Yes this is all OK because the automatic conversion of &c from type
char* to type const char* can not be used in any way to surreptitiously
write to any const-protected values.

This "simpler" example is indeed just that - because it doesn't deal with
double indirection or pointers to array at all - which is the case in
point.  If you really want to use a simple example that applies to our
discussion, we could change the character types to character array types
and extend it thus: 

char c[5] = "abcde";
char (*pc)[5] = &c;
const char (*pc_)[5] = &c; /* warning - prohibited */
(*pc)[1] = 'B'; /* OK */
(*pc_)[1] = 'B'; /* prohibited */

This illustrates your case better.  As I have argued above - to echo
what I understand to be your fundamental argument - there is no semantic
reason for prohibiting the automatic casting of &c from char (*)[5] to
const char (*)[5].  Nevertheless in C it is prohibited.  C'est la vie. 
Apparently the casting rules are the same as for char ** to const char **
- for which there _is_ a good reason for prohibition.  We might not think
it necessary or appropriate, but that's just the way C works in this case.

Another note - // comments are only allowed by the standard in C99, and
since you didn't specifically mention using C99 I presume you are not
using switches on gcc to invoke standards-compliant behaviour at all,
which you really should. Using -ansi -pedantic makes it reject non-C90
compliant code and using -std=C99 -pedantic does same for C99.  -W and
-Wall are good for additional non-standard-specific warnings.

> This is how most of libc's functions are declared -- their arguments are
> of type of a pointer to a constant value. 

I think you mean "const-qualifed type" rather than "constant value", but I
understand you.

> This is what I'd like to have
> with the array too. I'm not really sure why enforcing protection in this
> simple case above is OK (i.e. pc=&c;) but not OK in case of using the
> array as an object to be passed by reference/pointer. 

Neither am I because it doesn't seem to serve any protective purpose,
but as I've tried to explain it seems to be because the array is being
treated equivalently to the case of a pointer, where a protective purpose
_is_ served.

> I'm fine with
> warnings and errors caused by the improper use of a pointer to a
> constant object, e.g. attempt to modify the constant object. That's
> perfectly understandable. Why cannot I enforce such a protection by
> treating some object (not necessarily one defined as constant) as a
> constant one

You can in some simple cases as your example shows, so I don't know why
you're arguing here that you cannot.

> -- that's something I don't get, no matter if there's
> double indirection or not.

Double indirection in the sense of char ** (but not in the sense of
char(*)[]) _does_ matter though, as the example I quoted shows.

> I'm puzzled. What else am I missing?

You're totally missing the point of the example I quoted if you want to
maintain that double indirection of the form char ** should not be taken
into account when performing automatic const conversions.  Double
indirection in the form char (*)[] of course is different in semantics but
apparently treated by C in the same way as char **.

Note that once we get to char (**)[] then this is a double pointer and
semantically truly does require prohibition of automatic const conversion.

Now to respond to the message you posted elsewhere:

On Tue, 12 Jul 2005 11:06:33 +0400, Alexei A. Frounze wrote:

> "Netocrat" <netocrat@dodo.com.au> wrote in message
> news:pan.2005.07.11.11.06.43.292147@dodo.com.au... ...
>> That's true, but a pointer to an object can be treated as an array of
>> those objects and this is no less true for pointers to arrays - which
>> can be treated as arrays of arrays. So for index other than 0
>>
>> ap[index] != (*ap)[index]
>>
>> In fact accessing ap[1] in this instance is illegal but conceptually it
>> is an access of the second "5 element integer array" element of ap.
> 
> OK, I agree with this kind of double indirection.
> 
> The prob is though that we have different semantics for pointer to some
> object being not an array and for pointer to some object being an array in
> the sense that these two objects are treated differently.

Well that's not a totally accurate statement of the problem, because in
some cases (ie when that "some object" is a pointer) there really _is_
a need for different treatment as "Me"'s example shows.  But if you
rewrite that statement as "We have different semantics for pointer to
array and for pointer to any other non-pointer type", then I agree.

There doesn't seem to be a compelling argument for pointer to array being
treated any differently to other non-simple types, such as structs.  To
again modify your simple example:

struct s {
        int x;
        int y;
};

struct s c;
struct s *pc = &c;
const struct s *pc_ = &c; /* no prohibition here as there is when c is an
                           * array */
pc->x = 1; /* OK */
pc_->x = 2; /* prohibited */

> If this is
> because the 2nd pointer is seen as a pointer to pointer (simply because
> it unfortunately points to an array) 

As I've explained I believe that's the cause, but I don't know the
standard well enough to confirm this.

> and we have such restrictions on
> pointers to pointers, 

For good reason, as the example I quoted shows.

> then something is wrong in C or gcc. 

That's a strong statement, and clearly as it applies to char ** conversion
its inaccurate, but as it applies to treating pointers to arrays
equivalently with double pointers, in this case your opinion that
"something is wrong in C" appears to be at the least a proposition that
could reasonably be argued.

> Like I say in
> the other today's post, I don't see a good reason why an attempt to
> enforce treatment of some object as being constant (throught the use of
> a pointer to constant) is considered bad. Let's then forget about the
> array and indeed switch to a pointer to pointer: 
> char c = 'A';
> char *p = &c;
> char **pp = &p; // OK
> const char **pp_ = &p; // warned
> But all what type of pp_ means is that I cannot modify a char that is
> somehow doubly dereferenced by pp_. And I think I must be allowed to
> have this kind of assignment (pp_ = &p; in the above) because instead of
> loosening the protection I try to enforce it. This is the point now.

But Alex you're now ignoring the lesson of the original example I quoted. 
You're effectively saying, "the automatic conversion from char ** to
const char ** may have caused problems in Me's example, but it doesn't
in this case so it should be allowed here."  Well and good, but in general
the compiler can't know when such an automatic conversion would cause
problems and when it wouldn't, which is why there is a rule that applies
in _all_ cases.

So in summary:
(a) it appears that there is no way around your problem without using a
cast.  The cast doesn't appear to violate any const-protection semantics
as it would in the case where aInt3 was declared int * rather than int[5].
Casts in general are unwise though because they can mask useful (or
required) warnings, so be wary before using one.
(b) the prohibition of an automatic cast in this case does seem according
to const safety semantics to be unnecessary.

0
Reply netocrat (497) 7/12/2005 10:05:20 PM

On Tue, 12 Jul 2005 11:06:33 +0400, Alexei A. Frounze wrote:

> "Netocrat" <netocrat@dodo.com.au> wrote in message
> news:pan.2005.07.11.11.06.43.292147@dodo.com.au... ...
>> That's true, but a pointer to an object can be treated as an array of
>> those objects and this is no less true for pointers to arrays - which
>> can be treated as arrays of arrays. So for index other than 0
>>
>> ap[index] != (*ap)[index]
>>
>> In fact accessing ap[1] in this instance is illegal but conceptually it
>> is an access of the second "5 element integer array" element of ap.
> 
> OK, I agree with this kind of double indirection.
> 
> The prob is though that ...

<snip contents>

I integrated my reply to this post of yours with my reply to another of
your posts.  The message-id of that other reply is
<pan.2005.07.12.22.05.14.670298@dodo.com.au>

0
Reply netocrat (497) 7/12/2005 10:16:57 PM

Maxim S. Shatskih wrote:
>>An array is one layer of indirection.  Given its similarity to a pointer I
>>don't see much controversy in that statement.  A pointer to an array is
>>therefore a second layer of indirection.  What do you find disagreeable
>>about this reasoning?
> 
> 
> The C notion of the array is another. Array is the same as pointer to its first
> element (in all operations except sizeof(), and so ( p + a ) offset
> operations).
> 
> 
>>What is "the pointer" to which you are referring?
> 
> 
> MyArray and &(MyArray[0]) is the same in all contexts except sizeof().
> 
> 
>>absurd - a pointer to an array is much more than just an address.
> 
> 
> In C and C++, it is the same.
> 
> 
>>doubt that you mean that.  Generally a pointer is a variable...
> 
> 
> Pointer is a value. Value != variable.
> 

No. A pointer is a variable which can hold the address of another 
variable. You know that, right? It's in every C book you've ever read. 
Really! Some people refer to the value of an address and call that value 
a 'pointer' but they misuse the term.

int *p;	/* p is a variable of type pointer to int */
int a = 1; /* a is a variable with type int and value 1 */
p = &a; /* the address of a is assigned to variable p */

But p is the only pointer here. &a is an address.

-- 
Joe Wright
"Everything should be made as simple as possible, but not simpler."
                     --- Albert Einstein ---
0
Reply joewwright (1737) 7/12/2005 10:52:56 PM

> > Pointer is a value. Value != variable.
> >
>
> No. A pointer is a variable which can hold the address of another
> variable. You know that, right? It's in every C book you've ever read.

Well, great discussion about what "value" is. :)

For me, "value" is a thing like ( a + 1 ) or a[10]. It is not a variable, and
it is not necessary "lvalue" - a[10] is lvalue, while ( a + 1 ) or ((long)a) is
not.

Value has a strictly defined type known at compile time.

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com


0
Reply maxim19 (63) 7/13/2005 12:04:50 AM

On Tue, 12 Jul 2005 18:52:56 -0400, Joe Wright wrote:

> Maxim S. Shatskih wrote:
>>>An array is one layer of indirection.  Given its similarity to a pointer
>>>I don't see much controversy in that statement.  A pointer to an array
>>>is therefore a second layer of indirection.  What do you find
>>>disagreeable about this reasoning?
>> 
>> 
>> The C notion of the array is another. Array is the same as pointer to
>> its first element (in all operations except sizeof(), and so ( p + a )
>> offset operations).
>> 
>> 
>>>What is "the pointer" to which you are referring?
>> 
>> 
>> MyArray and &(MyArray[0]) is the same in all contexts except sizeof().
>> 
>> 
>>>absurd - a pointer to an array is much more than just an address.
>> 
>> 
>> In C and C++, it is the same.
>> 
>> 
>>>doubt that you mean that.  Generally a pointer is a variable...
>> 
>> 
>> Pointer is a value. Value != variable.
>> 
>> 
> No. A pointer is a variable which can hold the address of another
> variable. You know that, right? It's in every C book you've ever read.
> Really! Some people refer to the value of an address and call that value a
> 'pointer' but they misuse the term.

Actually a pointer is neither a value nor a variable but a type; the word
is commonly used to refer to a variable with pointer type though, and this
is reasonable usage.

Consider these usages:

"the dereferencing of variable x yields a pointer" - what you mean is
that dereferencing x yields a value of pointer type. 

"x is a pointer" - what you mean is that x is a variable with pointer
type.  This is the restricted sense in which the word pointer can be used
to refer to a variable.  A pedant might require this to be written as "x
is a pointer variable", but there aren't any pedants on this newsgroup.

"cast it to a pointer" - what you mean is to change the compiler's view of
the expression so that it sees a pointer type.  This could be any
expression - it doesn't have to be a variable.

"take the pointer and add one to it" - again this usage could apply to
any expression which evaluates to a pointer type; the expression could be
a value or a variable or some other expression, showing clearly that a
pointer is limited neither to being solely a value nor a variable.

> int *p;	/* p is a variable of type pointer to int */ 
> int a = 1; /* a is a variable with type int and value 1 */
> p = &a; /* the address of a is assigned to variable p */
> 
> But p is the only pointer here. &a is an address.

p is the only pointer variable, true, but &a is more than just an address;
it has a type, and that type is pointer to int.   Can we categorically
call it a pointer as we can for a variable that has pointer type?  It's
probably not unreasonable, but it isn't commonly done. In certain contexts
though it can indisputably be referred to as a pointer, as in "add one to
the pointer that we get when we dereference a".

0
Reply netocrat (497) 7/13/2005 12:06:29 AM

On Wed, 13 Jul 2005 04:04:50 +0400, Maxim S. Shatskih wrote:

>> > Pointer is a value. Value != variable.
>> >
>> >
>> No. A pointer is a variable which can hold the address of another
>> variable. You know that, right? It's in every C book you've ever read.
> 
> Well, great discussion about what "value" is. :)

Yes it's an interesting discussion, but I wouldn't have said it was about
value... it seems to me that it's more about what a pointer is.

> For me, "value" is a thing like ( a + 1 ) or a[10]. It is not a variable,
> and it is not necessary "lvalue" - a[10] is lvalue, while ( a + 1 ) or
> ((long)a) is not.  Value has a strictly defined type known at compile
> time.

Replace "value" with "expression" in the above quote.  And of course an
expression evaluates to a value, so you ultimately get to keep your value.

So after interpreting your usage of the word "value", what you are really
saying is that a pointer is an expression.  I take your point(er) - it's
true that a pointer _is_ an expression, but I do think that it is better
(because more broadly) defined as a type.

0
Reply netocrat (497) 7/13/2005 12:29:21 AM

> Actually a pointer is neither a value nor a variable but a type; the word
> is commonly used to refer to a variable with pointer type though, and this

Yes, and the value of the pointer type is also called "pointer" usually.

BTW - arrays and pointers are different for multu-dimension arrays. Imagine:

    int Array[10][5];

- and please tell me what will be the type of

    Array[1]

expression? Yes, the type will be "array of 5 integers", which is different
from "int *" - it is indexed in a different way.

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com


0
Reply maxim19 (63) 7/13/2005 1:22:06 AM

On Wed, 13 Jul 2005 05:22:06 +0400, Maxim S. Shatskih wrote:

>> Actually a pointer is neither a value nor a variable but a type; the
>> word is commonly used to refer to a variable with pointer type though,
>> and this
> 
> Yes, and the value of the pointer type is also called "pointer" usually.

Well I'd be more inclined to refer to the value of a pointer type as an
address, but from your other post I know that you use "value" to mean
"expression", so perhaps by the above statement you mean "...expressions
of pointer type are usually also called pointer".  So you would mean that
if we have a variable "a" then we can say that &a is a pointer.  I suppose
that that usage is OK - it's probably down to personal preference.

> BTW - arrays and pointers are different for multu-dimension arrays.
> Imagine:
> 
>     int Array[10][5];
> 
> - and please tell me what will be the type of
> 
>     Array[1]
> 
> expression? Yes, the type will be "array of 5 integers", which is
> different from "int *" - it is indexed in a different way.

What do you mean by this?  I don't know of any situation where an array of
5 integers and a pointer to int would be indexed differently.

0
Reply netocrat (497) 7/13/2005 1:44:48 AM

Joe Wright wrote:

>> Pointer is a value. Value != variable.
>> 
> 
> No. A pointer is a variable which can hold the address of another
> variable. You know that, right? It's in every C book you've ever read.
> Really! Some people refer to the value of an address and call that value
> a 'pointer' but they misuse the term.

These people must have written the Standard, then. In the definition of the
address operator, it says: 
"[T]he result is a pointer to the object or function designated  by its
operand".
Now what?


Christian

0
Reply christian.kandeler (96) 7/13/2005 7:14:00 AM

On Mon, 11 Jul 2005 23:33:05 +0300, Tommi Johnsson wrote:

> Lawrence Kirby wrote:

....

>> 
>> But this is an important example of where that is not the case. In
>> const int (*p)[5] p is already a pointer, it cannot "decay" further. 
> 
> in declaration like const int (*p)[5] it declares only type for p it 
> really doesnt decay anything

Right, any "decay" happens in usage not declaration. For example

   int a[10];
   int *pa;

   pa = a;


Here a is an array which is concerted to a pointer to its first element
for the assignment (the "decay"). There is nothing equivalent for p since
it is already a pointer, i.e. nothing that converts int (*)[5] to int **,
which would make no sense anyway.

Lawrence
0
Reply lknews (877) 7/13/2005 2:47:31 PM

Maxim S. Shatskih wrote:
>> Actually a pointer is neither a value nor a variable but a type; the word
>> is commonly used to refer to a variable with pointer type though, and this
> 
> Yes, and the value of the pointer type is also called "pointer" usually.
> 
> BTW - arrays and pointers are different for multu-dimension arrays. Imagine:
> 
>     int Array[10][5];
> 
> - and please tell me what will be the type of
> 
>     Array[1]
> 
> expression? Yes, the type will be "array of 5 integers", which is different
> from "int *" - it is indexed in a different way.
> 

It is not "indexed in a different way". All arrays, single- or
multi-dimensional, are indexed in exactly the same way. If you have an array 'a'
of type 'T[N]', in expression 'a[i]' array type will decay to pointer type 'T*'
and then the actual "indexing" will be applied to the resultant pointer.

For example, for an array declared as

  int Array1[10];

the type of 'Array1' is 'int[10]', but in expression

  Array1[5]

the type of 'Array1' will decay to 'int*' and then the usual pointer arithmetic
is applied ('*(Array1 + 5)'), giving us the 'int' we are looking for.

In your example with two-dimensional array the situation is essentially the
same. Two-dimensional array in C is noting more than just an array of arrays.
The rest just follows from that fact. The type of 'Array' is 'int[10][5]' which
decays to 'int(*)[5]' and the first index ('[1]') is applied to the resultant
pointer. The result of 'Array[1]' has type 'int[5]', as you correctly noted
above. Now, if you apply another index, 'int[5]' will decay to 'int*' and so on...

I don't see what here "is indexed in different way". Quite the opposite,
everything is indexed in exactly the same way.

--
Best regards,
Andrey Tarasevich
0
Reply andreytarasevich (1531) 7/13/2005 3:18:41 PM

On Mon, 11 Jul 2005 19:37:33 +0100, Chris Croughton wrote:

> On 11 Jul 2005 17:35:54 GMT, Chris Torek 
>    <nospam@torek.net> wrote:
> 
>> [Given "int arr[N];" and considering "&arr" vs "&arr[0]")

It is important to be clear that &arr and &arr[0] are fundamentally
different things. The only sense in which they are equal is that they
point to (different) objects that share the same starting byte, i.e.
(char *)&arr == (char *)&arr[0]. One points at an int, another points at
an array, so saying they are the same makes as much sense as given:

struct {
   int a;
   long b;
   double c;
} s;

saying that &s is the same as &s.a. And before you say anything, it isn't.
:-) One is a pointer to an int, the other is a pointer to a struct. That
makes them very different.

>> I think the real question boils down to whether &arr and &arr[0]
>> will compare equal under *all* "well-defined" conversions

The point is that they can't be compared directly. Pointer conversions are
a nasty business, once you've converted to a different pointer type you
can't really say that you're comparing the original value.

>> -- which
>> may even be only those to "char *" and "void *" -- and then I think
>> the answer is "yes", so that we can in fact say that the converted
>> values are always identical as long as we do a sensible conversion.

If you want to say that they point to objects sharing the same starting
byte address then say that, don't say that the pointers are the same. One
property of a pointer doesn't define the whole thing.

> Is that actually defined by the standard?  I remember that in some
> pre-standard C compilers arrays were actually implemented as pointers,
> so int arr[5]; would actually expand to the equivalent in
> pseudo-assember:
> 
>   arr:  dw  &_arr
>   _arr: dw  ?[5]
> 
> (The array pointer itself might be declared in a read-only segment.)
> 
> Is this sort of expansion actually banned by the standard, or is it
> "just not the done thing"?

The compiler can do whatever magic it likes so long as the program behaves
correctly. In standard C a array doesn't define a separate pointer object
so they must behave as if they don't. That goes for dynamically allocated
arrays and arrays of arrays too.

> In those compilers the effect would be the same as happens with "array
> parameters", taking &arr would get you the address of the pointer.

There is no such pointer object in standard C so that is not a valid
implementation. &arr gives a result of type pointer to array, not pointer
to pointer.

Lawrence
0
Reply lknews (877) 7/13/2005 4:03:40 PM

> What do you mean by this?  I don't know of any situation where an array of
> 5 integers and a pointer to int would be indexed differently.

int Array[10][5];
int** Ptr;

Array[2][3] is evaluated as *( Array + 2 * 5 + 3 )
Ptr[2][3]  is evaluated as *( *( Ptr + 2 ) + 3 ) - double indirection

So, a[i][j] is evaluated differently, depending on whether a is a pointer or an
array.

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com


0
Reply maxim19 (63) 7/13/2005 9:30:41 PM

Maxim S. Shatskih wrote:
>> What do you mean by this?  I don't know of any situation where an array of
>> 5 integers and a pointer to int would be indexed differently.
> 
> int Array[10][5];
> int** Ptr;
> 
> Array[2][3] is evaluated as *( Array + 2 * 5 + 3 )
> Ptr[2][3]  is evaluated as *( *( Ptr + 2 ) + 3 ) - double indirection
> 
> So, a[i][j] is evaluated differently, depending on whether a is a pointer or an
> array.
> 

Well, strictly speaking, the former is also evaluated as

  *( *( Array + 2 ) + 3 )

At purely syntactical level, the similarity between the two is still there.

The difference in terms of the "total number of indirections" exists for a
completely different reason. Take, for example, just the inner part - '*(Array +
2)'. There appears to be an indirection here. However, consider how this
expression is really interpreted: 'Array' decays from 'int[10][5]' to 'int
(*)[5]' (i.e. an implicit pointer is introduced), then 2 is added to that
pointer and then the pointer is dereferenced by '*' operator. In practice this
indirection is purely conceptual  - we create a temporary pointer and then
almost immediately dereference it. For this reason, there's no need to generate
any code that will implement any actual indirection here.

In case of 'Ptr' the situation is substantially different. The same inner
subexpression - '*(Ptr + 2)' - has a different meaning in this case. It does
indeed require an "explicit" indirection, i.e. the compiler is required to
generate code that will retrieve the pointer value stored in memory location
pointed by 'Ptr + 2'.

--
Best regards,
Andrey Tarasevich
0
Reply andreytarasevich (1531) 7/13/2005 10:18:02 PM

Alexei A. Frounze wrote:
>>
>> What's wrong in the original case is perfectly clear here. The warning is
> a
>> consequence of C language's const-correctness rules when they are applied
> to
>> arrays (and it's been mentioned here already).
>>
>> Simply speaking, in accordance with C language's const correctness rules,
> you
>> can perform an implicit 'T* -> const T*' conversion. Often people assume
> that,
>> say, 'int (*)[10] -> const int (*)[10]' conversion also belongs to that
> category
>> and should be performed implicitly without any warnings (that's what you
>> assumed, apparently). Unfortunately, this is not the case. In C language
> it is
>> not possible to const-qualify an array type as a whole. Period. Any
> attempts to
>> const-qualify an array type will actually const-qualify the type of the
> array
>> _elements_ not the array type itself. In other words, there's no situation
> in C
>> when the 'T* -> const T*' can be applied to pointers-to-arrays, since the
> proper
>> destination type cannot ever exist in C.
>> ...
> 
> Which means C's types are wrong since the T that is an array is treated
> differently from T that is not.

Yes, it is indeed treated differently.

> If we define:
> typedef int(tArrayType)[5];
> then still an object of type tArrayType is pretty much different from say
> typedef struct tArrayType {/*some member(s)*/} tArrayType;
> In the first case we'll get a warning when passing &T to a function
> declaring
> argument as "const* T" but we won't get the warning in the second case.

That's true. "Hiding" an array type behind a typedef-name does not make it to
loose its second-class-citizen properties. It doesn't become assignable, it
doesn't become const-qualifyable as a whole. That's just the way it is in C.

--
Best regards,
Andrey Tarasevich
0
Reply andreytarasevich (1531) 7/13/2005 10:21:28 PM

On Thu, 14 Jul 2005 01:30:41 +0400, Maxim S. Shatskih wrote:

>> What do you mean by this?  I don't know of any situation where an array
>> of 5 integers and a pointer to int would be indexed differently.
> 
> int Array[10][5];
> int** Ptr;
> 
> Array[2][3] is evaluated as *( Array + 2 * 5 + 3 ) Ptr[2][3]  is evaluated
> as *( *( Ptr + 2 ) + 3 ) - double indirection
> 
> So, a[i][j] is evaluated differently, depending on whether a is a pointer
> or an array.

Agreed, but your wording implied 1-dimensional arrays, not 2-dimensional
arrays.
0
Reply netocrat (497) 7/13/2005 10:22:50 PM

"Netocrat" <netocrat@dodo.com.au> wrote in message
news:pan.2005.07.12.22.05.14.670298@dodo.com.au...
> On Tue, 12 Jul 2005 10:32:10 +0400, Alexei A. Frounze wrote:
>
> > "Netocrat" <netocrat@dodo.com.au> wrote in message
> > news:pan.2005.07.11.02.51.38.950654@dodo.com.au...
> >> On Mon, 11 Jul 2005 09:29:27 +1000, Netocrat wrote:
> >>
> >> > [D]ouble indirection can easily be used to gain access to the
> >> > supposedly protected const type by assigning the middle pointer to a
> >> > non-const pointer. There's no way the compiler can detect this, so
> >> > disallowing automatic const-conversion for double-indirection
> >> > parameters makes sense.
> >>
> >> I'm talking nonsense.  You can't assign the middle pointer to a
> >> non-const pointer without a cast.  But you can use the automatic
> >> conversion to violate the const protection: quoting "Me" in another
> >> thread:
> >>
> >> const char c = 'c';
> >> char * pc;
> >> const char ** pcc = & pc ; /* not allowed */
> >> *pcc = & c;
> >> *pc = 'C'; /* would modify a const object if the conversion above were
> >>             * allowed */
> >
> > Correct, but I don't think it's a good example.
>
> I think it's a great example for what it's intended to show.  What it
> shows is that the const protection you are giving your c variable is
> useless if a char ** variable can be automatically cast to const char **.
> So really it's showing: if you want to be able to trust that the const
> qualifier actually does what it's supposed to do, then automatic
> conversions from char ** to const char ** _cannot_ be allowed.
>
> It may not be as useful in explaining how this relates to your case.  You
> seem to be arguing that an array should be treated as though it were a
> simple type, rather than as an indirected type, in this case.  Actually I
> don't see any problems with doing that.  The trick above can't be used
> when pc is declared as a char array rather than char pointer, since then
> we can't change its base address.  So perhaps you could argue something
> like this:
>
> Since the type char [] is fundamentally different from char * in that the
> base address of the variable it defines cannot be modified, the automatic
> conversion from char (*)[] to const char (*)[] is not unsafe, as the
> automatic conversion from char ** to const char ** is, and should be
> allowed.

The thing about the base address is that I can use any index/offset to get
outside the allowed memory region, be it array or a pointer, I can simply
write:
ptr[123456] = 0; or
arr[-987654] = 1;
whatever. C will let me shoot in the foot :) As I showed, gcc doesn't warn
about writing past the array's last element. At least, it did not do so with
the -Wall option, which I normally use.

> I don't see any problems with this statement (others may...).  But the
> point is, regardless of whether this statement is true or not, it seems to
> me (and again I must reiterate that I don't know what the standard says on
> this) that C does treat these types equivalently for const-casting
> purposes.
>
> > Simply being able to
> > modify something by pc and being unable to modify it by ppc is just
fine,
> > so is in the simpler example:
> > char c = 'A';
> > char *pc = &c;
> > const char *pc_ = &c;
> > *pc = 'B'; // OK
> > *pc_ = 'B'; // prohibited
>
> Yes this is all OK because the automatic conversion of &c from type
> char* to type const char* can not be used in any way to surreptitiously
> write to any const-protected values.
>
> This "simpler" example is indeed just that - because it doesn't deal with
> double indirection or pointers to array at all - which is the case in
> point.  If you really want to use a simple example that applies to our
> discussion, we could change the character types to character array types
> and extend it thus:
>
> char c[5] = "abcde";
> char (*pc)[5] = &c;
> const char (*pc_)[5] = &c; /* warning - prohibited */
> (*pc)[1] = 'B'; /* OK */
> (*pc_)[1] = 'B'; /* prohibited */
>
> This illustrates your case better.  As I have argued above - to echo
> what I understand to be your fundamental argument - there is no semantic
> reason for prohibiting the automatic casting of &c from char (*)[5] to
> const char (*)[5].

Yep.

> Nevertheless in C it is prohibited.  C'est la vie.

Sh!t.

> Apparently the casting rules are the same as for char ** to const char **
> - for which there _is_ a good reason for prohibition.  We might not think
> it necessary or appropriate, but that's just the way C works in this case.

:(

> Another note - // comments are only allowed by the standard in C99, and
> since you didn't specifically mention using C99 I presume you are not
> using switches on gcc to invoke standards-compliant behaviour at all,
> which you really should. Using -ansi -pedantic makes it reject non-C90
> compliant code and using -std=C99 -pedantic does same for C99.  -W and
> -Wall are good for additional non-standard-specific warnings.

Well, there used to be a lot of nonstandard (not yet standardized) features,
among which // is more or less harmless. I do not know many good compilers
that do not support // but nevertheless are of some good value to the
developers.
But let's not get away from the topic for now.

> > This is how most of libc's functions are declared -- their arguments are
> > of type of a pointer to a constant value.
>
> I think you mean "const-qualifed type" rather than "constant value", but I
> understand you.

Well, I may not be expressing myself well in terms of C's jargon, but I hope
I write clearly enough to be understood even though I'm not speaking English
natively :)

> > This is what I'd like to have
> > with the array too. I'm not really sure why enforcing protection in this
> > simple case above is OK (i.e. pc=&c;) but not OK in case of using the
> > array as an object to be passed by reference/pointer.
>
> Neither am I because it doesn't seem to serve any protective purpose,
> but as I've tried to explain it seems to be because the array is being
> treated equivalently to the case of a pointer, where a protective purpose
> _is_ served.

OK, how about:
const char* * const p
?
Would it be better than simply
const char** p
?

Sorry, I maybe talking nonsense at the moment... But if the dereferenced
character is protected by const (or is it not?), then I'm not sure of the
problem, the write/assignment should be prohibited.

> > I'm fine with
> > warnings and errors caused by the improper use of a pointer to a
> > constant object, e.g. attempt to modify the constant object. That's
> > perfectly understandable. Why cannot I enforce such a protection by
> > treating some object (not necessarily one defined as constant) as a
> > constant one
>
> You can in some simple cases as your example shows, so I don't know why
> you're arguing here that you cannot.

I cannot with arrays. :)

....
> Now to respond to the message you posted elsewhere:
>
> On Tue, 12 Jul 2005 11:06:33 +0400, Alexei A. Frounze wrote:
>
> > "Netocrat" <netocrat@dodo.com.au> wrote in message
> > news:pan.2005.07.11.11.06.43.292147@dodo.com.au... ...
> >> That's true, but a pointer to an object can be treated as an array of
> >> those objects and this is no less true for pointers to arrays - which
> >> can be treated as arrays of arrays. So for index other than 0
> >>
> >> ap[index] != (*ap)[index]
> >>
> >> In fact accessing ap[1] in this instance is illegal but conceptually it
> >> is an access of the second "5 element integer array" element of ap.
> >
> > OK, I agree with this kind of double indirection.
> >
> > The prob is though that we have different semantics for pointer to some
> > object being not an array and for pointer to some object being an array
in
> > the sense that these two objects are treated differently.
>
> Well that's not a totally accurate statement of the problem, because in
> some cases (ie when that "some object" is a pointer) there really _is_
> a need for different treatment as "Me"'s example shows.  But if you
> rewrite that statement as "We have different semantics for pointer to
> array and for pointer to any other non-pointer type", then I agree.

Doesn't matter whether you say pointer or array. We both see the same
problem with [const]char** and [const]int(*)[]. So, yes, we do have
different semantics... To me its more of an inconsistency. C is a great
language, but it really does have some problems and peculiarities someone
should be aware of (say, two ints are multipled, and the product is assigned
to a long. what a normal human being expects to get isn't something the
experienced programmer knows to get in reality).

> There doesn't seem to be a compelling argument for pointer to array being
> treated any differently to other non-simple types, such as structs.  To
> again modify your simple example:
>
> struct s {
>         int x;
>         int y;
> };
>
> struct s c;
> struct s *pc = &c;
> const struct s *pc_ = &c; /* no prohibition here as there is when c is an
>                            * array */
> pc->x = 1; /* OK */
> pc_->x = 2; /* prohibited */

Sure. Now I think what if I typdef or union the array with something else to
hide the array type... Would I still get the warning? Or would I get a
different one? Sure this is something stupid to do, but what the heck, since
we're into, there are maybe some posibilities :)

> > If this is
> > because the 2nd pointer is seen as a pointer to pointer (simply because
> > it unfortunately points to an array)
>
> As I've explained I believe that's the cause, but I don't know the
> standard well enough to confirm this.
>
> > and we have such restrictions on
> > pointers to pointers,
>
> For good reason, as the example I quoted shows.

Can you show *the bad* code, where this automatic pointer type conversion
(if considered to be fully allowed) yields something real nasty? I'm getting
lost...

> > then something is wrong in C or gcc.
>
> That's a strong statement, and clearly as it applies to char ** conversion
> its inaccurate, but as it applies to treating pointers to arrays
> equivalently with double pointers, in this case your opinion that
> "something is wrong in C" appears to be at the least a proposition that
> could reasonably be argued.

Maybe...

> > Like I say in
> > the other today's post, I don't see a good reason why an attempt to
> > enforce treatment of some object as being constant (throught the use of
> > a pointer to constant) is considered bad. Let's then forget about the
> > array and indeed switch to a pointer to pointer:
> > char c = 'A';
> > char *p = &c;
> > char **pp = &p; // OK
> > const char **pp_ = &p; // warned
> > But all what type of pp_ means is that I cannot modify a char that is
> > somehow doubly dereferenced by pp_. And I think I must be allowed to
> > have this kind of assignment (pp_ = &p; in the above) because instead of
> > loosening the protection I try to enforce it. This is the point now.
>
> But Alex you're now ignoring the lesson of the original example I quoted.

I'm lost. That's it. :)

> You're effectively saying, "the automatic conversion from char ** to
> const char ** may have caused problems in Me's example, but it doesn't
> in this case so it should be allowed here."

I might missed that "Me" example you're referring to...
Wait! This one:
const char c = 'c';
char * pc;
const char ** pcc = & pc ; /* not allowed */
*pcc = & c;
*pc = 'C'; /* would modify a const object if the conversion above were
             * allowed */
Shoot. I didn't read it with enough attention. I see it now.
But ... what a perverted mind one must have to do something like that!
Cetainly, that wasn't something I expected to see nor do myself!!! At least,
not now, having programmed in C for quite some years.

> Well and good, but in general
> the compiler can't know when such an automatic conversion would cause
> problems and when it wouldn't, which is why there is a rule that applies
> in _all_ cases.
>
> So in summary:
> (a) it appears that there is no way around your problem without using a
> cast.  The cast doesn't appear to violate any const-protection semantics
> as it would in the case where aInt3 was declared int * rather than int[5].
> Casts in general are unwise though because they can mask useful (or
> required) warnings, so be wary before using one.
> (b) the prohibition of an automatic cast in this case does seem according
> to const safety semantics to be unnecessary.

A big thanks,
Alex
P.S. where was I when I saw that weird example, huh? :)


0
Reply alexfru (352) 7/14/2005 12:48:24 AM

On Wed, 13 Jul 2005 17:03:40 +0100, Lawrence Kirby wrote:

> On Mon, 11 Jul 2005 19:37:33 +0100, Chris Croughton wrote:
> 
>> On 11 Jul 2005 17:35:54 GMT, Chris Torek
>>    <nospam@torek.net> wrote:
>> 
>>> [Given "int arr[N];" and considering "&arr" vs "&arr[0]")
> 
> It is important to be clear that &arr and &arr[0] are fundamentally
> different things. The only sense in which they are equal is that they
> point to (different) objects that share the same starting byte, i.e. (char
> *)&arr == (char *)&arr[0].

<snip further explanation>

Perhaps you missed Chris Torek's full post; because he made an identical
point to yours which the abbreviation quoted above omits.

>>> I think the real question boils down to whether &arr and &arr[0] will
>>> compare equal under *all* "well-defined" conversions

<snip>

> If you want to say that they point to objects sharing the same starting
> byte address then say that, don't say that the pointers are the same.
> One property of a pointer doesn't define the whole thing.
 
<snip>

> The compiler can do whatever magic it likes so long as the program
> behaves correctly. In standard C a array doesn't define a separate
> pointer object so they must behave as if they don't. That goes for
> dynamically allocated arrays and arrays of arrays too.

So to clarify, would you agree with the assertions that given the
declaration int arr[10];

a) &arr and &arr[0] are required by the standard to always point to
objects sharing the same starting byte address 

b) the values of &arr and &arr[0], provided that they can be cast to
equivalent pointer types with no loss of information, are required by the
standard to always be equal?

0
Reply netocrat (497) 7/14/2005 1:54:57 AM

On Thu, 14 Jul 2005 04:48:24 +0400, Alexei A. Frounze wrote:

<snip>

> The thing about the base address is that I can use any index/offset to get
> outside the allowed memory region, be it array or a pointer, I can simply
> write:
> ptr[123456] = 0; or
> arr[-987654] = 1;
> whatever. C will let me shoot in the foot :) As I showed, gcc doesn't warn
> about writing past the array's last element. At least, it did not do so
> with the -Wall option, which I normally use.

I agree that it is reasonable to expect that the -Wall option of gcc
should warn about obvious cases where a constant index exceeds the array
bounds.  In the case where you have assigned the array to a pointer it
isn't so obvious and I wouldn't expect it, but it would be a nice bonus.
Probably a tool such as lint would provide this capability.

<snip>

> C is a great
> language, but it really does have some problems and peculiarities
> someone should be aware of (say, two ints are multipled, and the product
> is assigned to a long. what a normal human being expects to get isn't
> something the experienced programmer knows to get in reality).

Agreed - casting requirements aren't immediately obvious as a C beginner.
Once you understand the paradigm they are - except for a few intricacies
like the case in point, which are not straightforward.

<snip>

> Sure. Now I think what if I typdef or union the array with something
> else to hide the array type... Would I still get the warning? Or would I
> get a different one? Sure this is something stupid to do, but what the
> heck, since we're into, there are maybe some posibilities :)

You could place it as the only element of a struct.  I don't see any
advantage of a union over a struct.  Then if you wanated to you could
typedef the struct so that declaring the array is simpler (many frown on
hiding structs with typedef when it's not strictly necessary with good
reason, but it's personal preference in the end).  It's a workaround but
it will achieve what you want; with the proviso that your array must be of
constant size unless you use C99 - otherwise you will need a separate
structure type for each array size.

<snip>

> I might missed that "Me" example you're referring to... Wait! This one:
> [...] Shoot. I didn't read it with enough attention. I see it now. But
> ... what a perverted mind one must have to do something like that!
> Cetainly, that wasn't something I expected to see nor do myself!!! At
> least, not now, having programmed in C for quite some years. 

Yes it's subtle - creatively perverted.

<snip>

> where was I when I saw that weird example, huh? :)

In the same place we go when we read several pages of a novel that
directly afterwards we can't remember anything from.

0
Reply netocrat (497) 7/14/2005 3:21:57 AM

> Agreed - casting requirements aren't immediately obvious as a C beginner.

With "const", all is clear. The "const" attribute of the lvalue cannot be
removed without the explicit cast - neither in C nor on C++.

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com


0
Reply maxim19 (63) 7/14/2005 3:27:23 AM

"Maxim S. Shatskih" <maxim@storagecraft.com> writes:

> With "const", all is clear. The "const" attribute of the lvalue cannot be
> removed without the explicit cast - neither in C nor on C++.

Sure it can--try calling a function like strchr().
-- 
int main(void){char p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.\
 \n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof p-1;putchar(p[i]\
);}return 0;}
0
Reply blp (3953) 7/14/2005 3:40:51 AM

Ben Pfaff wrote:
> "Maxim S. Shatskih" <maxim@storagecraft.com> writes:
> > With "const", all is clear. The "const" attribute of the lvalue cannot be
> > removed without the explicit cast - neither in C nor on C++.
>
> Sure it can--try calling a function like strchr().

Or just...

  char *foo();

  void bah(const char *x)
  {
    char *y = foo(x);
    ...
  }

  char *foo(char *x)
  {
    return x;
  }

-- 
Peter

0
Reply airia (1802) 7/14/2005 6:17:57 AM

On Wed, 13 Jul 2005 23:17:57 -0700, Peter Nilsson wrote:

> Ben Pfaff wrote:
>> "Maxim S. Shatskih" <maxim@storagecraft.com> writes:
>> > With "const", all is clear. The "const" attribute of the lvalue cannot
>> > be removed without the explicit cast - neither in C nor on C++.
>>
>> Sure it can--try calling a function like strchr().
> 
> Or just...
> 
>   char *foo();
> 
>   void bah(const char *x)
>   {
>     char *y = foo(x);
>     ...
>   }
>   }
>   char *foo(char *x)
>   {
>     return x;
>   }

Neither of these examples removes a const attribute from an lvalue, which
is impossible by definition.  An lvalue can be assigned to.  If it had a
const attribute that was to be removed, you couldn't assign to it and it
wouldn't be an lvalue in the first place.

I believe though that you've correctly interpreted what Maxim meant as
opposed to what actually said.

0
Reply netocrat (497) 7/14/2005 6:34:55 AM

> >   char *foo();

Oh, sorry. The only language for which I've read the formal description was C++
(old one - circa 1993) and not C. In C++, such things are impossible.

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com


0
Reply maxim19 (63) 7/14/2005 6:40:50 AM

Andrey Tarasevich wrote:
> It is not "indexed in a different way". All arrays, single- or
> multi-dimensional, are indexed in exactly the same way. 

Nope, they're not. The equivalence stops at one-dimensional arrays. 
Multidimensional arrays aren't addressed the same way as 
multi-indirectioned (sp?) pointers.

That is to say, int foo[10][20] doesn't decay to int **.

Multi-dimensional arrays store all of their elements consecutively, the 
single elements are accessed by means of pointer math on the dimensions 
and size of the elements, whilst multi-indirectioned pointers are simply 
pointers (to pointers... to pointers) to the elements:


int foo[3][3] :

+--+--+--+
|  |  |  |
+--+--+--+
|  |  |  |
+--+--+--+
|  |  |  |
+--+--+--+

int **baz :

+--+--+--+ ...
|  |  |  | ...
+--+--+--+ ...
  || || ||
  \/ \/ \/
+--+--+--+
|  +  +  |
+--+--+--+
|  +  +  |
+--+--+--+
|  +  +  |
+--+--+--+
...........
...........
...........


-- 
Fabio Alemagna
0
Reply dont5491 (10) 7/14/2005 6:50:02 AM

On Thu, 14 Jul 2005 10:40:50 +0400, Maxim S. Shatskih wrote:

>> >   char *foo();
> 
> Oh, sorry. The only language for which I've read the formal description
> was C++ (old one - circa 1993) and not C. In C++, such things are
> impossible.

Actually assuming you that by "lvalue" you meant "expression" what you
said was correct anyway.  You said that the const attribute cannot be
removed from an expression without an explicit cast, which is true.

Looking again at the examples given by Ben Pfaff and Peter Nilsson,
they're showing the const attribute being added, rather than removed,
without an explicit cast.
0
Reply netocrat (497) 7/14/2005 7:02:09 AM

Fabio Alemagna wrote:
>> It is not "indexed in a different way". All arrays, single- or
>> multi-dimensional, are indexed in exactly the same way. 
> 
> Nope, they're not. 

Yes, they are.

> The equivalence stops at one-dimensional arrays. 
> Multidimensional arrays aren't addressed the same way as 
> multi-indirectioned (sp?) pointers.

That's true. But in my message I'm talking about the difference between
the way single- and multi-dimensional arrays are addressed. No pointers
involved (multi-indirectioned or not). You for some reason start talking
about pointers. Why?

> That is to say, int foo[10][20] doesn't decay to int **.

That's true. But, once again, how is this relevant?

> [skipped]

Once again, true. But I still don't see how all this applies to what I
said in my message.

-- 
Best regards,
Andrey Tarasevich

0
Reply andreytarasevich (1531) 7/14/2005 8:28:40 AM

Netocrat wrote:
> ...
> Neither of these examples removes a const attribute from an lvalue, which
> is impossible by definition.  An lvalue can be assigned to.  

Huh? No. By definition, lvalue is something that has address in storage. In
general case lvalue cannot be assigned to. Only _modifyable_ lvalue can be
assigned to, but there are also non-modifyable lvalues.

> If it had a
> const attribute that was to be removed, you couldn't assign to it and it
> wouldn't be an lvalue in the first place.

Not true. You seem to assume that assignability is a defining property of
"lvalueness". That's simply not true.

--
Best regards,
Andrey Tarasevich
0
Reply andreytarasevich (1531) 7/14/2005 9:47:13 AM

On Thu, 14 Jul 2005 02:47:13 -0700, Andrey Tarasevich wrote:

> Netocrat wrote:
>> ...
>> Neither of these examples removes a const attribute from an lvalue, which
>> is impossible by definition.  An lvalue can be assigned to.  
> 
> Huh? No. By definition, lvalue is something that has address in storage. In
> general case lvalue cannot be assigned to. Only _modifyable_ lvalue can be
> assigned to, but there are also non-modifyable lvalues.

Right, my concept of lvalue was slightly out.  Non-modifiable being for
example arrays and structs.

Maxim's original statement then is accurate:

> With "const", all is clear. The "const" attribute of the lvalue cannot
> be removed without the explicit cast - neither in C nor on C++.

>> If it had a
>> const attribute that was to be removed, you couldn't assign to it and it
>> wouldn't be an lvalue in the first place.
> 
> Not true. You seem to assume that assignability is a defining property of
> "lvalueness". That's simply not true.

On reading the standard I see that you are correct.

0
Reply netocrat (497) 7/14/2005 11:05:27 AM

Fabio Alemagna wrote:
> Andrey Tarasevich wrote:
> 
>> It is not "indexed in a different way". All arrays, single- or
>> multi-dimensional, are indexed in exactly the same way.
> 
> Nope, they're not. The equivalence stops at one-dimensional arrays.
> Multidimensional arrays aren't addressed the same way as
> multi-indirectioned (sp?) pointers.
> 
> That is to say, int foo[10][20] doesn't decay to int **.

Which is because there are no multi-dimensioned arrays in C, there
are just arrays of arrays.  An array of pointers is not a
multidimensioned array, or even a fake of one linearized.

-- 
"If you want to post a followup via groups.google.com, don't use
 the broken "Reply" link at the bottom of the article.  Click on 
 "show options" at the top of the article, then click on the 
 "Reply" at the bottom of the article headers." - Keith Thompson

0
Reply cbfalconer (19183) 7/14/2005 12:34:38 PM

In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:

>  char *foo();
> 
>  void bah(const char *x)
>  {
>    char *y = foo(x);

You raise UB here: (const char*) and (char*) types are
not compatible - you cannot pass incompatible arguments
to a function call (cf. 6.5.2.2#6).

>    ...
>  }
> 
>  char *foo(char *x)
>  {
>    return x;
>  }

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/14/2005 12:45:33 PM

In comp.lang.c Netocrat <netocrat@dodo.com.au> wrote:
> On Thu, 14 Jul 2005 02:47:13 -0700, Andrey Tarasevich wrote:
>> Netocrat wrote:

>>> ...
>>> Neither of these examples removes a const attribute from an lvalue, which
>>> is impossible by definition.  An lvalue can be assigned to.  
>> 
>> Huh? No. By definition, lvalue is something that has address in storage. In
>> general case lvalue cannot be assigned to. Only _modifyable_ lvalue can be
>> assigned to, but there are also non-modifyable lvalues.
> 
> Right, my concept of lvalue was slightly out.  Non-modifiable being for
> example arrays and structs.

I'm not sure it's that simple with storage for lvalues.  Take `register'
variables for an example, or pointers to objects whose lifetime has
finished (dereferencing such a pointer is an lvalue, whether
the object exists or not).

Consider also this example:

  struct s { int a[1]; };
  struct s f(void);

  f().a[0] = 7;  //UB

The expression on the left is clearly a modifiable lvalue.
But does it take any storage?  I think C++ is more verbose about
temporaries; they can be optimized out, which is unspecified.
I don't think C even has an idea of a temporary.

OTOH, f().a decays into a pointer to its first element, therefore
the array the pointer points to must be an object, therefore it 
must (temporarily) take some storage.  Is that a right conclusion?

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/14/2005 1:39:16 PM

Netocrat <netocrat@dodo.com.au> wrote:

> On Thu, 14 Jul 2005 02:47:13 -0700, Andrey Tarasevich wrote:
> 
> > Netocrat wrote:
> >> ...
> >> Neither of these examples removes a const attribute from an lvalue, which
> >> is impossible by definition.  An lvalue can be assigned to.  
> > 
> > Huh? No. By definition, lvalue is something that has address in storage. In
> > general case lvalue cannot be assigned to. Only _modifyable_ lvalue can be
> > assigned to, but there are also non-modifyable lvalues.
> 
> Right, my concept of lvalue was slightly out.  Non-modifiable being for
> example arrays and structs.

Nope, only arrays, not structs. Structs can be assigned to. (You might
be confusing this with the lack of struct constants in C89. This lack
means that, if you want to assign something to a struct, in must be
another struct; there's no such thing as i=4 for structs, only i=j. Or
perhaps by the possibility of structs being incomplete (see below); but
this isn't true for structs in general.)
Other non-modifiable lvalues are incomplete types (unfinished
variable-length arrays, declared but not yet defined structs, etc.),
const objects (no surprise there), or structs or unions with
non-modifiable members.

Richard
0
Reply rlb (4118) 7/14/2005 3:03:21 PM

CBFalconer wrote:
>>That is to say, int foo[10][20] doesn't decay to int **.
> 
> 
> Which is because there are no multi-dimensioned arrays in C,

Of course there are, you quoted one above.

-- 
Fabio Alemagna

http://xoomer.virgilio.it/scene_di_ordinaria_follia/
0
Reply dont5491 (10) 7/14/2005 6:47:35 PM

Andrey Tarasevich wrote:
>>The equivalence stops at one-dimensional arrays. 
>>Multidimensional arrays aren't addressed the same way as 
>>multi-indirectioned (sp?) pointers.
> 
> 
> That's true. But in my message I'm talking about the difference between
> the way single- and multi-dimensional arrays are addressed. No pointers
> involved (multi-indirectioned or not). You for some reason start talking
> about pointers. Why?

Want an honest answer? I have no idea, sorry :-| Dunno what the heck I 
was thinking about :-)

-- 
Fabio Alemagna

http://xoomer.virgilio.it/scene_di_ordinaria_follia/
0
Reply dont5491 (10) 7/14/2005 6:48:52 PM

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Fabio Alemagna wrote:
> CBFalconer wrote:
> 
>>> That is to say, int foo[10][20] doesn't decay to int **.
>>
>>
>>
>> Which is because there are no multi-dimensioned arrays in C,
> 
> 
> Of course there are, you quoted one above.

Nope. That's not a multi-dimensional array.

That's an array of 10 elements, each element being an array of 20 ints.

- --
Lew Pitcher
IT Specialist, Enterprise Data Systems,
Enterprise Technology Solutions, TD Bank Financial Group

(Opinions expressed are my own, not my employers')
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (MingW32)

iD8DBQFC1rT/agVFX4UWr64RAmMZAJ4kxJO5L13YdlEfpNylxup8GzOiAQCg0eTU
K9j0Yat8G0REQ52iU7GJ5QU=
=1ba/
-----END PGP SIGNATURE-----
0
Reply Lew.Pitcher (530) 7/14/2005 6:54:55 PM

Lew Pitcher wrote:
>>>
>>> Which is because there are no multi-dimensioned arrays in C,
>> 
>> 
>> Of course there are, you quoted one above.
> 
> Nope. That's not a multi-dimensional array.
> 
> That's an array of 10 elements, each element being an array of 20 ints.
> ...

So? "Multi-dimensional array" is just a concept. There are different ways to
implement this concept. Can anyone please explain to me, why the C-language's
approach to multidimensional arrays does not qualify as a valid implementation
of that concept? I hear it again and again, but somehow nobody is ever able to
come up with a more-or-less rational basis for such statement.

--
Best regards,
Andrey Tarasevich
0
Reply andreytarasevich (1531) 7/14/2005 8:02:38 PM

Lew Pitcher wrote:
>>>>That is to say, int foo[10][20] doesn't decay to int **.
>>>
>>>Which is because there are no multi-dimensioned arrays in C,
>>
>>Of course there are, you quoted one above.
> 
> Nope. That's not a multi-dimensional array.
> 
> That's an array of 10 elements, each element being an array of 20 ints.

Precisely, a multidimensional array.

-- 
Fabio Alemagna

http://xoomer.virgilio.it/scene_di_ordinaria_follia/
0
Reply dont5491 (10) 7/14/2005 8:07:23 PM

"Andrey Tarasevich" <andreytarasevich@hotmail.com> wrote in message
news:11ddh53rcvnbka1@news.supernews.com...
> Lew Pitcher wrote:

Guys, if there's nothing to add to my initial post (I think so as I have no
further questions on the subject), you may exclude alt.os.development from
the target.

Thanks,
Alex


0
Reply alexfru (352) 7/14/2005 8:09:28 PM

Fabio Alemagna wrote:
> Lew Pitcher wrote:
>
(and somebody foolishly stripped all the attributions)
>
>>>>> That is to say, int foo[10][20] doesn't decay to int **.
>>>>
>>>> Which is because there are no multi-dimensioned arrays in C,
>>>
>>> Of course there are, you quoted one above.
>>
>> Nope. That's not a multi-dimensional array.  That's an array of
>> 10 elements, each element being an array of 20 ints.
> 
> Precisely, a multidimensional array.

No, it is an array of 200 ints, which can be mapped into an
array[10] of array[20] of int.  It can also be mapped into an
array[20] of array[10], or array[2] of array[100], etc.  The
notation is just a means of getting the compiler to do some of the
arithmetic for you, and the technique fails all too often,
especially when you don't understand what is going on.

The memory people have gone to a lot of trouble to present that
memory as a linear array.  You wouldn't like it if you had to deal
with the reality, involving row and column strobes, chip selects,
etc.  In the old days you also had to deal with inhibit current.

-- 
"If you want to post a followup via groups.google.com, don't use
 the broken "Reply" link at the bottom of the article.  Click on 
 "show options" at the top of the article, then click on the 
 "Reply" at the bottom of the article headers." - Keith Thompson


0
Reply cbfalconer (19183) 7/14/2005 10:19:51 PM

S.Tobias wrote:
> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
>
> >  char *foo();
> >
> >  void bah(const char *x)
> >  {
> >    char *y = foo(x);
>
> You raise UB here: (const char*) and (char*) types are
> not compatible - you cannot pass incompatible arguments
> to a function call (cf. 6.5.2.2#6).

C99 has different wording from N869.

[It looks like it's not valid C90 though.]

> >    ...
> >  }
> > 
> >  char *foo(char *x)
> >  {
> >    return x;
> >  }

-- 
Peter

0
Reply airia (1802) 7/15/2005 1:10:33 AM

CBFalconer wrote:
> No, it is an array of 200 ints,

You can't change your definition each time you answer. Before you said 
it's an array of an array, now you say it's just an array, 'cause that 
suits your goal better now.

> which can be mapped into an
> array[10] of array[20] of int.  It can also be mapped into an
> array[20] of array[10], or array[2] of array[100], etc. 

So what? That's an implementation detail. To make you happy the 2nd 
dimension arrays should have been put where, exactly?

> The
> notation is just a means of getting the compiler to do some of the
> arithmetic for you, and the technique fails all too often,
> especially when you don't understand what is going on.

Please, can you give examples about such failures? I've never 
encountered any.

> The memory people have gone to a lot of trouble to present that
> memory as a linear array.  You wouldn't like it if you had to deal
> with the reality, involving row and column strobes, chip selects,
> etc.  In the old days you also had to deal with inhibit current.

Hm? What's got that to do with multidimensional arrays in C?

Ok, let's do something constructive: please, explain what would be a 
"real" multidimensional array.


-- 
Fabio Alemagna

http://xoomer.virgilio.it/scene_di_ordinaria_follia/
0
Reply dont5491 (10) 7/15/2005 3:44:31 AM

On Thu, 14 Jul 2005 20:07:23 +0000, Fabio Alemagna wrote:

> Lew Pitcher wrote:
>>>>>That is to say, int foo[10][20] doesn't decay to int **.
>>>>
>>>>Which is because there are no multi-dimensioned arrays in C,
>>>
>>>Of course there are, you quoted one above.
>> 
>> Nope. That's not a multi-dimensional array.
>> 
>> That's an array of 10 elements, each element being an array of 20 ints.
> 
> Precisely, a multidimensional array.

This is easily settled by the standard.

N869, 6.7.5.2:

When several ``array of'' specifications are adjacent, a multidimensional
array is declared.

There's similar language in the C89 draft.

0
Reply netocrat (497) 7/15/2005 4:26:16 AM

On Thu, 14 Jul 2005 13:39:16 +0000, S.Tobias wrote:

> In comp.lang.c Netocrat <netocrat@dodo.com.au> wrote:
>> On Thu, 14 Jul 2005 02:47:13 -0700, Andrey Tarasevich wrote:
>>> Netocrat wrote:
> 
>>>> ...
>>>> Neither of these examples removes a const attribute from an lvalue, which
>>>> is impossible by definition.  An lvalue can be assigned to.  
>>> 
>>> Huh? No. By definition, lvalue is something that has address in storage. In
>>> general case lvalue cannot be assigned to. Only _modifyable_ lvalue can be
>>> assigned to, but there are also non-modifyable lvalues.
>> 
>> Right, my concept of lvalue was slightly out.  Non-modifiable being for
>> example arrays and structs.
>
> I'm not sure it's that simple with storage for lvalues.  Take `register'
> variables for an example, or pointers to objects whose lifetime has
> finished (dereferencing such a pointer is an lvalue, whether the object
> exists or not).

The C89 draft says:

"The unary * operator denotes indirection. If the operand points to ... an
object, the result is an lvalue designating the object... If an invalid
value has been assigned to the pointer, the behavior of the unary *
operator is undefined."

Since in this case the pointer has an invalid value it would be illegal to
dereference it; the question of whether or not the result would be an
lvalue is moot.

> Consider also this example:
> 
>   struct s { int a[1]; };
>   struct s f(void);
> 
>   f().a[0] = 7;  //UB
> 
> The expression on the left is clearly a modifiable lvalue.

Not so clearly according to gcc.  I get:
"ISO C90 forbids subscripting non-lvalue array".

> But does it take any storage?  I think C++ is more verbose about
> temporaries; they can be optimized out, which is unspecified.
> I don't think C even has an idea of a temporary.

It doesn't occur in the context of variable storage in either draft.
 
> OTOH, f().a decays into a pointer to its first element, therefore
> the array the pointer points to must be an object, therefore it 
> must (temporarily) take some storage.  Is that a right conclusion?

If f() were a modifiable lvalue which in this context it does not appear
to be, then I would see nothing wrong with your reasoning.  However given
that there's no means of later reading back the assignment, it's really a
no-op and again the question is moot: nothing changes whether the
assignment occurs or not.  Perhaps that's why C99 allows it.

0
Reply netocrat (497) 7/15/2005 5:30:16 AM

On Thu, 14 Jul 2005 15:03:21 +0000, Richard Bos wrote:

> Netocrat <netocrat@dodo.com.au> wrote:
> 
>> On Thu, 14 Jul 2005 02:47:13 -0700, Andrey Tarasevich wrote:
>> 
>> > Netocrat wrote:
>> >> ...
>> >> Neither of these examples removes a const attribute from an lvalue, which
>> >> is impossible by definition.  An lvalue can be assigned to.  
>> > 
>> > Huh? No. By definition, lvalue is something that has address in storage. In
>> > general case lvalue cannot be assigned to. Only _modifyable_ lvalue can be
>> > assigned to, but there are also non-modifyable lvalues.
>> 
>> Right, my concept of lvalue was slightly out.  Non-modifiable being for
>> example arrays and structs.
> 
> Nope, only arrays, not structs. Structs can be assigned to. (You might
> be confusing this with the lack of struct constants in C89. 

It was thoughtlessness rather than confusion, but see below.

> This lack
> means that, if you want to assign something to a struct, in must be
> another struct; there's no such thing as i=4 for structs, only i=j. 

You seem to imply that something similar to i=4 where i is a struct is
possible in C99, which I haven't come across before.

> Or
> perhaps by the possibility of structs being incomplete (see below); but
> this isn't true for structs in general.)
> Other non-modifiable lvalues are incomplete types (unfinished
> variable-length arrays, declared but not yet defined structs, etc.),

I'm with you on all of those bar the last.  Surely by the time you attempt
to access a struct variable as an lvalue, the struct must have been
defined?

<snip>

0
Reply netocrat (497) 7/15/2005 6:15:49 AM

The problem is that array itself is a pointer to it's beginning so then
writing &aInt3 would give you pointer to pointer to an array. Solution
is to pass only aInt3 to your function.

Another thing is that const modifier. It tells you that this function
is not going to change received pointer i.e. set it to another array.
And after a function call it will point to the same thing it was
pointing befeore the call.

0
Reply PiwkoLukasz (2) 7/15/2005 12:09:41 PM

[Followups set to comp.lang.c]

Lukaszp wrote:

> The problem is that array itself is a pointer to it's beginning so then

Not quite. The array itself is an array. The /value/ of the array, when that 
array's name is used in a value context, is the address of the first 
element.

> writing &aInt3 would give you pointer to pointer to an array. Solution
> is to pass only aInt3 to your function.

The declaration in question was:

  int aInt3[5] = {0,1,2,4,9};

When you write &aInt3, you're actually getting the address of the array (the 
array is not being used in a value context here); the type is int (*)[5], 
i.e. a pointer to an array, not a pointer to a pointer to an array as you 
incorrectly claimed. (This is a tricky area - it's intensely logical, but 
C's logic doesn't always map perfectly to programmers' first-cut instinct!)

-- 
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
mail: rjh at above domain
0
Reply invalid29 (585) 7/15/2005 12:19:11 PM

Ok. Thanks. did i have right with the const modifier?

0
Reply PiwkoLukasz (2) 7/15/2005 12:36:04 PM

Richard Heathfield wrote:

> The /value/ of the array, when that
> array's name is used in a value context, is the address of the first
> element.

I see you have the word /value/ highlighted.

Operations which result in pointers to the operand, 
don't operate on the value of the operand.

The value of &i doesn't depend on the value of i.

The value of (array + 0) doesn't depend on the contents of the array,
though I'll admit I can't recall any consensus as to what,
if anything, "value" means when applied to an array.

-- 
pete
0
Reply pfiland (6613) 7/15/2005 12:36:56 PM

Lukaszp wrote:

> Ok. Thanks. did i have right with the const modifier?

Funny you should mention that. No, you didn't have it right. :-(

Sorry, but that's life. const int (*p)[5] means "pointer to array of 5 const 
ints", not "const pointer to array of 5 ints".

Apologies for not noticing that in my first reply.

-- 
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
mail: rjh at above domain
0
Reply invalid29 (585) 7/15/2005 12:51:40 PM

pete wrote:

> Richard Heathfield wrote:
> 
>> The /value/ of the array, when that
>> array's name is used in a value context, is the address of the first
>> element.
> 
> I see you have the word /value/ highlighted.

Correct.

> 
> Operations which result in pointers to the operand,
> don't operate on the value of the operand.

Correct as far as I can see, without spilling ISO ketchup all over the 
thread.

> 
> The value of &i doesn't depend on the value of i.

Correct, although of course the value of i can depend on &i in at least one 
case.

> The value of (array + 0) doesn't depend on the contents of the array,
> though I'll admit I can't recall any consensus as to what,
> if anything, "value" means when applied to an array.

It's The Rule, a la CT. In a value context, the name of an array decays to a 
pointer to the first element of that array. So, effectively, the value of 
an array is a pointer to its first element, n'est-ce-pas?

-- 
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
mail: rjh at above domain
0
Reply invalid29 (585) 7/15/2005 12:55:32 PM

Netocrat <netocrat@dodo.com.au> wrote:
> On Thu, 14 Jul 2005 13:39:16 +0000, S.Tobias wrote:
>> In comp.lang.c Netocrat <netocrat@dodo.com.au> wrote:
>>> On Thu, 14 Jul 2005 02:47:13 -0700, Andrey Tarasevich wrote:
>>>> Netocrat wrote:

>>>>> ...
>>>>> Neither of these examples removes a const attribute from an lvalue, which
>>>>> is impossible by definition.  An lvalue can be assigned to.  
>>>> 
>>>> Huh? No. By definition, lvalue is something that has address in storage. In
>>>> general case lvalue cannot be assigned to. Only _modifyable_ lvalue can be
>>>> assigned to, but there are also non-modifyable lvalues.
>>> 
>>> Right, my concept of lvalue was slightly out.  Non-modifiable being for
>>> example arrays and structs.
>>
>> I'm not sure it's that simple with storage for lvalues.  Take `register'
>> variables for an example, or pointers to objects whose lifetime has
>> finished (dereferencing such a pointer is an lvalue, whether the object
>> exists or not).
> 
> The C89 draft says:
> 
> "The unary * operator denotes indirection. If the operand points to ... an
> object, the result is an lvalue designating the object... If an invalid
> value has been assigned to the pointer, the behavior of the unary *
> operator is undefined."
> 
> Since in this case the pointer has an invalid value it would be illegal to
> dereference it; the question of whether or not the result would be an
> lvalue is moot.

It is not moot.  Being an [rl]value is a compile-time property, and
must be known before the program is run.  A compiler must accept
this:
  int *p = 0;
  /*...*/
  *p = 7;
(unless it can prove `p' will always be null ptr).  Whatever value
`p' has, `*p' is always an lvalue.  If we couldn't make this
statement then the *constraint* that the left operand of "=" 
be a (modifiable) lvalue would be undiagnosable (IOW every
implementation would be non-conforming... well... unless it
issued any diagnostic each time it were run).

The quote that you have brought in, simply defines the result of
indirection operator; it "explicitly" does not define it when ptr does
not point to an object, in which case it doesn't say if the expression
is an lvalue or not.  I think that either mentioning it's an lvalue
in the first case is redundant, or not doing so in the second case
is a flaw.  It is difficult to say, because the lvalue definition
in the Standard is seriously flawed anyway.

My "working" definition is: lvalue is an expression that potentially
locates an object.  (It might be not strictly correct in a few aspects.)
There're a few useful remarks on lvalues in the Rationale.

>> Consider also this example:
>> 
>>   struct s { int a[1]; };
>>   struct s f(void);
>> 
>>   f().a[0] = 7;  //UB
>> 
>> The expression on the left is clearly a modifiable lvalue.

This is a minor variation on an example from a previous thread in c.l.c:
  struct s {...} s1, s2;
  (s1 = s2).a[0] = 7;  //UB

> Not so clearly according to gcc.  I get:
> "ISO C90 forbids subscripting non-lvalue array".

gcc is lying.  The expression "f().a[0]" is perfectly valid.

>> But does it take any storage?  I think C++ is more verbose about
>> temporaries; they can be optimized out, which is unspecified.
>> I don't think C even has an idea of a temporary.
> 
> It doesn't occur in the context of variable storage in either draft.
> 
>> OTOH, f().a decays into a pointer to its first element, therefore
>> the array the pointer points to must be an object, therefore it 
>> must (temporarily) take some storage.  Is that a right conclusion?
> 
> If f() were a modifiable lvalue which in this context it does not appear
> to be, then I would see nothing wrong with your reasoning.  

It doesn't have to be.
  int a[1];
  a[0];
`a' is not a modifiable lvalue, `a[0]' is (cf. Rationale).

>However given
> that there's no means of later reading back the assignment, it's really a
> no-op and again the question is moot: nothing changes whether the
> assignment occurs or not.  Perhaps that's why C99 allows it.

I don't quite understand what you mean here.  I have written the assignment
to illustrate that `f().a[0]' is an lvalue (a compiler must accept it).
The UB is invoked neither by this expression, nor the assignment itself;
what invokes it is an attempt to modify the return of a function, 
cf. 6.5.2.2#5 (hence by the assignment too, but rather indirectly).

Anyway, as to the storage of temporaries in C, I'm going to start
a separate thread on the issue later today.

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/15/2005 1:57:21 PM

[F'ups to clc]

In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
> S.Tobias wrote:
>> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
>>
>> >  char *foo();
>> >
>> >  void bah(const char *x)
>> >  {
>> >    char *y = foo(x);
>>
>> You raise UB here: (const char*) and (char*) types are
>> not compatible - you cannot pass incompatible arguments
>> to a function call (cf. 6.5.2.2#6).
> 
> C99 has different wording from N869.
> 
> [It looks like it's not valid C90 though.]

I'm not sure what you mean.  Here's the quote from the C99 Standard
I was referring to:
(6.5.2.2p6)
#  If the expression that denotes the called function has a type that
#  does not include a prototype, [...]
#  [...] If the function is defined with a type that includes a
#  prototype, and either the prototype ends with an ellipsis (, ...) or
#  the types of the arguments after promotion are not compatible with
#  the types of the parameters, the behavior is undefined. [...]

> 
>> >    ...
>> >  }
>> > 
>> >  char *foo(char *x)
>> >  {
>> >    return x;
>> >  }
> 

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/15/2005 2:10:00 PM

[F'ups to clc]

In comp.lang.c Netocrat <netocrat@dodo.com.au> wrote:
> On Thu, 14 Jul 2005 15:03:21 +0000, Richard Bos wrote:

[snip]
>> Other non-modifiable lvalues are incomplete types (unfinished
>> variable-length arrays, declared but not yet defined structs, etc.),
> 
> I'm with you on all of those bar the last.  Surely by the time you attempt
> to access a struct variable as an lvalue, the struct must have been
> defined?

I don't know if you're aware of this, but an expression with 
an incomplete structure type can also be an lvalue.  (There're might 
be some problems with obtaining such expressions though, which
makes it a bit of an academic issue.)
  struct s;
  struct s *ps;
  & * ps;
The sub-expression `*ps' has an incomplete struct type, and is an lvalue.

Other example of incomplete-type lvalue is an array:
  int a[7];
  int (*pa)[] = &a;
  *pa;
`*pa' is an lvalue with the incomplete type `int[]'.

The idea of lvalue has little to do with accessing an object it
designates; size is not included in the notion.  An lvalue is
an object locator.  Read C99 Rationale for more information.

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/15/2005 2:32:42 PM

On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote:

> [F'ups to clc]
> 
> In comp.lang.c Netocrat <netocrat@dodo.com.au> wrote:
>> On Thu, 14 Jul 2005 15:03:21 +0000, Richard Bos wrote:
> 
> [snip]
>>> Other non-modifiable lvalues are incomplete types (unfinished
>>> variable-length arrays, declared but not yet defined structs, etc.),
>> 
>> I'm with you on all of those bar the last.  Surely by the time you attempt
>> to access a struct variable as an lvalue, the struct must have been
>> defined?
> 
> I don't know if you're aware of this, but an expression with 
> an incomplete structure type can also be an lvalue.

I wasn't specifically aware, but neither was I was disagreeing with
that assertion (admittedly my wording is unclear in that regard), my
point was actually that I couldn't see a way in which such an lvalue
could occur.  Checking the standard, you're right that it is an acceptable
lvalue:

N869, 6.3.2.1
"An lvalue is an expression with an object type or an incomplete type
other than void; if an lvalue does not designate an object when it is
evaluated, the behavior is undefined."

> (There're might 
> be some problems with obtaining such expressions though, which
> makes it a bit of an academic issue.)

Which was exactly the point I was making - in fact I can't imagine any way
to validly access an incomplete structure as an lvalue.  So it would seem
to be an entirely academic issue.

>   struct s;
>   struct s *ps;
>   & * ps;
> The sub-expression `*ps' has an incomplete struct type, and is an lvalue.

Here you dereference a pointer to an incomplete struct which I can't see
ever having legal meaning (I can't find anything in the standard that
strictly forbids it, but intuitively it's wrong to me).  Gcc agrees with
me in both C90 and C99 modes:

error: dereferencing pointer to incomplete type

Not that you seem to have much faith in gcc's diagnostics.

> Other example of incomplete-type lvalue is an array:
>   int a[7];
>   int (*pa)[] = &a;
>   *pa;
> `*pa' is an lvalue with the incomplete type `int[]'.

This seems fine because int[] when accessed as an lvalue decays to int*,
which is a complete type.  Whereas in the case of *ps from the previous
example, there's no sense in which "struct s" can be considered to be
complete or to decay into a complete type.
 
> The idea of lvalue has little to do with accessing an object it
> designates; size is not included in the notion.  An lvalue is an object
> locator.  Read C99 Rationale for more information.

I'm glad you directed me there because I found this quote:

"A difference of opinion within the C community centered around the
meaning of lvalue, one group considering an lvalue to be any kind of
object locator, another group holding that an lvalue is meaningful on the
left side of an assigning operator.  The C89 Committee adopted the
definition of lvalue as an object locator.  The term modifiable lvalue is
used for the second of the above concepts."

In fact my original understanding of lvalue coincided with the second
definition, which is why I made the assertion that "An lvalue can be
assigned to" that started this sub-thread.  I accept that the standard
chose a slightly different definition but I disagree with the decision.
The word "lvalue" implies "left value" implying an expression that is
legal on the left side of an assignment statement implying that it must be
modifiable.  So I think a different word should have been chosen - perhaps
"object locator" - and lvalue retained its meaning as assignable object.

0
Reply netocrat (497) 7/15/2005 8:44:41 PM

Netocrat wrote:
 
> N869, 6.3.2.1
> "An lvalue is an expression with an object type or an incomplete type
> other than void; if an lvalue does not designate an object when it is
> evaluated, the behavior is undefined."
> 
> > (There're might
> > be some problems with obtaining such expressions though, which
> > makes it a bit of an academic issue.)
> 
> Which was exactly the point I was making
> - in fact I can't imagine any way
> to validly access an incomplete structure as an lvalue.
> So it would seem
> to be an entirely academic issue.
> 
> >   struct s;
> >   struct s *ps;
> >   & * ps;
> > The sub-expression `*ps' has an incomplete struct type,
> > and is an lvalue.
> 
> Here you dereference a pointer to an incomplete struct
> which I can't see
> ever having legal meaning (I can't find anything in the standard that
> strictly forbids it, but intuitively it's wrong to me). 
> Gcc agrees with
> me in both C90 and C99 modes:
 
> error: dereferencing pointer to incomplete type

The code he wrote was 
    &*ps;
The & and the *, cancel each other out.

> Not that you seem to have much faith in gcc's diagnostics.

I don't know what code you compiled to achieve that error.

new.c compiles without errors.

/* BEGIN new.c */

#include <stdio.h>

int main(void)
{
    struct s;
    struct s *ps;
    extern struct s structure;
    extern int array[];

    ps = &structure;
    ps = &*ps;
    printf(  "ps is %p\n", (void *)   ps);
    printf("&*ps is %p\n", (void *) &*ps);
    printf("&structure is %p\n", (void *) &structure);
    printf("&array is %p\n", (void *) &array);
    return 0;
}

/* END new.c */

-- 
pete
0
Reply pfiland (6613) 7/15/2005 9:17:49 PM

On Fri, 15 Jul 2005 13:57:21 +0000, S.Tobias wrote:

> Netocrat <netocrat@dodo.com.au> wrote:
>> On Thu, 14 Jul 2005 13:39:16 +0000, S.Tobias wrote:
>>> In comp.lang.c Netocrat <netocrat@dodo.com.au> wrote:
>>>> On Thu, 14 Jul 2005 02:47:13 -0700, Andrey Tarasevich wrote:
>>>>> Netocrat wrote:
> 
>>>>>> ...
>>>>>> Neither of these examples removes a const attribute from an lvalue,
>>>>>> which is impossible by definition.  An lvalue can be assigned to.
>>>>> 
>>>>> Huh? No. By definition, lvalue is something that has address in
>>>>> storage. In general case lvalue cannot be assigned to. Only
>>>>> _modifyable_ lvalue can be assigned to, but there are also
>>>>> non-modifyable lvalues.
>>>> 
>>>> Right, my concept of lvalue was slightly out.  Non-modifiable being
>>>> for example arrays and structs.
>>>
>>> I'm not sure it's that simple with storage for lvalues.  Take
>>> `register' variables for an example, or pointers to objects whose
>>> lifetime has finished (dereferencing such a pointer is an lvalue,
>>> whether the object exists or not).
>> 
>> The C89 draft says:
>> 
>> "The unary * operator denotes indirection. If the operand points to ...
>> an object, the result is an lvalue designating the object... If an
>> invalid value has been assigned to the pointer, the behavior of the
>> unary * operator is undefined."
>> 
>> Since in this case the pointer has an invalid value it would be illegal
>> to dereference it; the question of whether or not the result would be
>> an lvalue is moot.
> 
> It is not moot.

Perhaps that was an inflammatory thing to say.  We both agree that the
dereference is illegal.  Let's pretend I hadn't called the question moot
since it is actually the whole point of our discussion.

> Being an [rl]value is a compile-time property, and must be known before
> the program is run.

Agreed after reading the standard and rationale more closely.

<snip code and explanation>

> The quote that you have brought in, simply defines the result of
> indirection operator; it "explicitly" does not define it when ptr does
> not point to an object, in which case it doesn't say if the expression
> is an lvalue or not.

Why back away from your original strong statement that "being an lvalue is
a compile-time property"?  From the quote in my other post, the standard
says: "if an lvalue does not designate an object when it is evaluated, the
behavior is undefined."  This exactly describes the case we're discussing.
It is both an lvalue and invokes undefined behaviour on being assigned to.

> I think that either mentioning it's an lvalue in the first case is
> redundant, or not doing so in the second case is a flaw.  It is
> difficult to say, because the lvalue definition in the Standard is
> seriously flawed anyway.
> 
> My "working" definition is: lvalue is an expression that potentially
> locates an object.  (It might be not strictly correct in a few aspects.)
> There're a few useful remarks on lvalues in the Rationale.

That seems to be a useful definition.
 
>>> Consider also this example:
>>> 
>>>   struct s { int a[1]; };
>>>   struct s f(void);
>>> 
>>>   f().a[0] = 7;  //UB
>>> 
>>> The expression on the left is clearly a modifiable lvalue.
> 
> This is a minor variation on an example from a previous thread in c.l.c:
>   struct s {...} s1, s2;
>   (s1 = s2).a[0] = 7;  //UB

I remember reading that thread - "Undefined behaviour when modifying the
result of an assignment operator".
 
>> Not so clearly according to gcc.  I get: "ISO C90 forbids subscripting
>> non-lvalue array".
> 
> gcc is lying.  The expression "f().a[0]" is perfectly valid.

It's perfectly valid as a non-modifiable lvalue; but we both agree that
assigning to it as a modifiable lvalue invokes undefined behaviour.
Perhaps in this case gcc is using the alternate definition of lvalue (that
which I initially asserted of "modifiable lvalue") in which case the
warning is accurate, although why it doesn't also appear in C99 mode I
don't know.

>>> But does it take any storage?  I think C++ is more verbose about
>>> temporaries; they can be optimized out, which is unspecified. I don't
>>> think C even has an idea of a temporary.
>> 
>> It doesn't occur in the context of variable storage in either draft.
>> 
>>> OTOH, f().a decays into a pointer to its first element, therefore the
>>> array the pointer points to must be an object, therefore it must
>>> (temporarily) take some storage.  Is that a right conclusion?
>> 
>> If f() were a modifiable lvalue which in this context it does not
>> appear to be, then I would see nothing wrong with your reasoning.
> 
> It doesn't have to be.
>   int a[1];
>   a[0];
> `a' is not a modifiable lvalue, `a[0]' is (cf. Rationale).

Agreed, but I was talking about f(), not f().a.  In the case of f(), it
does have to be.  If the struct returned by f() is not a modifiable
lvalue, then neither are the elements of its member array.

>>However given
>> that there's no means of later reading back the assignment, it's really
>> a no-op and again the question is moot: nothing changes whether the
>> assignment occurs or not.  Perhaps that's why C99 allows it.
> 
> I don't quite understand what you mean here.

That's not unreasonable, because I didn't take into account that 6.5.2.2#5
applies in this case and thus I totally ignored your UB comment.  I
assumed that this is legal and defined code in C99 on the basis that gcc
didn't produce a diagnostic in C99 mode, which is obviously a less than
reliable basis and wrong in this case.

So I'll reinterpret my statement: assuming that the storage temporarily
exists (which conceptually it does), and pretending that we are not
prohibited from assigning to it by 6.5.2.2#5, the assignment has no effect
anyway (it's a no-op) since we cannot at any later point retrieve the
assigned value (the temporary storage disappears immediately after the
assignment statement).

As for the question being moot, again, pretend that I didn't say that.

> I have written the assignment
> to illustrate that `f().a[0]' is an lvalue (a compiler must accept it).

No disagreement there.

> The UB is invoked neither by this expression, nor the assignment itself;
> what invokes it is an attempt to modify the return of a function, cf.
> 6.5.2.2#5 (hence by the assignment too, but rather indirectly).

Accepted.  I didn't spot the problem as I would have immediately in the
simpler case of:

int f(void);
f() = 7;  /* UB for the same reason but more obviously so */

<snip>
0
Reply netocrat (497) 7/15/2005 9:23:52 PM

On Fri, 15 Jul 2005 21:17:49 +0000, pete wrote:

> Netocrat wrote:
>  
>> N869, 6.3.2.1
>> "An lvalue is an expression with an object type or an incomplete type
>> other than void; if an lvalue does not designate an object when it is
>> evaluated, the behavior is undefined."
>> 
>> > (There're might
>> > be some problems with obtaining such expressions though, which makes
>> > it a bit of an academic issue.)
>> 
>> Which was exactly the point I was making - in fact I can't imagine any
>> way
>> to validly access an incomplete structure as an lvalue. So it would
>> seem
>> to be an entirely academic issue.
>> 
>> >   struct s;
>> >   struct s *ps;
>> >   & * ps;
>> > The sub-expression `*ps' has an incomplete struct type, and is an
>> > lvalue.
>> 
>> Here you dereference a pointer to an incomplete struct which I can't
>> see
>> ever having legal meaning (I can't find anything in the standard that
>> strictly forbids it, but intuitively it's wrong to me). Gcc agrees with
>> me in both C90 and C99 modes:
>  
>> error: dereferencing pointer to incomplete type
> 
> The code he wrote was
>     &*ps;
> The & and the *, cancel each other out.

The * is applied before the &.  But dereferencing an incomplete structure
is illegal (as I said I can't find where in the standard this is
specified, but I believe it must be as intuitively it makes no sense to me).

>> Not that you seem to have much faith in gcc's diagnostics.
> 
> I don't know what code you compiled to achieve that error.
> 
> new.c compiles without errors.
> 
> /* BEGIN new.c */
> 
> #include <stdio.h>
> 
> int main(void)
> {
>     struct s;
>     struct s *ps;
>     extern struct s structure;
>     extern int array[];
> 
>     ps = &structure;
>     ps = &*ps;
>     printf(  "ps is %p\n", (void *)   ps); printf("&*ps is %p\n", (void
>     *) &*ps); printf("&structure is %p\n", (void *) &structure);
>     printf("&array is %p\n", (void *) &array); return 0;
> }
> 
> /* END new.c */

$ gcc -ansi -pedantic new.c
new.c: In function `main':
new.c:13: error: dereferencing pointer to incomplete type
new.c:15: error: dereferencing pointer to incomplete type
$ gcc -std=c99 -pedantic new.c
new.c: In function `main':
new.c:13: error: dereferencing pointer to incomplete type
new.c:15: error: dereferencing pointer to incomplete type
$ gcc --version
gcc (GCC) 3.3.5-20050130 (Gentoo 3.3.5.20050130-r1, ssp-3.3.5.20050130-1, 
pie-8.7.7.1)
<snip copyright>

0
Reply netocrat (497) 7/15/2005 9:33:51 PM

Netocrat wrote:

> > The code he wrote was
> >     &*ps;
> > The & and the *, cancel each other out.
> 
> The * is applied before the &. 

No.

N869
       6.5.3.2  Address and indirection operators

       Semantics
       [#3]  The  unary  &  operator  returns  the  address  of its 
       operand. 

       If the operand is the result of a
       unary * operator, neither that operator nor the  &  operator
       is  evaluated  and  the  result  is as if both were omitted,

> >> Not that you seem to have much faith in gcc's diagnostics.

After a while here on this newsgroup,
you should get to a point where you're smarter than your compiler
and your compiler is no longer an instructional aid.

> > I don't know what code you compiled to achieve that error.
> >
> > new.c compiles without errors.
> >
> > /* BEGIN new.c */
> >
> > #include <stdio.h>
> >
> > int main(void)
> > {
> >     struct s;
> >     struct s *ps;
> >     extern struct s structure;
> >     extern int array[];
> >
> >     ps = &structure;
> >     ps = &*ps;
> >     printf(  "ps is %p\n", (void *)   ps);
> >     printf("&*ps is %p\n", (void *) &*ps);
> >     printf("&structure is %p\n", (void *) &structure);
> >     printf("&array is %p\n", (void *) &array); return 0;
> > }
> >
> > /* END new.c */
> 
> $ gcc -ansi -pedantic new.c

I don't know whether or not 
that's how you compile a c file with gcc.

-- 
pete
0
Reply pfiland (6613) 7/15/2005 10:16:01 PM

pete wrote:
> 
> Netocrat wrote:
> 
> > > The code he wrote was
> > >     &*ps;
> > > The & and the *, cancel each other out.
> >
> > The * is applied before the &.
> 
> No.
> 
> N869

Well, I did some more checking.
They cancel in C99, but I can't find the same words in C89.

> > > /* BEGIN new.c */
> > >
> > > #include <stdio.h>
> > >
> > > int main(void)
> > > {
> > >     struct s;
> > >     struct s *ps;
> > >     extern struct s structure;
> > >     extern int array[];
> > >
> > >     ps = &structure;
> > >     ps = &*ps;
> > >     printf(  "ps is %p\n", (void *)   ps);
> > >     printf("&*ps is %p\n", (void *) &*ps);
> > >     printf("&structure is %p\n", (void *) &structure);
> > >     printf("&array is %p\n", (void *) &array); return 0;
> > > }
> > >
> > > /* END new.c */
> >
> > $ gcc -ansi -pedantic new.c
> 
> I don't know whether or not
> that's how you compile a c file with gcc.

When I link new.c with other.c, it forms a complete program.

/* BEGIN other.c */

struct s {
    int a;
} structure;
int array[1];

/* END other.c */

Are you unable to form a complete program
by compiling new.c and other.c and linking them?

ps is 00413FC8
&*ps is 00413FC8
&structure is 00413FC8
&array is 00413FD8

-- 
pete
0
Reply pfiland (6613) 7/15/2005 10:28:16 PM

On Fri, 15 Jul 2005 22:16:01 +0000, pete wrote:

> Netocrat wrote:
> 
>> > The code he wrote was
>> >     &*ps;
>> > The & and the *, cancel each other out.
>> 
>> The * is applied before the &. 
> 
> No.
> 
> N869
>        6.5.3.2  Address and indirection operators
> 
>        Semantics
>        [#3]  The  unary  &  operator  returns  the  address  of its 
>        operand. 
> 
>        If the operand is the result of a
>        unary * operator, neither that operator nor the  &  operator
>        is  evaluated  and  the  result  is as if both were omitted,

Under C99 you are correct.  gcc (and I) are wrong about the semantics in
that case.  But gcc isn't claimed to be 100% C99 compliant anyhow.

Under C90 those semantics aren't mandated and Derek M. Robert's excellent
pdf standards commentary explains on page 1025 that "[t]he responses to DR
#012, DR #076, and DR #106 specified that [code such as int *n = NULL; int
*p = &*n;] were constraint violations", and that in practice some C90
implementations do not optimise &* into a no-op.

In either case it's still true that an incomplete struct type is never
treated as an lvalue, which is my original point.  If &* is optimised into
a no-op then *ps never occurs; and if under C90 it is not then *ps is (as
I read things) a constraint violation which gcc correctly faults on.

<snip>

>> $ gcc -ansi -pedantic new.c
> 
> I don't know whether or not 
> that's how you compile a c file with gcc.

If you want it to do so in ANSI C89-compatible mode and avoid most (all?)
strictly unnecessary diagnostics it is.

0
Reply netocrat (497) 7/15/2005 11:15:53 PM

On Fri, 15 Jul 2005 22:28:16 +0000, pete wrote:

> pete wrote:

<snip comments on C89 addressed in my other post>

>> > > /* BEGIN new.c */
>> > >
>> > > #include <stdio.h>
>> > >
>> > > int main(void)
>> > > {
>> > >     struct s;
>> > >     struct s *ps;
>> > >     extern struct s structure;
>> > >     extern int array[];
>> > >
>> > >     ps = &structure;
>> > >     ps = &*ps;
>> > >     printf(  "ps is %p\n", (void *)   ps);
>> > >     printf("&*ps is %p\n", (void *) &*ps);
>> > >     printf("&structure is %p\n", (void *) &structure);
>> > >     printf("&array is %p\n", (void *) &array); return 0;
>> > > }
>> > >
>> > > /* END new.c */
>> >
>> > $ gcc -ansi -pedantic new.c
>> 
>> I don't know whether or not
>> that's how you compile a c file with gcc.
> 
> When I link new.c with other.c, it forms a complete program.
> 
> /* BEGIN other.c */
> 
> struct s {
>     int a;
> } structure;
> int array[1];
> 
> /* END other.c */
> 
> Are you unable to form a complete program
> by compiling new.c and other.c and linking them?
> 
> ps is 00413FC8
> &*ps is 00413FC8
> &structure is 00413FC8
> &array is 00413FD8

Yes, I am unable.  other.c compiles to object.o OK.  But gcc won't
compile new.c, regardless of whether I instruct it to:
a) compile it alone
b) compile it simultaneously with other.c
c) compile it and link with other.o.

I get the same 2 diagnostics in both standards modes: "error:
dereferencing pointer to incomplete type".

Replacing the forward declaration of struct s in new.c with the actual
declaration as in other.c works though (as one would expect).  It compiles
without warning in both modes and gives results equivalent to yours:

ps is 0xbffff1a4
&*ps is 0xbffff1a4
&structure is 0xbffff1a4
&array is 0xbffff1a0

0
Reply netocrat (497) 7/15/2005 11:37:15 PM

Netocrat wrote:
> 
> On Fri, 15 Jul 2005 22:28:16 +0000, pete wrote:
> 
> > pete wrote:
> 
> <snip comments on C89 addressed in my other post>
> 
> >> > > /* BEGIN new.c */
> >> > >
> >> > > #include <stdio.h>
> >> > >
> >> > > int main(void)
> >> > > {
> >> > >     struct s;
> >> > >     struct s *ps;
> >> > >     extern struct s structure;
> >> > >     extern int array[];
> >> > >
> >> > >     ps = &structure;
> >> > >     ps = &*ps;
> >> > >     printf(  "ps is %p\n", (void *)   ps);
> >> > >     printf("&*ps is %p\n", (void *) &*ps);
> >> > >     printf("&structure is %p\n", (void *) &structure);
> >> > >     printf("&array is %p\n", (void *) &array); return 0;
> >> > > }
> >> > >
> >> > > /* END new.c */
> >> >
> >> > $ gcc -ansi -pedantic new.c
> >>
> >> I don't know whether or not
> >> that's how you compile a c file with gcc.
> >
> > When I link new.c with other.c, it forms a complete program.
> >
> > /* BEGIN other.c */
> >
> > struct s {
> >     int a;
> > } structure;
> > int array[1];
> >
> > /* END other.c */
> >
> > Are you unable to form a complete program
> > by compiling new.c and other.c and linking them?
> >
> > ps is 00413FC8
> > &*ps is 00413FC8
> > &structure is 00413FC8
> > &array is 00413FD8
> 
> Yes, I am unable.  other.c compiles to object.o OK.  But gcc won't
> compile new.c, regardless of whether I instruct it to:
> a) compile it alone
> b) compile it simultaneously with other.c
> c) compile it and link with other.o.
> 
> I get the same 2 diagnostics in both standards modes: "error:
> dereferencing pointer to incomplete type".
> 
> Replacing the forward declaration of struct s in new.c with the actual
> declaration as in other.c works though (as one would expect).  It compiles
> without warning in both modes and gives results equivalent to yours:
> 
> ps is 0xbffff1a4
> &*ps is 0xbffff1a4
> &structure is 0xbffff1a4
> &array is 0xbffff1a0

OK
Thank you.

-- 
pete
0
Reply pfiland (6613) 7/15/2005 11:57:08 PM

Netocrat <netocrat@dodo.com.au> wrote:
> On Fri, 15 Jul 2005 13:57:21 +0000, S.Tobias wrote:
> 
>> Netocrat <netocrat@dodo.com.au> wrote:
>>> On Thu, 14 Jul 2005 13:39:16 +0000, S.Tobias wrote:

>>> The C89 draft says:
>>> 
>>> "The unary * operator denotes indirection. If the operand points to ...
>>> an object, the result is an lvalue designating the object... If an
>>> invalid value has been assigned to the pointer, the behavior of the
>>> unary * operator is undefined."
>>> 
>>> Since in this case the pointer has an invalid value it would be illegal
>>> to dereference it; the question of whether or not the result would be
>>> an lvalue is moot.
>> 
>> It is not moot.
> 
> Perhaps that was an inflammatory thing to say.  
[snip]

No, it wasn't, really.  Indeed lvalue-ness of expressions could depend
on their evaluation (ie. only expressions with valid behaviour are
lvalues).  But then being an lvalue would not be diagnosable at compile-
time.  Fine.  Expressions like `7 = 11' would yield only UB during
run time.  Good.

It is not so (fortunately), lvalue property is referred to in Constrains
in many places, therefore it must be known before evaluation of
an expression.  The Standard ought to specify it for every expression, 
irrespective of its validity.

Dereferencing an object pointer (other than void*) is always an
lvalue (if it isn't then it ought to be).  If it appropriate type
(non-const, non-array, etc.), it's a modifiable lvalue.  (It is
conceivable to imagine a scenario where it wasn't an lvalue.)
Some expressions are non-lvalues, eg. cast expressions; gcc used
to have an extension where some cast expressions were lvalues.

The expression `*(int*)0' is a modifiable lvalue.  It can be diagnosed,
and a compiler could refuse to compile it if it could prove it would
be evaluated.  I believe it couldn't reject this code:
  if (0)  *(int*)0 = 7;


>> The quote that you have brought in, simply defines the result of
>> indirection operator; it "explicitly" does not define it when ptr does
>> not point to an object, in which case it doesn't say if the expression
>> is an lvalue or not.
> 
> Why back away from your original strong statement that "being an lvalue is
> a compile-time property"?  

I was just acknowledging the fact that the Standard didn't define it.
I think it ought to.

>From the quote in my other post, the standard
> says: "if an lvalue does not designate an object when it is evaluated, the
> behavior is undefined."  This exactly describes the case we're discussing.
> It is both an lvalue and invokes undefined behaviour on being assigned to.

It's true, but `*(int*)0' should also be an lvalue (apart from invoking UB); 
the Standard (probably) does not say so (at least not explicitly).

>>>>   struct s { int a[1]; };
>>>>   struct s f(void);
>>>> 
>>>>   f().a[0] = 7;  //UB
>>>> 
>>>> The expression on the left is clearly a modifiable lvalue.
>> 
>> This is a minor variation on an example from a previous thread in c.l.c:
>>   struct s {...} s1, s2;
>>   (s1 = s2).a[0] = 7;  //UB
> 
> I remember reading that thread - "Undefined behaviour when modifying the
> result of an assignment operator".
> 
>>> Not so clearly according to gcc.  I get: "ISO C90 forbids subscripting
>>> non-lvalue array".
>> 
>> gcc is lying.  The expression "f().a[0]" is perfectly valid.
> 
> It's perfectly valid as a non-modifiable lvalue; but we both agree that
> assigning to it as a modifiable lvalue invokes undefined behaviour.

Try it with on-line como - there's not a single warnings.
Cut'n'paste version:
  int main()
  {
  struct s { int a[1]; };
  struct s f(void);
  f().a[0] = 7;  /*UB*/
  }

[snip]
>>   int a[1];
>>   a[0];
>> `a' is not a modifiable lvalue, `a[0]' is (cf. Rationale).
> 
> Agreed, but I was talking about f(), not f().a.  In the case of f(), it
> does have to be.  If the struct returned by f() is not a modifiable
> lvalue, then neither are the elements of its member array.

It doesn't matter, really.  `a' is not an lvalue, but `a[0]' is.
Similarly, `f().a' is not an lvalue;  `f().a[0]' is the same as
`*(f().a + 0)', dereferencing a pointer is always an lvalue (in
this case it is even well defined by the Std, because `f().a' 
presumably points to some valid temporary storage).  Since its type
is `int', it's a modifiable lvalue.


> So I'll reinterpret my statement: assuming that the storage temporarily
> exists (which conceptually it does), and pretending that we are not
> prohibited from assigning to it by 6.5.2.2#5, the assignment has no effect
> anyway (it's a no-op) since we cannot at any later point retrieve the
> assigned value (the temporary storage disappears immediately after the
> assignment statement).

Imagine this:

  struct s { int a[1]; };

  struct s sfunc(void) 
    { 
    static const struct s s;  //const is optional, but more illustrative here
    return s;
    }

  int main()
    {
    sfunc().a[0] = 7; //UB
    }

The compiler might make an optimization an reuse the storage of `s' for
the return value (ie. not create a temporary storage).  I'm sure you
can see what a disaster it would make.  

Actually, if "UB" line wasn't an UB, the compiler could not make such
an optimization.  I think it's for this reason (so that it can) that
"UB" is UB.  It's the same story with the result of "=" operator.


[...]
> int f(void);
> f() = 7;  /* UB for the same reason but more obviously so */

Erm... strictly speaking, no, because it won't compile.  And if it
would then behaviour of "=" would raise UB, since LHS is not an lvalue.

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/16/2005 12:32:38 AM

pete <pfiland@mindspring.com> wrote:

> new.c compiles without errors.

Which compiler/version did you compile it with?

> /* BEGIN new.c */
> 
> #include <stdio.h>
> 
> int main(void)
> {
>    struct s;
>    struct s *ps;
>    extern struct s structure;
>    extern int array[];
> 
>    ps = &structure;
>    ps = &*ps;
>    printf(  "ps is %p\n", (void *)   ps);
>    printf("&*ps is %p\n", (void *) &*ps);
>    printf("&structure is %p\n", (void *) &structure);
>    printf("&array is %p\n", (void *) &array);
>    return 0;
> }
> 
> /* END new.c */

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/16/2005 12:34:20 AM

Netocrat <netocrat@dodo.com.au> wrote:
> On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote:

> N869, 6.3.2.1
> "An lvalue is an expression with an object type or an incomplete type
> other than void; if an lvalue does not designate an object when it is
> evaluated, the behavior is undefined."

The `*ps' is not evaluated in this case.

However `ps' is indeterminate, and using it raises UB.  It should
be initialized.

>>   struct s;
>>   struct s *ps;
>>   & * ps;
>> The sub-expression `*ps' has an incomplete struct type, and is an lvalue.
> 
> Here you dereference a pointer to an incomplete struct which I can't see
> ever having legal meaning (I can't find anything in the standard that
> strictly forbids it, but intuitively it's wrong to me).  

Well, C is not for mentally healthy people.  ;-)

>Gcc agrees with
> me in both C90 and C99 modes:
> 
> error: dereferencing pointer to incomplete type

I think gcc is wrong.  On-line como compiles it fine (with
an unrelated warning).
Cut'n'paste version:
  int main()
  {
  struct s *p = 0;
  & * p;
  }


>> Other example of incomplete-type lvalue is an array:
>>   int a[7];
>>   int (*pa)[] = &a;
>>   *pa;
>> `*pa' is an lvalue with the incomplete type `int[]'.
> 
> This seems fine because int[] when accessed as an lvalue decays to int*,
> which is a complete type.  

Yes, however it is an lvalue before this conversion.  After it decays
into int*, it's no longer an lvalue.

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/16/2005 1:05:01 AM

S.Tobias wrote:
> 
> pete <pfiland@mindspring.com> wrote:
> 
> > new.c compiles without errors.
> 
> Which compiler/version did you compile it with?

MSVC++ 5.0

> > /* BEGIN new.c */
> >
> > #include <stdio.h>
> >
> > int main(void)
> > {
> >    struct s;
> >    struct s *ps;
> >    extern struct s structure;
> >    extern int array[];
> >
> >    ps = &structure;
> >    ps = &*ps;
> >    printf(  "ps is %p\n", (void *)   ps);
> >    printf("&*ps is %p\n", (void *) &*ps);
> >    printf("&structure is %p\n", (void *) &structure);
> >    printf("&array is %p\n", (void *) &array);
> >    return 0;
> > }
> >
> > /* END new.c */

-- 
pete
0
Reply pfiland (6613) 7/16/2005 1:26:53 AM

On Sat, 16 Jul 2005 01:05:01 +0000, S.Tobias wrote:

> Netocrat <netocrat@dodo.com.au> wrote:
>> On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote:
> 
>> N869, 6.3.2.1
>> "An lvalue is an expression with an object type or an incomplete type
>> other than void; if an lvalue does not designate an object when it is
>> evaluated, the behavior is undefined."
> 
> The `*ps' is not evaluated in this case.

As I've noted elsewhere, you are correct for C99 but in C90 *ps is
evaluated - at least conceptually i.e. this is a constraint violation:

int *n = NULL.
int *p;
p = &*n;

> However `ps' is indeterminate, and using it raises UB.  It should
> be initialized.

Agreed.
 
>>>   struct s;
>>>   struct s *ps;
>>>   & * ps;
>>> The sub-expression `*ps' has an incomplete struct type, and is an lvalue.
>> 
>> Here you dereference a pointer to an incomplete struct which I can't see
>> ever having legal meaning (I can't find anything in the standard that
>> strictly forbids it, but intuitively it's wrong to me).  
> 
> Well, C is not for mentally healthy people.  ;-)

So I'm either headed for the asylum or incompetent as a C programmer. 
You've given me some quite attractive options. :)

But seriously, clearly *ps on its own is nonsensical; surely you agree
with me on that?  What does your compiler say to the simple statement
*ps; (gcc tells me where to get off)?  That I haven't yet found a specific
prohibition in the standard merely convinces me that I don't know where to
look.

I'm not satisfied that your code is an example of an incomplete struct
occurring as an lvalue.  In the case where &* is being treated as a no-op,
then semantically *ps (and hence the incomplete struct) never occurs.  In
all other cases (C90 implementations that do not perform this no-op),
*ps is meaningless and is properly rejected by the compiler as it is by
gcc.

>>Gcc agrees with
>> me in both C90 and C99 modes:
>> 
>> error: dereferencing pointer to incomplete type
> 
> I think gcc is wrong.  On-line como compiles it fine (with an unrelated
> warning).
> Cut'n'paste version:
>   int main()
>   {
>   struct s *p = 0;
>   & * p;
>   }

Yes perhaps, but on-line como in C90 mode probably treats &* as a
no-op anyhow, without diagnosing the illegality we get when *ps
conceptually occurs (as C90 requires it to). I quote "The New C Standard"
pdf: "...no C90 implementations known to your author diagnosed occurrences
of these constructs [the code I gave above ie p = &*n where n is NULL]".

Of course in C99 mode it is correct.

>>> Other example of incomplete-type lvalue is an array:
>>>   int a[7];
>>>   int (*pa)[] = &a;
>>>   *pa;
>>> `*pa' is an lvalue with the incomplete type `int[]'.
>> 
>> This seems fine because int[] when accessed as an lvalue decays to
>> int*, which is a complete type.
> 
> Yes, however it is an lvalue before this conversion.

Yes, but the fundamental difference between this case and incomplete
struct is that type int[] has intrinsic meaning (an array of unknown
length but known element size and known starting point which can therefore
be indexed).  An incomplete struct has no similar meaning.

> After it decays into int*, it's no longer an lvalue.

What would stop it from being an lvalue?  It fits the definition of
"[a]n expression with an object type ... other than void".

0
Reply netocrat (497) 7/16/2005 3:53:37 AM

On Sat, 16 Jul 2005 00:32:38 +0000, S.Tobias wrote:

> Netocrat <netocrat@dodo.com.au> wrote:
>> On Fri, 15 Jul 2005 13:57:21 +0000, S.Tobias wrote:
>> 
>>> Netocrat <netocrat@dodo.com.au> wrote:
>>>> On Thu, 14 Jul 2005 13:39:16 +0000, S.Tobias wrote:
> 
>>>> The C89 draft says:
>>>> 
>>>> "The unary * operator denotes indirection. If the operand points to
>>>> ... an object, the result is an lvalue designating the object... If
>>>> an invalid value has been assigned to the pointer, the behavior of
>>>> the unary * operator is undefined."
>>>> 
>>>> Since in this case the pointer has an invalid value it would be
>>>> illegal to dereference it; the question of whether or not the result
>>>> would be an lvalue is moot.
>>> 
>>> It is not moot.
>> 
>> Perhaps that was an inflammatory thing to say.
> [snip]
> 
> No, it wasn't, really.  Indeed lvalue-ness of expressions could depend
> on their evaluation ...

<snip>

> It is not so (fortunately), lvalue property is referred to in Constrains
> in many places, therefore it must be known before evaluation of an
> expression.  The Standard ought to specify it for every expression,
> irrespective of its validity.
>
> Dereferencing an object pointer (other than void*) is always an lvalue
> (if it isn't then it ought to be).  If it appropriate type (non-const,
> non-array, etc.), it's a modifiable lvalue.  (It is conceivable to
> imagine a scenario where it wasn't an lvalue.) Some expressions are
> non-lvalues, eg. cast expressions; gcc used to have an extension where
> some cast expressions were lvalues.

<snip>
 
>>> The quote that you have brought in, simply defines the result of
>>> indirection operator; it "explicitly" does not define it when ptr does
>>> not point to an object, in which case it doesn't say if the expression
>>> is an lvalue or not.
>> 
>> Why back away from your original strong statement that "being an lvalue
>> is a compile-time property"?
> 
> I was just acknowledging the fact that the Standard didn't define it. I
> think it ought to.
> 
>>From the quote in my other post, the standard
>> says: "if an lvalue does not designate an object when it is evaluated,
>> the behavior is undefined."  This exactly describes the case we're
>> discussing. It is both an lvalue and invokes undefined behaviour on
>> being assigned to.
> 
> It's true, but `*(int*)0' should also be an lvalue (apart from invoking
> UB); the Standard (probably) does not say so (at least not explicitly).

OK I agree that it should be explicit.  How about this for an alternate
definition of an lvalue: "Any expression which after removing any possible
register storage-specifier, we can take the address of."  Then the
question of whether dereferencing that address yields a valid object is a
separate issue.  This works for the example you gave of a dereferenced
pointer to an expired object.  We can take it's address so it is clearly
an lvalue, but dereferencing that address is a separate test which yields
undefined behaviour.  It also works for incomplete structs.  We can always
take the address of the incomplete struct, so it is clearly an lvalue, but
(I contend that) dereferencing that address is never valid.
 
>>>>>   struct s { int a[1]; };
>>>>>   struct s f(void);
>>>>> 
>>>>>   f().a[0] = 7;  //UB
>>>>> 
>>>>> The expression on the left is clearly a modifiable lvalue.
>>> 
>>> This is a minor variation on an example from a previous thread in
>>> c.l.c:
>>>   struct s {...} s1, s2;
>>>   (s1 = s2).a[0] = 7;  //UB
>> 
>> I remember reading that thread - "Undefined behaviour when modifying
>> the result of an assignment operator".
>> 
>>>> Not so clearly according to gcc.  I get: "ISO C90 forbids
>>>> subscripting non-lvalue array".
>>> 
>>> gcc is lying.  The expression "f().a[0]" is perfectly valid.
>> 
>> It's perfectly valid as a non-modifiable lvalue; but we both agree that
>> assigning to it as a modifiable lvalue invokes undefined behaviour.
> 
> Try it with on-line como - there's not a single warnings. Cut'n'paste
> version:
>   int main()
>   {
>   struct s { int a[1]; };
>   struct s f(void);
>   f().a[0] = 7;  /*UB*/
>   }

Similarly as for gcc, we don't get warnings in C99 mode, but in C90
mode on-line como gives:

"ComeauTest.c", line 5: error: invalid use of non-lvalue array
    f().a[0] = 7;  /*UB*/
    ^

>[snip]
>>>   int a[1];
>>>   a[0];
>>> `a' is not a modifiable lvalue, `a[0]' is (cf. Rationale).
>> 
>> Agreed, but I was talking about f(), not f().a.  In the case of f(), it
>> does have to be.  If the struct returned by f() is not a modifiable
>> lvalue, then neither are the elements of its member array.
> 
> It doesn't matter, really.

It does though.  You ignored my reasoning.  f() returns a struct.  If
that struct in this context is not a modifiable lvalue, then its elements
aren't modifiable lvalues either.  This applies to the elements of its
array member.

> `a' is not an lvalue, but `a[0]' is.

ITYM "modifiable lvalue".  But yes, in the generic case where a is a
non-const array, I agree.

> Similarly, `f().a' is not an lvalue;

Again, ITYM that it is an lvalue but not a modifiable lvalue.

> `f().a[0]' is the same as
> `*(f().a + 0)', dereferencing a pointer is always an lvalue (in this
> case it is even well defined by the Std, because `f().a' presumably
> points to some valid temporary storage).  Since its type is `int', it's
> a modifiable lvalue.

Your reasoning is sound but predicated on the basis that f() itself is a
modifiable lvalue.  It falls apart when that is not the case... ie
consider const struct s cs.  Now cs.a[0] is not a modifiable lvalue
because cs is not.  The same reasoning applies to f().

<snip>

>> int f(void);
>> f() = 7;  /* UB for the same reason but more obviously so */
> 
> Erm... strictly speaking, no, because it won't compile.

I don't have a complete handle on all the 'wrong behaviour' terminology
and consequences so strictly speaking you may be right.  But in the case
of C90 (at least as interpreted by gcc and online como) neither will your
original example using f().a[0] = 7 compile.

> And if it
> would then behaviour of "=" would raise UB, since LHS is not an lvalue.

Ummm, yes, that was my point.

0
Reply netocrat (497) 7/16/2005 5:22:12 AM

S.Tobias wrote:
> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
> > S.Tobias wrote:
> >> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
> >>
> >> >  char *foo();
> >> >
> >> >  void bah(const char *x)
> >> >  {
> >> >    char *y = foo(x);
> >>
> >> You raise UB here: (const char*) and (char*) types are
> >> not compatible - you cannot pass incompatible arguments
> >> to a function call (cf. 6.5.2.2#6).
> >
> > C99 has different wording from N869.
> >
> > [It looks like it's not valid C90 though.]
>
> I'm not sure what you mean.  Here's the quote from the C99 Standard
> I was referring to:
> (6.5.2.2p6)
> #  If the expression that denotes the called function has a type that
> #  does not include a prototype, [...]
> #  [...] If the function is defined with a type that includes a
> #  prototype, and either the prototype ends with an ellipsis (, ...)
> #  or the types of the arguments after promotion are not compatible
> #  with the types of the parameters, the behavior is undefined. [...]

At the time of calling, the function (foo) isn't defined with a
prototype. So the behaviour is determined by the '...' you snipped.

-- 
Peter

0
Reply airia (1802) 7/16/2005 6:15:05 AM

On Fri, 15 Jul 2005 23:15:05 -0700, Peter Nilsson wrote:

> S.Tobias wrote:
>> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
>> > S.Tobias wrote:
>> >> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
>> >>
>> >> >  char *foo();
>> >> >
>> >> >  void bah(const char *x)
>> >> >  {
>> >> >    char *y = foo(x);
>> >>
>> >> You raise UB here: (const char*) and (char*) types are
>> >> not compatible - you cannot pass incompatible arguments
>> >> to a function call (cf. 6.5.2.2#6).
>> >
>> > C99 has different wording from N869.
>> >
>> > [It looks like it's not valid C90 though.]
>>
>> I'm not sure what you mean.  Here's the quote from the C99 Standard
>> I was referring to:
>> (6.5.2.2p6)
>> #  If the expression that denotes the called function has a type that
>> #  does not include a prototype, [...]
>> #  [...] If the function is defined with a type that includes a
>> #  prototype, and either the prototype ends with an ellipsis (, ...)
>> #  or the types of the arguments after promotion are not compatible
>> #  with the types of the parameters, the behavior is undefined. [...]
> 
> At the time of calling, the function (foo) isn't defined with a
> prototype. So the behaviour is determined by the '...' you snipped.

I mischaracterised your code elsewhere - I see that as advertised it
does remove the const attribute from an lvalue without an explicit cast.

It must compile, but it also raises undefined behaviour.  Stan didn't
quote properly to support his valid argument.  A little further on...

N869, 6.5.2.2#6
       If the  function  is
       defined  with  a type that does not include a prototype, and
       the  types  of  the  arguments  after  promotion   are   not
       compatible with those of the parameters after promotion, the
       behavior is undefined, except for [cases that don't apply].

According to compatibility rules, const char * is not compatible with char *:

N869, 6.7.3#9
       For  two  qualified types to be compatible, both shall
       have the identically qualified version of a compatible type...

So the behaviour is undefined.  The same wording for both paragraphs
occurs in the C89 draft, although it doesn't list the - non-applicable to
this case - exclusions.

In any event, if the original type of the object pointed to were
const-qualified, then modifying that object would be illegal (I know
that's not the point you were making but it's important):

N869, 6.7.3#5
       If an attempt is made to modify an object defined  with
       a  const-qualified  type  through use of an lvalue with non-
       const-qualified type, the  behavior  is  undefined.

0
Reply netocrat (497) 7/16/2005 8:16:17 AM

Netocrat wrote:
 
> > After it decays into int*, it's no longer an lvalue.
> 
> What would stop it from being an lvalue?  It fits the definition of
> "[a]n expression with an object type ... other than void".

"A cast does not yield an lvalue"

The result of a type conversion, can't be an lvalue.

char object;

(int)object = INT_MAX; /* no good */

Only one byte of memory is reserved for object.
There isn't really an int type object there.

-- 
pete
0
Reply pfiland (6613) 7/16/2005 9:53:51 AM

Netocrat wrote:
 
> OK I agree that it should be explicit.
> How about this for an alternate
> definition of an lvalue:
> "Any expression which after removing any possible
> register storage-specifier, we can take the address of." 

Function identifiers aren't lvalues either,
even though functions have addresses.

-- 
pete
0
Reply pfiland (6613) 7/16/2005 10:01:34 AM

pete wrote:
> 
> Netocrat wrote:
> 
> > > After it decays into int*, it's no longer an lvalue.
> >
> > What would stop it from being an lvalue?  It fits the definition of
> > "[a]n expression with an object type ... other than void".

N869

       6.3.2  Other operands
       6.3.2.1  Lvalues and function designators

       [#3] Except when it is the operand of the sizeof operator or
       the  unary  &  operator,  or  is  a  string  literal used to
       initialize an array, an expression that has type ``array  of
       type''  is converted to an expression with type ``pointer to
       type'' that points to  the  initial  element  of  the  array
       object  

and  is  not  an  lvalue.


-- 
pete
0
Reply pfiland (6613) 7/16/2005 10:45:44 AM

Netocrat <netocrat@dodo.com.au> wrote:
> On Sat, 16 Jul 2005 00:32:38 +0000, S.Tobias wrote:
>> Netocrat <netocrat@dodo.com.au> wrote:
>>> On Fri, 15 Jul 2005 13:57:21 +0000, S.Tobias wrote:
>>>> Netocrat <netocrat@dodo.com.au> wrote:
>>>>> On Thu, 14 Jul 2005 13:39:16 +0000, S.Tobias wrote:

>How about this for an alternate
> definition of an lvalue: "Any expression which after removing any possible
> register storage-specifier, we can take the address of."  

Perhaps it's close, I don't know.  You have to define what it means
"to take the address of" - if it's applying "&" operator, then
how do you define what can be operand to it - something that 
"&" can be applied to?  As pete indicated, function types
must be taken care of; also void expressions and bit fields.


>>>>>>   struct s { int a[1]; };
>>>>>>   struct s f(void);
>>>>>> 
>>>>>>   f().a[0] = 7;  //UB
>>>>>> 
>>>>>> The expression on the left is clearly a modifiable lvalue.
>>>> 
>>>> This is a minor variation on an example from a previous thread in
>>>> c.l.c:
>>>>   struct s {...} s1, s2;
>>>>   (s1 = s2).a[0] = 7;  //UB
>>> 
>>> I remember reading that thread - "Undefined behaviour when modifying
>>> the result of an assignment operator".
>>> 
>>>>> Not so clearly according to gcc.  I get: "ISO C90 forbids
>>>>> subscripting non-lvalue array".
>>>> 
>>>> gcc is lying.  The expression "f().a[0]" is perfectly valid.

Well... perhaps not then.

>>> 
>>> It's perfectly valid as a non-modifiable lvalue; but we both agree that
>>> assigning to it as a modifiable lvalue invokes undefined behaviour.
>> 
>> Try it with on-line como - there's not a single warnings. Cut'n'paste
>> version:
>>   int main()
>>   {
>>   struct s { int a[1]; };
>>   struct s f(void);
>>   f().a[0] = 7;  /*UB*/
>>   }
> 
> Similarly as for gcc, we don't get warnings in C99 mode, but in C90
> mode on-line como gives:
> 
> "ComeauTest.c", line 5: error: invalid use of non-lvalue array
>    f().a[0] = 7;  /*UB*/
>    ^
> 

Yes, thanks, I didn't test all possible options.  I didn't find
any significant changes in constraints to "[]" and "*", and semantics
of "." operators.  Perhaps the difference between C90 and C99
results only from the change in the definition and interpretation 
of an lvalue.

Also I have noticed the parts "If an attempt is made to modify
the result of [...] or to access it after the next sequence point,
the behavior is undefined." referring to assignments and function
calls are not there in C90.

>>[snip]
>>>>   int a[1];
>>>>   a[0];
>>>> `a' is not a modifiable lvalue, `a[0]' is (cf. Rationale).
>>> 
>>> Agreed, but I was talking about f(), not f().a.  In the case of f(), it
>>> does have to be.  If the struct returned by f() is not a modifiable
>>> lvalue, then neither are the elements of its member array.
>> 
>> It doesn't matter, really.
> 
> It does though.  You ignored my reasoning.  f() returns a struct.  If
> that struct in this context is not a modifiable lvalue, then its elements
> aren't modifiable lvalues either.  This applies to the elements of its
> array member.
> 
>> `a' is not an lvalue, but `a[0]' is.
> ITYM "modifiable lvalue".  But yes, in the generic case where a is a
> non-const array, I agree.
Thanks, it was late.

>> Similarly, `f().a' is not an lvalue;
> Again, ITYM that it is an lvalue but not a modifiable lvalue.
"Similarly" is not the right word here, but since `f()' is not
an lvalue, neither is `f().a' (before conversion to pointer).
(By accident I was actually right.)  This is a cv:
  &f().a;


>> `f().a[0]' is the same as
>> `*(f().a + 0)', dereferencing a pointer is always an lvalue (in this
>> case it is even well defined by the Std, because `f().a' presumably
>> points to some valid temporary storage).  Since its type is `int', it's
>> a modifiable lvalue.
> 
> Your reasoning is sound but predicated on the basis that f() itself is a
> modifiable lvalue.  

No, `f()' is not even an lvalue, and neither is `f().a'.  
But `f().a[0]' is.

>It falls apart when that is not the case... ie
> consider const struct s cs.  Now cs.a[0] is not a modifiable lvalue
> because cs is not.  The same reasoning applies to f().

`cs.a[0]' is not modifiable because members inherit qualifiers
(here: `const') from their containing types.  It is not modifiable
not because `cs' isn't, but because its type is `const int'.
This is a modifiable lvalue:
  ((int*)cs.a)[0]  //UB if modified

For another example:
  struct s { int i; const int ci; } s;
`s' is a non-modifiable lvalue, but `s.i' is modifiable.

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/16/2005 3:58:59 PM

Netocrat <netocrat@dodo.com.au> wrote:
> On Sat, 16 Jul 2005 01:05:01 +0000, S.Tobias wrote:
>> Netocrat <netocrat@dodo.com.au> wrote:
>>> On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote:

>>> N869, 6.3.2.1
>>> "An lvalue is an expression with an object type or an incomplete type
>>> other than void; if an lvalue does not designate an object when it is
>>> evaluated, the behavior is undefined."
>> 
>> The `*ps' is not evaluated in this case.
> 
> As I've noted elsewhere, you are correct for C99 but in C90 *ps is
> evaluated - at least conceptually i.e. this is a constraint violation:
> 
> int *n = NULL.
> int *p;
> p = &*n;

I don't think so.  In C90 6.2.2.1 there's a paragraph which is equivalent
of 6.3.2.1#2 in C99 (in n869.txt) (they seem the same, but I haven't 
checked).  It means that at least the value of `*n' is not taken (because
it is operand to "&").  I don't see any cv in C90 except that `*n' is
not strictly an lvalue, which cannot (in general) be established at
compile time (due to poor definition of an lvalue).

In C99 in 6.5.3.2#3 it says that in "&*n", "*" is not evaluated.  What
I think it means (and why it's necessary) is this:  To evaluate "*"
is to establish lvalue for the object that `n' points to (it doesn't yet
mean to take a value - it wouldn't occur anyway in presence of "&").
It is UB if an lvalue doesn't designate an object, and it would
be UB if "*" were evaluated in this case; since it isn't, "&*n"
is well defined for all values of `n'.


>>>>   struct s;
>>>>   struct s *ps;
>>>>   & * ps;
>>>> The sub-expression `*ps' has an incomplete struct type, and is an lvalue.
> 
> But seriously, clearly *ps on its own is nonsensical; surely you agree
> with me on that?  

I agree.  But OTOH the notion of an lvalue is more of a pointer, or
half-dereferenced pointer (which establishes only a position of an
object, but not its range or value).  In this context "& * ps" is
not strange.  
Apart from that, an lvalue is rather a tag in the grammatical
description of a language, that is, certain expressions are labeled
"lvalues", whether there is an object or not.

>What does your compiler say to the simple statement
> *ps; (gcc tells me where to get off)?  That I haven't yet found a specific
> prohibition in the standard merely convinces me that I don't know where to
> look.

I haven't found either.  I'm prepraring a question on that, too.

> 
> I'm not satisfied that your code is an example of an incomplete struct
> occurring as an lvalue.  In the case where &* is being treated as a no-op,
> then semantically *ps (and hence the incomplete struct) never occurs.  

But you have to know in advance if "*ps" is an lvalue before you
can put "&" and "*ps" together (whether "&*" is a no-op, or not);
if it weren't an lvalue, then there'd be constraint violation.

And in C99 "&*" is not quite a no-op: the expression after that
is no longer an lvalue (if it was before):
  n = 0;
  &*n = 0;  //CV


-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/16/2005 5:18:54 PM

Peter Nilsson <airia@acay.com.au> wrote:
> S.Tobias wrote:
>> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
>> > S.Tobias wrote:
>> >> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
>> >>
>> >> >  char *foo();
>> >> >
>> >> >  void bah(const char *x)
>> >> >  {
>> >> >    char *y = foo(x);
>> >>
>> >> You raise UB here: (const char*) and (char*) types are
>> >> not compatible - you cannot pass incompatible arguments
>> >> to a function call (cf. 6.5.2.2#6).
>> >
>> > C99 has different wording from N869.
>> >
>> > [It looks like it's not valid C90 though.]
>>
>> I'm not sure what you mean.  Here's the quote from the C99 Standard
>> I was referring to:
>> (6.5.2.2p6)
>> #  If the expression that denotes the called function has a type that
>> #  does not include a prototype, [...]
>> #  [...] If the function is defined with a type that includes a
>> #  prototype, and either the prototype ends with an ellipsis (, ...)
>> #  or the types of the arguments after promotion are not compatible
>> #  with the types of the parameters, the behavior is undefined. [...]
> 
> At the time of calling, the function (foo) isn't defined with a
> prototype. So the behaviour is determined by the '...' you snipped.

(I snipped only irrelevant parts.)

At the point of call, only the declaration "char *foo()" is visible,
therefore at that point the expression "foo" has a function type
without a prototype.  6.5.2.2p6 is applicable.

The function is defined (elsewhere, no matter where, might be in 
a different translation unit) with a type that includes a prototype:
  char *foo(char *x) {/*...*/}
That's where the next quoted part applies.

I don't see any ambiguity here.


If the function were defined (again: anywhere) with a type
without a prototype, ie:
  char *foo(x) char *x; {/*...*/}
then further (snipped) part would apply.

However, I think the second "dash" exception is ambiguous
(in how "both" and the two "or"s should be interpreted):
  const char *pc;
  foo(pc); //UB?

[Warning: there're differences between n869 and the Std there.]

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/16/2005 6:19:02 PM

On Sat, 16 Jul 2005 10:45:44 +0000, pete wrote:

> pete wrote:
>> 
>> Netocrat wrote:
>> 
>> > > After it decays into int*, it's no longer an lvalue.
>> >
>> > What would stop it from being an lvalue?  It fits the definition of
>> > "[a]n expression with an object type ... other than void".
>
> N869
> 
>        6.3.2  Other operands
>        6.3.2.1  Lvalues and function designators
> 
>        [#3] Except when it is the operand of the sizeof operator or the
>        unary  &  operator,  or  is  a  string  literal used to
>        initialize an array, an expression that has type ``array  of
>        type''  is converted to an expression with type ``pointer to
>        type'' that points to  the  initial  element  of  the  array
>        object
> 
> and  is  not  an  lvalue.

Which you seem to be justifying using the logic of your previous post:

> "A cast does not yield an lvalue"
>
> The result of a type conversion, can't be an lvalue.

This falsely gives equivalence to the terms "cast" and "type conversion". 
See the wording of N869, 6.3#1 for evidence that this is false.  The
conversion by which an array decays into a pointer is not a cast and not
subject to such rules.

As further proof consider that if the pointer to which the array decays
were not an lvalue, we would be prohibited from assigning to its elements.

> char object;
> 
> (int)object = INT_MAX; /* no good */
> 
> Only one byte of memory is reserved for object. There isn't really an
> int type object there.

Agreed that (int)object is not an lvalue, but its non-modifiability is
not the proof.

0
Reply netocrat (497) 7/16/2005 9:28:41 PM

On Sat, 16 Jul 2005 17:18:54 +0000, S.Tobias wrote:

> Netocrat <netocrat@dodo.com.au> wrote:
>> On Sat, 16 Jul 2005 01:05:01 +0000, S.Tobias wrote:
>>> Netocrat <netocrat@dodo.com.au> wrote:
>>>> On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote:
> 
>>>> N869, 6.3.2.1
>>>> "An lvalue is an expression with an object type or an incomplete type
>>>> other than void; if an lvalue does not designate an object when it is
>>>> evaluated, the behavior is undefined."
>>> 
>>> The `*ps' is not evaluated in this case.
>> 
>> As I've noted elsewhere, you are correct for C99 but in C90 *ps is
>> evaluated - at least conceptually i.e. this is a constraint violation:
>> 
>> int *n = NULL.
>> int *p;
>> p = &*n;
> 
> I don't think so.

Perhaps if you read the defect reports that I referenced you would.  Those
reports are #012, #076 and #106, the one dealing specifically with this
case being #076.

Note the final words of #012, dealing with a related case of void *p;
"...as long as a diagnostic message is issued, a translator may assign a
meaning to the expression &*p discussed above. Conforming programs shall
not use this expression, however."

Pretty explicit.

> In C90 6.2.2.1 there's a paragraph which is equivalent
> of 6.3.2.1#2 in C99 (in n869.txt)  (they seem the same, but I haven't
> checked).  It means that at least the value of `*n' is not taken
> (because it is operand to "&").  

I only have access to n869 and the C89 draft.  n869 has no 6.2.2.1 and
the C89 draft has different numbering, so I don't know where to find that
paragraph.  Quote it if you can.

Given the defect reports though, the behaviour required under C90 is clear.

> I don't see any cv in C90 except that `*n' is
> not strictly an lvalue, which cannot (in general) be established at
> compile time (due to poor definition of an lvalue).

That's significant enough to prevent the program from being conforming, so
why downplay it?

Defect report #076:
"Subclause 6.3.3.2 requires the operand of &to be an lvalue; NULL is
not an lvalue. [...] [Therefore t]he use of [the construct *n where n ==
NULL] prevents a program from being strictly conforming..."

> In C99 in 6.5.3.2#3 it says that in "&*n", "*" is not evaluated.  What I
> think it means (and why it's necessary) is this:  To evaluate "*" is to
> establish lvalue for the object that `n' points to (it doesn't yet mean
> to take a value - it wouldn't occur anyway in presence of "&"). 

How could evaluating "*" - in the case where "&*" is not a no-op -
not result in taking a value?

Perhaps you would accept this as a clarification/expansion: at compile
time it is established whether the "*" operator results in an lvalue, but
it cannot always be established that it is legal to evaluate that lvalue.
At runtime evaluating that lvalue may in some cases be illegal.

Also the definition of the indirection operator is lacking in the case of
pointer to incomplete struct.

N869, 6.5.3.2#4:
       The  unary  *  operator  denotes  indirection.  If the
       operand points to a  function,  the  result  is  a  function
       designator;  if  it  points  to  an object, the result is an
       lvalue designating the object.   If  the  operand  has  type 
       ``pointer  to  type'',  the result has type ``type''.  If an
       invalid value has been assigned to the pointer, the behavior
       of the unary * operator is undefined.74)

In this case, the operand points to neither a function nor an object,
since an incomplete type cannot be an object (as implied by the definition
of lvalue).  So all that is specified in this case is the type.  This is
where I would have expected to find wording to the effect of "if it points
to an incomplete type, the result is that a syntax error occurs except in
cases where the & operator is immediately applied".

> It is UB
> if an lvalue doesn't designate an object, and it would be UB if "*" were
> evaluated in this case; 

Agreed.

> since it isn't, "&*n" is well defined for all
> values of `n'.

Only in C99 - this is not true in C90 as the defect reports clarify.

>>>>>   struct s;
>>>>>   struct s *ps;
>>>>>   & * ps;
>>>>> The sub-expression `*ps' has an incomplete struct type, and is an
>>>>> lvalue.
>> 
>> But seriously, clearly *ps on its own is nonsensical; surely you agree
>> with me on that?
> 
> I agree.  But OTOH the notion of an lvalue is more of a pointer, or
> half-dereferenced pointer (which establishes only a position of an
> object, but not its range or value).  In this context "& * ps" is not
> strange.

It's not strange, but without the &* no-op, it's invalid.  In 6.3.2.1 as
quoted top of post: "if an lvalue does not designate an object when it is
evaluated, the behavior is undefined."  Since *ps represents an incomplete
type, it cannot be an object.  Therefore the behaviour is undefined.  

So 6.3.2.1 is effectively saying, "an expression with incomplete type is
an lvalue, but cannot be evaluated as one".  Which is pretty meaningless
except for the &* case.

<snip>
 
>> I'm not satisfied that your code is an example of an incomplete struct
>> occurring as an lvalue.  In the case where &* is being treated as a
>> no-op, then semantically *ps (and hence the incomplete struct) never
>> occurs.
> 
> But you have to know in advance if "*ps" is an lvalue before you can put
> "&" and "*ps" together (whether "&*" is a no-op, or not); if it weren't
> an lvalue, then there'd be constraint violation.

So we could say that *ps is known in advance to be an lvalue but its
evaluation as such is prohibited.

> And in C99 "&*" is not quite a no-op: the expression after that is no
> longer an lvalue (if it was before):
>   n = 0;
>   &*n = 0;  //CV

Agreed, I missed that.

0
Reply netocrat (497) 7/16/2005 9:31:49 PM

Netocrat wrote:
> 
> On Sat, 16 Jul 2005 10:45:44 +0000, pete wrote:
> 
> > pete wrote:
> >>
> >> Netocrat wrote:
> >>
> >> > > After it decays into int*, it's no longer an lvalue.
> >> >
> >> > What would stop it from being an lvalue?
> >> > It fits the definition of
> >> > "[a]n expression with an object type ... other than void".
> >
> > N869
> >
> >        6.3.2  Other operands
> >        6.3.2.1  Lvalues and function designators
> >
> >        [#3] Except when it is the operand of the sizeof
> >        operator or the
> >        unary  &  operator,  or  is  a  string  literal used to
> >        initialize an array, an expression that has type ``array  of
> >        type''  is converted to an expression with type ``pointer to
> >        type'' that points to  the  initial  element  of  the  array
> >        object
> >
> > and  is  not  an  lvalue.
> 
> Which you seem to be justifying using the logic of your previous post:

No. 
I'm quoting the standard for the purpose 
of correctly answering your question:
"What would stop it from being an lvalue?"

The "and  is  not  an  lvalue." is quoted from the standard.
I separated it from the rest of the text to make it stand out.


> > "A cast does not yield an lvalue"
> >
> > The result of a type conversion, can't be an lvalue.
> 
> This falsely gives equivalence to the
> terms "cast" and "type conversion".

Yes.
I should have supplied the verse from the standard originally.

-- 
pete
0
Reply pfiland (6613) 7/17/2005 12:14:39 AM

On Sun, 17 Jul 2005 00:14:39 +0000, pete wrote:

<snip>

> I'm quoting the standard for the purpose 
> of correctly answering your question:
> "What would stop 
[an array decayed to a pointer]
> from being an lvalue?"
> 
> The "and  is  not  an  lvalue." is quoted from the standard. I separated
> it from the rest of the text to make it stand out.

OK, you and Stan are correct.

0
Reply netocrat (497) 7/17/2005 1:34:32 AM

S.Tobias wrote:
> Peter Nilsson <airia@acay.com.au> wrote:
> > S.Tobias wrote:
> >> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
> >> > S.Tobias wrote:
> >> >> In comp.lang.c Peter Nilsson <airia@acay.com.au> wrote:
> >> >>
> >> >> >  char *foo();
> >> >> >
> >> >> >  void bah(const char *x)
> >> >> >  {
> >> >> >    char *y = foo(x);
> >> >>
> >> >> You raise UB here: (const char*) and (char*) types are
> >> >> not compatible - you cannot pass incompatible arguments
> >> >> to a function call (cf. 6.5.2.2#6).
> >> >
> >> > C99 has different wording from N869.
> >> >
> >> > [It looks like it's not valid C90 though.]
> >>
> >> I'm not sure what you mean.  Here's the quote from the C99 Standard
> >> I was referring to:
> >> (6.5.2.2p6)
> >> #  If the expression that denotes the called function has a type that
> >> #  does not include a prototype, [...]
> >> #  [...] If the function is defined with a type that includes a
> >> #  prototype, and either the prototype ends with an ellipsis (, ...)
> >> #  or the types of the arguments after promotion are not compatible
> >> #  with the types of the parameters, the behavior is undefined. [...]
> >
> > At the time of calling, the function (foo) isn't defined with a
> > prototype. So the behaviour is determined by the '...' you snipped.
>
> (I snipped only irrelevant parts.)
>
> At the point of call, only the declaration "char *foo()" is visible,
> therefore at that point the expression "foo" has a function type
> without a prototype.  6.5.2.2p6 is applicable.
>
> The function is defined (elsewhere, no matter where, might be in
> a different translation unit) with a type that includes a prototype:
>   char *foo(char *x) {/*...*/}
> That's where the next quoted part applies.
>
> I don't see any ambiguity here.

Indeed, you are correct. I had it in my mind that C99 removed K&R style
function definitions.

Thank you for the correction.

-- 
Peter

0
Reply airia (1802) 7/17/2005 2:47:58 AM

On Sat, 16 Jul 2005 15:58:59 +0000, S.Tobias wrote:
> Netocrat <netocrat@dodo.com.au> wrote:
>> On Sat, 16 Jul 2005 00:32:38 +0000, S.Tobias wrote:
>>> Netocrat <netocrat@dodo.com.au> wrote:
>>>> On Fri, 15 Jul 2005 13:57:21 +0000, S.Tobias wrote:
>>>>> Netocrat <netocrat@dodo.com.au> wrote:
>>>>>> On Thu, 14 Jul 2005 13:39:16 +0000, S.Tobias wrote:
> 
>> How about this for an alternate
>> definition of an lvalue: "Any expression which after removing any possible
>> register storage-specifier, we can take the address of."  
> 
> Perhaps it's close, I don't know.  You have to define what it means
> "to take the address of" - if it's applying "&" operator, then
> how do you define what can be operand to it - something that 
> "&" can be applied to?  

Yes, "an expression that we can take the address of" would equate to "an
expression that we can legally apply the & operator to", although the
current definition of & relies on the definition of lvalue so this
dependency would have to be reversed or we have infinite recursion.

The current definition is more descriptive, but this alternative
definition works better for me.  YMMV.

On first reading 6.3.2.1#1 it's not clear that there are qualifications
and messiness elsewhere.  e.g. there is a separate footnote for each of the
cases of a comma operator, a cast, and a conditional expression; each
footnote specifying that the case in question does "not yield an lvalue".
Another example: the result of a function call is not an lvalue but this
isn't made explicit - the word "lvalue" does not occur in 6.5.2.2 and a
function return doesn't seem to violate the current lvalue definition of
"an expression with an object type or an incomplete type other than void".
It would seem to fit into the same category as a cast which is explicitly
defined as a non-lvalue.

Using this definition I don't have to consider such issues as part of the
lvalue definition - I can defer them to the & operator. Likewise I can
define the & operator in terms of lvalue and defer them back to the lvalue
definition.  So I get to have my cake and eat it too. :)

> As pete indicated, function types
> must be taken care of; also void expressions and bit fields.

Hmm, a void expression can't occur as an lvalue, and we can reword for the
others:

"Any expression, apart from a function, which after removing any possible
register storage-specifier and treating a bit-field type as though it were
an ordinary structure member, we may legally take the address of."

<snip>

>>>   int main()
>>>   {
>>>   struct s { int a[1]; };
>>>   struct s f(void);
>>>   f().a[0] = 7;  /*UB*/
>>>   }
>>
>> Similarly as for gcc, we don't get warnings in C99 mode, but in C90
>> mode on-line como gives:
>> 
>> "ComeauTest.c", line 5: error: invalid use of non-lvalue array
>>    f().a[0] = 7;  /*UB*/
>>    ^
>> 
> Yes, thanks, I didn't test all possible options.  I didn't find any
> significant changes in constraints to "[]" and "*", and semantics of "."
> operators.  Perhaps the difference between C90 and C99 results only from
> the change in the definition and interpretation of an lvalue.

That's it.  The wording of 6.3.2.1 has changed from: "lvalue that has
value" to "expression that has value", relaxing the requirement for an
array to be an lvalue before it will decay into the pointer used to access
its elements.

<snip>

>>>>>   int a[1];
>>>>>   a[0];
>>>>> `a' is not a modifiable lvalue, `a[0]' is (cf. Rationale).
>>>> 
>>>> Agreed, but I was talking about f(), not f().a.  In the case of f(),
>>>> it does have to be.  If the struct returned by f() is not a
>>>> modifiable lvalue, then neither are the elements of its member array.
>>> 
>>> It doesn't matter, really.

<snip my false reasoning>

>>> `a' is not an lvalue, but `a[0]' is.
>> ITYM "modifiable lvalue".  But yes, in the generic case where a is a
>> non-const array, I agree.
> Thanks, it was late.
> 
>>> Similarly, `f().a' is not an lvalue;
>> Again, ITYM that it is an lvalue but not a modifiable lvalue.
> "Similarly" is not the right word here, but since `f()' is not an
> lvalue, neither is `f().a' (before conversion to pointer). (By accident
> I was actually right.)  This is a cv:
>   &f().a;

So in this case we can validly claim that if the parent struct is not a
modifiable lvalue then neither is the member.  However I accept your
reasoning that it doesn't extend to the array elements - you are right
that they are modifiable lvalues (invoking UB on modification) even though
f() and f().a are not.

<snip rest>

0
Reply netocrat (497) 7/17/2005 9:15:33 AM

Netocrat wrote:

> >> How about this for an alternate
> >> definition of an lvalue:

This is Lawrence Kirby's comments and corrections to my last attempt:

http://groups-beta.google.com/group/comp.lang.c/msg/27ff959caf56f520?hl=en&

Have you yet noticed that a constant expression like (5),
fits the C99 definition of an Lvalue which does not refer
to an object and causes undefined behavior when evaluated?

-- 
pete
0
Reply pfiland (6613) 7/17/2005 9:57:38 AM

pete wrote:
> Netocrat wrote:
> 
> 
>>>>How about this for an alternate
>>>>definition of an lvalue:
> 
> 
> This is Lawrence Kirby's comments and corrections to my last attempt:
> 
> http://groups-beta.google.com/group/comp.lang.c/msg/27ff959caf56f520?hl=en&
> 
> Have you yet noticed that a constant expression like (5),
> fits the C99 definition of an Lvalue which does not refer
> to an object and causes undefined behavior when evaluated?
> 
It is clear that 5 cannot be an lvalue and that the Standard is never 
wrong. That puts our ability to 'read for comprehension' in question. :-)

-- 
Joe Wright
"Everything should be made as simple as possible, but not simpler."
                     --- Albert Einstein ---
0
Reply joewwright (1737) 7/17/2005 12:06:29 PM

On Sun, 17 Jul 2005 09:57:38 +0000, pete wrote:

> Netocrat wrote:
> 
>> >> How about this for an alternate
>> >> definition of an lvalue:
> 
> This is Lawrence Kirby's comments and corrections to my last attempt:
> 
> http://groups-beta.google.com/group/comp.lang.c/msg/27ff959caf56f520?hl=en&

Your definition with corrections seems pretty complete except for
incomplete types.

> Have you yet noticed that a constant expression like (5),
> fits the C99 definition of an Lvalue which does not refer
> to an object and causes undefined behavior when evaluated?

That has to be a test question, right?

Going by N869 as I don't have the standard:

You claim that the C99 definition of an lvalue "does not refer to
an object", when it plainly does.

You also claim that a constant expression like (5) fits the C99 definition
of an lvalue, which it plainly does not.  This constant expression is
neither an incomplete type, nor an object which the draft defines at
3.15#1 as a "region of data storage in the execution environment, the
contents of which can represent values" (the constant expression (5)
conceptually is not stored).

Finally your question implies that there is some sense in which evaluating
a constant as an lvalue leads to undefined behaviour, which again is
plainly false since a constant can never be evaluated as an lvalue. 
Any attempt to treat it as a modifiable lvalue by assigning to it will be
caught as a syntax error, and will not cause undefined behaviour.

Did I pass?

The constant expression (5) contrasts with Lawrence Kirby's example of a
string literal, which must have data storage.  His example also provides a
good test for my definition which holds up - it is valid to take the
address of a string literal.

In the thread you reference, Tim Rentsch comments:

> Sadly there doesn't seem to be a simple means of
> identifying lvalues the way assignment does for modifiable
> lvalues. 

With qualifications, a simple means of identifying an lvalue is to check
whether the address-of operator can be applied to the expression.

The qualifications are that you must exclude functions, remove register
storage-specifiers and treat bit-fields as though they were addressable
like other struct members are.  I don't know whether that's simple enough.

0
Reply netocrat (497) 7/17/2005 12:57:14 PM

Netocrat wrote:
> 
> On Sun, 17 Jul 2005 09:57:38 +0000, pete wrote:

> > Have you yet noticed that a constant expression like (5),
> > fits the C99 definition of an Lvalue which does not refer
> > to an object and causes undefined behavior when evaluated?
> 
> That has to be a test question, right?
> 
> Going by N869 as I don't have the standard:
> 
> You claim that the C99 definition of an lvalue "does not refer to
> an object", when it plainly does.

No.
I claim that 5 is an lvalue.
I claim that 5 does not represent an object.


> You also claim that a constant expression like
> (5) fits the C99 definition
> of an lvalue, which it plainly does not. 

> This constant expression is
> neither an incomplete type, nor an object which the draft defines at
> 3.15#1 as a "region of data storage in the execution environment, the
> contents of which can represent values" (the constant expression (5)
> conceptually is not stored).

It makes no difference that 5 is not an object.
The definition of lvalue only requires an expression of object type.
5 is an expression of type int.
int is an object type.
There's 3 kinds of types: 
object types, incomplete types, and function types.
5 is an expression with an object type, therefore 5 is an lvalue.
5 does not represent an object.
Evaluating 5 causes undefined behavior.

N869
       6.3.2  Other operands
       6.3.2.1  Lvalues and function designators
       [#1] An lvalue is an expression with an object  type  or  an
       incomplete type other than void; if an  lvalue  does  not
       designate  an  object  when it is evaluated, the behavior is
       undefined.

> Did I pass?

No. You confused "object" with "object type".
Even though object types describe objects,
constant expressions also have object type.

5
5u
5.0
5.0f

-- 
pete
0
Reply pfiland (6613) 7/17/2005 3:16:24 PM

Netocrat <netocrat@dodo.com.au> writes:

> On Sun, 17 Jul 2005 09:57:38 +0000, pete wrote:
> 
> > Have you yet noticed that a constant expression like (5),
> > fits the C99 definition of an Lvalue which does not refer
> > to an object and causes undefined behavior when evaluated?
> 
> That has to be a test question, right?
> 
> Going by N869 as I don't have the standard:

Quoting ISO/IEC 9899:1999(E) 6.3.2.1 p1:

    An lvalue is an expression with an object type or an
    incomplete type other than void;

This definition is hopelessly broken.  Almost every expression is
an lvalue, including '(3+4)', '&i' (after 'int i;'), and even 'f'
(after 'int f(void);'), because a function type is converted to
pointer-to-function, which is an object type.

Other than expressions that happen to be void, about the only
expressions that aren't lvalues are things like the 'f' in
'sizeof f' or '&f' (but '&f' is still an lvalue).  I realize this
isn't how CLC'ers are used to thinking of what "lvalue" means,
but if one looks at what the document actually says, essentially
all expressions are lvalues.

Furthermore, the definition excludes expressions that are
usefully considered with similar expressional forms:

    char *p;  struct foo *s;  void *v;
    *p;  /* this is an lvalue */
    *s;  /* so is this, even when 'struct foo' is an incomplete type */
    *v;  /* this isn't an lvalue */

Clearly there are two distinct notions here:  a syntactic notion
defining expressions that are (possible) candidates for
modification, and a semantic notion defining expressions that
refer to values but are not themselves values (ie, expressions
that designate objects).  The combination of the first notion and
the second notion together with additional constraints such as
non-const-ness, etc, produce what we're used to calling a
<i>modifiable lvalue</i>.

I expect we're stuck with the term "lvalue" for the semantic
notion of designating an object, probably better termed "object
locator" or something similar.  It would be an improvement to
give terms and defintions for both notions.  What's most
important is that the definition for the object locator property
needs to be redone.


> In the thread you reference, Tim Rentsch comments:
> 
> > Sadly there doesn't seem to be a simple means of
> > identifying lvalues the way assignment does for modifiable
> > lvalues. 
> 
> With qualifications, a simple means of identifying an lvalue is to check
> whether the address-of operator can be applied to the expression.
> 
> The qualifications are that you must exclude functions, remove register
> storage-specifiers and treat bit-fields as though they were addressable
> like other struct members are.  I don't know whether that's simple enough.

A good operational definition for modifiable lvalue is "can it be
written on the left hand side of an assignment operator"?

It seems rather useless (not counting having discussions in CLC)
to have a simple means to identify lvalues, because (1) the
definition is broken, and (2) what's usually needed is a test
that answers "is it legal to write thus and such an expression in
thus and such context", which is to say a syntactic constraint.
Most C programmers don't need to understand the term "lvalue"
as it is used in the standard.
0
Reply txr (1104) 7/17/2005 7:09:39 PM

On Sun, 17 Jul 2005 15:16:24 +0000, pete wrote:

> Netocrat wrote:
>> 
>> On Sun, 17 Jul 2005 09:57:38 +0000, pete wrote:
> 
>> > Have you yet noticed that a constant expression like (5),
>> > fits the C99 definition of an Lvalue which does not refer
>> > to an object and causes undefined behavior when evaluated?
>> 
>> That has to be a test question, right?
>> 
>> Going by N869 as I don't have the standard:
>> 
>> You claim that the C99 definition of an lvalue "does not refer to
>> an object", when it plainly does.
> 
> No.

OK, I misinterpreted that bit.  You meant (5) rather than the C99
definition of an lvalue. 

> I claim that 5 is an lvalue.
> I claim that 5 does not represent an object.

Agreed that your second claim is true.

The intent of the standard is that the above two claims cannot both be
true (except in the case of indirection, where it's not always clear at
compile-time whether an expression will evaluate to a valid object or not,
and in the case where 5 represents an incomplete type, which we agree it
does not).

Look at 6.3.2.1#2 where it refers to "the value stored in the designated
object", clearly indicating as I said that it is intended that only
expressions that designate an actual object (or can potentially designate
in the case of indirection) are to be considered lvalues.

Therefore the first claim is untrue.

This accords with my intuitive definition - it's illegal to take the
address of a constant.

<snip>

> Evaluating 5 causes undefined behavior.

6.5.16#2
       An assignment operator shall have a  modifiable  lvalue
       as its left operand.

Since 5 is not even an lvalue, let alone a modifiable lvalue, this
paragraph applies and attempting to assign to 5 is a constraint
violation - it will be rejected by a conforming compiler at compile time. 
And there is nothing undefined about evaluating 5 in any other context -
it evaluates to "type int with value 5" according to 6.4.4.1#5.

>> Did I pass?
> 
> No.

I resubmit my thesis for second consideration.

Actually I'm glad that you weren't testing - it would have been too much
like trolling.

0
Reply netocrat (497) 7/17/2005 8:05:14 PM

Netocrat <netocrat@dodo.com.au> wrote:
> On Sat, 16 Jul 2005 17:18:54 +0000, S.Tobias wrote:
>> Netocrat <netocrat@dodo.com.au> wrote:
>>> On Sat, 16 Jul 2005 01:05:01 +0000, S.Tobias wrote:
>>>> Netocrat <netocrat@dodo.com.au> wrote:
>>>>> On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote:

>>>>> N869, 6.3.2.1
>>>>> "An lvalue is an expression with an object type or an incomplete type
>>>>> other than void; if an lvalue does not designate an object when it is
>>>>> evaluated, the behavior is undefined."
>>>> 
>>>> The `*ps' is not evaluated in this case.
>>> 
>>> As I've noted elsewhere, you are correct for C99 but in C90 *ps is
>>> evaluated - at least conceptually i.e. this is a constraint violation:
>>> 
>>> int *n = NULL.
>>> int *p;
    int i;
    n = is_moon_waxing ? NULL : &i;
>>> p = &*n;
    CV in C90?  It is undiagnosable here.
>> 
>> I don't think so.
> 
> Perhaps if you read the defect reports that I referenced you would.  Those
> reports are #012, #076 and #106, the one dealing specifically with this
> case being #076.
[snip]

It just means that the Standard had its problems, that's why they
keep correcting it.  It still has...


>> In C90 6.2.2.1 there's a paragraph which is equivalent
>> of 6.3.2.1#2 in C99 (in n869.txt)  (they seem the same, but I haven't
>> checked).  It means that at least the value of `*n' is not taken
>> (because it is operand to "&").  
> 
> I only have access to n869 and the C89 draft.  n869 has no 6.2.2.1 and
> the C89 draft has different numbering, so I don't know where to find that
> paragraph.  Quote it if you can.

In the C89 draft that would be 3.2.2.1 par 2.  It's basically
the same as C99 6.3.2.1#2.

> 
> Given the defect reports though, the behaviour required under C90 is clear.
> 
>> I don't see any cv in C90 except that `*n' is
>> not strictly an lvalue, which cannot (in general) be established at
>> compile time (due to poor definition of an lvalue).
> 
> That's significant enough to prevent the program from being conforming, so
> why downplay it?
> 
> Defect report #076:
> "Subclause 6.3.3.2 requires the operand of &to be an lvalue; NULL is
> not an lvalue. [...] [Therefore t]he use of [the construct *n where n ==
> NULL] prevents a program from being strictly conforming..."

Yes, I can only agree with it.  And all compilers should issue
whatever diagnostic each time, just in case, or else they'll risk
violating the Standard.

> 
>> In C99 in 6.5.3.2#3 it says that in "&*n", "*" is not evaluated.  What I
>> think it means (and why it's necessary) is this:  To evaluate "*" is to
>> establish lvalue for the object that `n' points to (it doesn't yet mean
>> to take a value - it wouldn't occur anyway in presence of "&"). 
> 
> How could evaluating "*" - in the case where "&*" is not a no-op -
> not result in taking a value?

Evaluating "*" and taking a value are two different processes.  
1. The result of "*" is an lvalue (or function designator) (6.5.3.2#4).  
2. If a (non-array) lvalue is not an operand to "&", "++", "--", "." or 
   "=", it is "converted to the value stored in the designated object 
   (and is no longer an lvalue)" (6.3.2.1#2) (that's when the value 
   is read).

> 
> Perhaps you would accept this as a clarification/expansion: at compile
> time it is established whether the "*" operator results in an lvalue, but
> it cannot always be established that it is legal to evaluate that lvalue.
> At runtime evaluating that lvalue may in some cases be illegal.
> 
> Also the definition of the indirection operator is lacking in the case of
> pointer to incomplete struct.
> 
> N869, 6.5.3.2#4:
>       The  unary  *  operator  denotes  indirection.  If the
>       operand points to a  function,  the  result  is  a  function
>       designator;  if  it  points  to  an object, the result is an
>       lvalue designating the object.   If  the  operand  has  type 
>       ``pointer  to  type'',  the result has type ``type''.  If an
>       invalid value has been assigned to the pointer, the behavior
>       of the unary * operator is undefined.74)
> 
> In this case, the operand points to neither a function nor an object,
> since an incomplete type cannot be an object (as implied by the definition
> of lvalue).  

Pointers to incomplete types may point to objects.  An object is
simply a range of storage (see the definition somewhere in clause 3.).

>So all that is specified in this case is the type.  This is
> where I would have expected to find wording to the effect of "if it points
> to an incomplete type, the result is that a syntax error occurs except in
> cases where the & operator is immediately applied".

No, the result is just an lvalue of incomplete struct type.  
The value is not accessed yet.  You have a bad idea of what lvalues
are.  They can be incomplete type - see again the definition at the
top of this article.

> 
>> It is UB
>> if an lvalue doesn't designate an object, and it would be UB if "*" were
>> evaluated in this case; 

It's not quite true perhaps; UB is invoked when an lvalue that doesn't
designate an object is evaluated.  It seems that by not evaluating "*"
the Standard wants to accomodate invalid pointers (cf. result of "*").

>> since it isn't, "&*n" is well defined for all
>> values of `n'.
> 
> Only in C99 - this is not true in C90 as the defect reports clarify.

Yes, I was writing this only in context of C99.


> 
>>>>>>   struct s;
>>>>>>   struct s *ps;
>>>>>>   & * ps;
>>>>>> The sub-expression `*ps' has an incomplete struct type, and is an
>>>>>> lvalue.
>>> But seriously, clearly *ps on its own is nonsensical; surely you agree
>>> with me on that?
>> I agree.  But OTOH the notion of an lvalue is more of a pointer, or
>> half-dereferenced pointer (which establishes only a position of an
>> object, but not its range or value).  In this context "& * ps" is not
>> strange.
>It's not strange, but without the &* no-op, it's invalid.  In 6.3.2.1 as
>quoted top of post: "if an lvalue does not designate an object when it is
>evaluated, the behavior is undefined."  Since *ps represents an incomplete
>type, it cannot be an object.  Therefore the behaviour is undefined.

Not automatically, only when the lvalue is evaluated.
`*ps' is not an object; `ps' may point to an object, `*ps' is
an lvalue designating that object ("lvalue" == think "pointer").
When operands to "&", lvalues are not evaluated.

>So 6.3.2.1 is effectively saying, "an expression with incomplete type is
>an lvalue, but cannot be evaluated as one".  Which is pretty meaningless
>except for the &* case.
(and except when the incomplete lvalue is an array type)

Generally, yes, I think.  There doesn't seem much you can do
with incomplete lvalues.

>>> I'm not satisfied that your code is an example of an incomplete struct
>>> occurring as an lvalue.  In the case where &* is being treated as a
>>> no-op, then semantically *ps (and hence the incomplete struct) never
>>> occurs.
>> 
>> But you have to know in advance if "*ps" is an lvalue before you can put
>> "&" and "*ps" together (whether "&*" is a no-op, or not); if it weren't
>> an lvalue, then there'd be constraint violation.

Sorry, I was wrong for C99 here, but it holds for C90, and for other 
operators where lvalue is required ("++", "--", "=").  See below.

> So we could say that *ps is known in advance to be an lvalue but its
> evaluation as such is prohibited.

Precisely, that's how I would like to see it.

But note that the C99 Standard handles this in a different manner for
"&" operator (and is slightly inconsistent in words).  It says its 
operand must be (among others) the result of "*" operator, and "*" 
(in "&*") is not evaluated.  This way it tries to weasel out of trouble
of special cases (such as in the above DRs).  I don't see anything
that is technically wrong with this.  

Maybe you have a point above: since "*" is not evaluated, we don't
really know if `*ps' is an lvalue.  But OTOH the subexpression `*ps'
is an expression and the question whether it is an lvalue is valid. 
Everything indicates that it is.
One more example:
  extern struct s s;
  struct s *ps = &s;
  *ps;  //UB
The last line raises UB because lvalue `*ps' is incomplete type
(and cannot be converted to a value; the last sentence of 6.3.2.1).
What I mean to show is that in order to explain why UB arises, first
you have to assert that `*ps' is lvalue (which is actually trivial,
for the result of "*" is always an lvalue, so there's no question
about it here).

All this is very confusing, especially that there's no good
definition of lvalue in the Standard.  I get confused too.
You have to have a brain size of a planet to understand C lvalues
- undoubtedly I don't.

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/17/2005 8:25:35 PM

Tim Rentsch <txr@alumnus.caltech.edu> wrote:

> Clearly there are two distinct notions here:  a syntactic notion
> defining expressions that are (possible) candidates for
> modification, and a semantic notion defining expressions that
> refer to values but are not themselves values (ie, expressions
> that designate objects).  The combination of the first notion and
> the second notion together with additional constraints such as
> non-const-ness, etc, produce what we're used to calling a
> <i>modifiable lvalue</i>.
> 
> I expect we're stuck with the term "lvalue" for the semantic
> notion of designating an object, probably better termed "object
> locator" or something similar.  It would be an improvement to
> give terms and defintions for both notions.  What's most
> important is that the definition for the object locator property
> needs to be redone.

IMHO the semantic notion of lvalue in C is unnecessary and
causes only problems.  I think the Std could leave out
the definition of lvalue (it could only describe its 
semantic purpose in non-normative text), tag all expressions
as lvalues or values, and keep the words "if an  lvalue does not
designate an object when it is evaluated, the behavior is
undefined", and possibly a few other bits.  The idea of
an lvalue is used mostly in Constraints.

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/17/2005 8:53:54 PM

Netocrat wrote:
> 
> On Sun, 17 Jul 2005 15:16:24 +0000, pete wrote:
> 
> > Netocrat wrote:
> >>
> >> On Sun, 17 Jul 2005 09:57:38 +0000, pete wrote:
> >
> >> > Have you yet noticed that a constant expression like (5),
> >> > fits the C99 definition of an Lvalue which does not refer
> >> > to an object and causes undefined behavior when evaluated?
> >>
> >> That has to be a test question, right?
> >>
> >> Going by N869 as I don't have the standard:
> >>
> >> You claim that the C99 definition of an lvalue "does not refer to
> >> an object", when it plainly does.
> >
> > No.
> 
> OK, I misinterpreted that bit.  You meant (5) rather than the C99
> definition of an lvalue.
> 
> > I claim that 5 is an lvalue.
> > I claim that 5 does not represent an object.
> 
> Agreed that your second claim is true.
> 
> The intent of the standard is that the above two claims
> cannot both be true (except in the case of indirection,
> where it's not always clear at compile-time whether an
> expression will evaluate to a valid object or not,
> and in the case where 5 represents an incomplete type,
> which we agree it does not).

I know that's the intent of the standard, but
"An lvalue is an expression with an object  type"
is what it says.

-- 
pete
0
Reply pfiland (6613) 7/18/2005 12:53:31 AM

On Sun, 17 Jul 2005 12:09:39 -0700, Tim Rentsch wrote:

> Netocrat <netocrat@dodo.com.au> writes:
> 
>> On Sun, 17 Jul 2005 09:57:38 +0000, pete wrote:
>> 
>> > Have you yet noticed that a constant expression like (5),
>> > fits the C99 definition of an Lvalue which does not refer
>> > to an object and causes undefined behavior when evaluated?
>> 
>> That has to be a test question, right?
>> 
>> Going by N869 as I don't have the standard:
> 
> Quoting ISO/IEC 9899:1999(E) 6.3.2.1 p1:
> 
>     An lvalue is an expression with an object type or an
>     incomplete type other than void;
> 
> This definition is hopelessly broken.  

At this point in the thread after reading and considering (hopefully most
of the relevant parts of) the standard, I have to disagree.  Yes, if
that sentence were the entire definition it would be useless.  Yes, I
agree that it should be worded much better.  The definition is a little 
scattered, nevertheless the concept of lvalue is fully and properly
defined by the standard.

The Rationale explains that the committee decided between giving lvalue
the meaning that "modifiable lvalue" now has, and the definition it
eventually did give lvalue, which it describes as that of an "object
locator".  Also note footnote 46 in N869 which says:
          The  name ``lvalue'' comes originally from the assignment
          expression E1 = E2, in  which  the  left  operand  E1  is
          required  to  be  a  (modifiable)  lvalue.  It is perhaps better
          considered as  representing  an  object  ``locator value''.
Note the implication that the difference between lvalue and modifiable
lvalue is slight, which is not how you and Pete have been reading the
standard.  Your readings accord lvalue status to constants such as 5 and
(3 + 4), which are in no way related to the "original" definition of
lvalue as the left operand of the assignment operator.  Rather, the two
terms "locator value" and "object locator" used in footnote 46 and the
rationale respectively both clearly refer to something in storage -
referring to that something as an lvalue implies "finding" where it is
stored.

This "being found in storage" can be equated to taking the address of the
object (with qualifications) which is why I think my alternate definition
of an lvalue as being a valid operand of & (with qualifications) is apt.
I've used this concept to guide my interpretation of the Standard in what
follows.

The rest of footnote 46 says that "[w]hat is sometimes called ``rvalue''
is in this International Standard described as the ``value of an
expression''." I prefer the simplicity of the term rvalue especially in
contrast to lvalue so I'll use it in this post.

Let me summarise my interpretation then:

6.3.2.1#1: Limits lvalue to object type or incomplete type but not void.
           The incomplete type is included to allow for things like
           incomplete arrays which can still represent "a region of
           data storage" (defn of object) even though their size is not
           known.  I won't comment on incomplete structs because that's
           ongoing elsewhere in this thread and I don't want the debate
           distract us here.
6.3.2.1#2: Clarifies that the "expression with an object type" must
           represent an _actual_ object ("the designated object") for it
           to be defined as an lvalue (although 6.3.2.1#1 makes an
           exception for indirection of a pointer to an invalid object -
           this is clearly an unwanted exception that must be allowed so
           that "lvalue-ness" can be determined at compile time).
6.3.2.1#3: Specifies that a decayed array is an rvalue, not an lvalue.
           This initially confused me because it could alternately have
           been defined that the decayed array still represents the same
           object in memory (although given that the original array is a
           non-modifiable lvalue, there's no way that treating the decayed
           pointer as a non-modifiable lvalue would give it any different
           semantics than it now has).  It is also consistent with the
           other type conversion rule chosen - that the result of a cast
           is always an rvalue, not an lvalue.
Footnotes (numbering from N869):
76,83,85)  Specify that a cast, a conditional expression and a comma
           operator each yield an rvalue, not an lvalue.  A cast need not
           have been defined this way because it could alternately have
           been deemed to represent the same object in storage as that it
           was cast from, but just represents it using a different type.
           Admittedly that would have introduced a few problems but the
           point is that it could have been done.  Neither the conditional
           expression nor comma case need necessarily have been defined
           this way for every case either.  Given int x, y, z; the
           expression (z?x:y) simplifies to either x or y, both of which
           are objects in storage.  OTOH it is never possible to interpret
           the result of (z?4:5) as an object in storage.  So it is
           simplest to adopt the conceptual model that these three
           operations return rvalues, not lvalues.

Stan Tobias has argued that the Standard should explicitly specify for
each expression type whether it is an lvalue or not.  I don't think that
that's a bad idea, but I also think that according to the above, every
expression is already specified.  i.e. in the case where an expression
does not certainly represent an object in storage (and is not an
indirection) it is an rvalue, not an lvalue.  In any case where there is
potential confusion, the standard clarifies which of rvalue or lvalue
applies.

> Almost every expression is
> an lvalue, including 

I'll take your cases one by one.

> '(3+4)',

As I argued above and in my previous reply to Pete
(<pan.2005.07.17.20.05.13.344779@dodo.com.au>) by my reading the standard
requires that an lvalue designate an object (something in storage), which
this expression does not.  Therefore it is an rvalue (this of course
coincides with programmer expectations).

> '&i' (after 'int i;'),

Again, &i does not represent a stored object, therefore it is an rvalue
not an lvalue.

> and even 'f' (after
> 'int f(void);'), because a function type is converted to
> pointer-to-function, which is an object type.

Still not an lvalue (a function result is not required to be located
somewhere in storage, it is just required that it can be treated as a
value), therefore not an rvalue.
 
> Other than expressions that happen to be void, about the only
> expressions that aren't lvalues are things like the 'f' in 'sizeof f' or
> '&f'

If that were truly the case, the concept of lvalue would indeed be broken.
Considering that the address-off operator is defined in terms of lvalue
then things like &(3 + 4) would be legal, which they clearly should not be.

But I agree that f in sizeof f (as well as sizeof f itself and f in any
other context) is not an lvalue.  In particular footnote 74 distinguishes
between a "function designator" and an "lvalue that is a valid operand of
the unary & operator".  Given that a function designator is a valid
operand of the unary & operator, this clarifies that a function designator
is not to be considered to be an lvalue.

> (but '&f' is still an lvalue).

Again I disagree - it is an rvalue since it does not represent an object
in storage.

> I realize this isn't how CLC'ers
> are used to thinking of what "lvalue" means, but if one looks at what
> the document actually says, essentially all expressions are lvalues.

This suggests that the standard is not clearly enough worded, although
if you dig deep enough as we have been doing in this thread, you get to
the nuggets.

> Furthermore, the definition excludes expressions that are usefully
> considered with similar expressional forms:
> 
>     char *p;  struct foo *s;  void *v;
>     *p;  /* this is an lvalue */

Yup.

>     *s;  /* so is this, even when 'struct foo' is an incomplete type */

Yes it is an lvalue but it's not possible to access it as one (which
makes the concept useless): 6.3.2.1#1 says that "if an lvalue does not
designate an object when it is evaluated, the behavior is undefined.".

>     *v;  /* this isn't an lvalue */

True, as explicitly defined.  The point of the void type is really to
allow for pointer to void.  It has no meaning as an lvalue - _what_ object
in storage is being located?  By definition there is no object.  And
before you say, "oh but what about &*v?", reread the thread because
we've ascertained that whilst strictly speaking this is a constraint
violation in C90, most implementations will ignore it and do what C99
requires, which is that &* be optimised away and the result no longer be
an lvalue.
 
> Clearly there are two distinct notions here:  a syntactic notion
> defining expressions that are (possible) candidates for modification,
> and a semantic notion defining expressions that refer to values but are
> not themselves values (ie, expressions that designate objects).  The
> combination of the first notion and the second notion together with
> additional constraints such as non-const-ness, etc, produce what we're
> used to calling a <i>modifiable lvalue</i>.

That's an excellent description.  The second notion defines a plain
lvalue, which accords with what I've described an lvalue to be since an
object in 3.15 is defined as "a region of data storage".  You clearly have
the same idea as me of what an lvalue is _intended_ to be, you just
believe that the standard has stuffed up the definition.  I hope you'll
accept my reasoning that this isn't the case, although it could be better
worded.

> I expect we're stuck with the term "lvalue" for the semantic notion of
> designating an object, probably better termed "object locator" or
> something similar.  It would be an improvement to give terms and
> defintions for both notions.  What's most important is that the
> definition for the object locator property needs to be redone.

Agreed that object locator is a better term; agreed that the definition is
scattered and not obvious, but as I've tried to show, it isn't broken and
doesn't necessarily have to be redone.

>> In the thread you reference, Tim Rentsch comments:
>> 
>> > Sadly there doesn't seem to be a simple means of identifying lvalues
>> > the way assignment does for modifiable lvalues.
>> 
>> With qualifications, a simple means of identifying an lvalue is to
>> check whether the address-of operator can be applied to the expression.
>> 
>> The qualifications are that you must exclude functions, remove register
>> storage-specifiers and treat bit-fields as though they were addressable
>> like other struct members are.  I don't know whether that's simple
>> enough.
> 
> A good operational definition for modifiable lvalue is "can it be
> written on the left hand side of an assignment operator"?
> 
> It seems rather useless (not counting having discussions in CLC) to have
> a simple means to identify lvalues, because (1) the definition is
> broken, and (2) what's usually needed is a test that answers "is it
> legal to write thus and such an expression in thus and such context",
> which is to say a syntactic constraint. Most C programmers don't need to
> understand the term "lvalue" as it is used in the standard.

Yes that's true.  It doesn't have syntactic relevance but it does have
semantic relevance - i.e. it answers the question "can we consider that
this expression is stored"?  OTOH given that my definition relies on the &
operator that question is better answered by referring directly to the &
operator, rather than indirectly through my lvalue definition.

0
Reply netocrat (497) 7/18/2005 12:59:27 AM

On Mon, 11 Jul 2005 09:22:08 +1000, Netocrat <netocrat@dodo.com.au>
wrote:

> On Mon, 11 Jul 2005 00:53:27 +0400, Alexei A. Frounze wrote:
<snip>
> > Anyway, can you suggest a declaration of the function's argument that
> > would simultaneously satisfy:
> > - being a pointer to an array of a fixed number of elements, say, 5
> > integers - this array is to be constant, in other words, its elements
> > can't be altered by using the pointer
> 
> No.  To the best of my understanding it is not possible according to the
> standard.
> 
Yes. Exactly as the OP did, use a pointer actually to the array as a
whole, not to its elements, which is what you normally use in C and
what (C) programmers normally mean by "pointer to the/an array".
(And use prototype declaration for your function, or as in my brief
examples prototype definition in the same translation unit.)

void func (const int *x) 
/* or exactly equivalent void func (const int x[]) */
{ can read but not write x[i] for suitable values of i }
.... int a [5], b[10]; func (a); func (b); /* both OK */ ...

void func (const int (*x)[5])
{ can read but not write (*x)[i] note additional indirection }
.... int a[5], b[10]; func (&a); /* OK */ 
func (&b); /* incompatible pointer */ ...

c.l.c FAQ 6.13, and the rest of section 6, at the usual places and
http://www.eskimo.com/~scs/C-faq/top.html 

- David.Thompson1 at worldnet.att.net
0
Reply david.thompson1 (1042) 7/18/2005 2:21:44 AM

On Mon, 18 Jul 2005 02:21:44 +0000, Dave Thompson wrote:

> On Mon, 11 Jul 2005 09:22:08 +1000, Netocrat <netocrat@dodo.com.au>
> wrote:
> 
>> On Mon, 11 Jul 2005 00:53:27 +0400, Alexei A. Frounze wrote:
> <snip>
>> > Anyway, can you suggest a declaration of the function's argument that
>> > would simultaneously satisfy:
>> > - being a pointer to an array of a fixed number of elements, say, 5
>> > integers - this array is to be constant, in other words, its elements
>> > can't be altered by using the pointer
>> 
>> No.  To the best of my understanding it is not possible according to the
>> standard.
>> 
> Yes. Exactly as the OP did, use a pointer actually to the array as a
> whole, not to its elements, which is what you normally use in C and
> what (C) programmers normally mean by "pointer to the/an array".
> (And use prototype declaration for your function, or as in my brief
> examples prototype definition in the same translation unit.)
> 
> void func (const int *x) 
> /* or exactly equivalent void func (const int x[]) */
> { can read but not write x[i] for suitable values of i }
> ... int a [5], b[10]; func (a); func (b); /* both OK */ ...
> 
> void func (const int (*x)[5])
> { can read but not write (*x)[i] note additional indirection }
> ... int a[5], b[10]; func (&a); /* OK */ 
> func (&b); /* incompatible pointer */ ...
> 
> c.l.c FAQ 6.13, and the rest of section 6, at the usual places and
> http://www.eskimo.com/~scs/C-faq/top.html 

Yup, that all works fine Dave but what Alexei actually wanted was more
specific and can't be done - at least no one who's responded to this
thread so far has found a way.

He wanted to pass a pointer to an array of a certain number of elements
(i.e. your first prototype is insufficient, but your second is sufficient)
into a function and be guaranteed that the contents of the array could not
be modified within the function.  But he also wanted to be able to pass in
parameters of a type that were not const-qualified in any way eg.
int(*)[5], and have them automatically cast to the required type ie const
int (*)[5].  But this seems to be prohibited by C's automatic
const-conversion rules.

0
Reply netocrat (497) 7/18/2005 2:44:00 AM

On Mon, 18 Jul 2005 00:53:31 +0000, pete wrote:

> Netocrat wrote:
>> 
>> On Sun, 17 Jul 2005 15:16:24 +0000, pete wrote:
>> 
>> > Netocrat wrote:
>> >>
>> >> On Sun, 17 Jul 2005 09:57:38 +0000, pete wrote:
>> >
>> >> > Have you yet noticed that a constant expression like (5),
>> >> > fits the C99 definition of an Lvalue which does not refer
>> >> > to an object and causes undefined behavior when evaluated?
>> >>
>> >> That has to be a test question, right?
>> >>
>> >> Going by N869 as I don't have the standard:
>> >>
>> >> You claim that the C99 definition of an lvalue "does not refer to
>> >> an object", when it plainly does.
>> >
>> > No.
>> 
>> OK, I misinterpreted that bit.  You meant (5) rather than the C99
>> definition of an lvalue.
>> 
>> > I claim that 5 is an lvalue.
>> > I claim that 5 does not represent an object.
>> 
>> Agreed that your second claim is true.
>> 
>> The intent of the standard is that the above two claims
>> cannot both be true (except in the case of indirection,
>> where it's not always clear at compile-time whether an
>> expression will evaluate to a valid object or not,
>> and in the case where 5 represents an incomplete type,
>> which we agree it does not).
> 
> I know that's the intent of the standard, but
> "An lvalue is an expression with an object  type"
> is what it says.

But not _all_ that it says.  It has several footnotes, one of which says
that the result of a conditional expression is not an lvalue.  By your
reasoning I should ignore that footnote because given int x, y, z, z?x:y
has object type and that's all that matters.

Clearly the footnote should be considered as furthering the very limited
9-word definition you quote, _just_ as the wording of paragraph 2 which
describes "the designated object" furthers this definition by clarifying
that merely having object type is insufficient, the expression must
actually be an object.

0
Reply netocrat (497) 7/18/2005 2:52:30 AM

On Sun, 17 Jul 2005 20:53:54 +0000, S.Tobias wrote:

> Tim Rentsch <txr@alumnus.caltech.edu> wrote:
> 
>> Clearly there are two distinct notions here:  a syntactic notion
>> defining expressions that are (possible) candidates for
>> modification, and a semantic notion defining expressions that
>> refer to values but are not themselves values (ie, expressions
>> that designate objects).  The combination of the first notion and
>> the second notion together with additional constraints such as
>> non-const-ness, etc, produce what we're used to calling a
>> <i>modifiable lvalue</i>.
>> 
>> I expect we're stuck with the term "lvalue" for the semantic
>> notion of designating an object, probably better termed "object
>> locator" or something similar.  It would be an improvement to
>> give terms and defintions for both notions.  What's most
>> important is that the definition for the object locator property
>> needs to be redone.
> 
> IMHO the semantic notion of lvalue in C is unnecessary and
> causes only problems.  I think the Std could leave out
> the definition of lvalue (it could only describe its 
> semantic purpose in non-normative text), tag all expressions
> as lvalues or values, and keep the words "if an  lvalue does not
> designate an object when it is evaluated, the behavior is
> undefined", and possibly a few other bits.  The idea of
> an lvalue is used mostly in Constraints.

Sure, that's not a bad idea, but I don't think that it's necessary and I
do think that the semantic notion is useful, although not in the broad
way that Pete and Tim have interpreted it as applying to e.g. constants.

I think all would be clear if the first sentence of 6.3.2.1#1 were
changed from:

"An lvalue is an expression with object type or an incomplete type other
than void; if an lvalue does not designate an object when it is evaluated,
the behaviour is undefined"

to:

"An lvalue is an expression that either resolves to an object or to an
incomplete type in storage, or resolves to the indirection of an
expression of type 'pointer to object' or 'pointer to incomplete type'; if
an lvalue does not designate an object when it is evaluated, the behaviour
is undefined"

Note that "object type" has been changed simply to "object".  This makes
it explicit that non-objects such as the constant expression (5) and
the return of a function are not defined as lvalues, without having to
read down and find this description tucked away separately in paragraph 2.

The void wording has been removed because a void type properly defined
cannot represent an object.  I haven't checked whether it is so defined
elsewhere though so perhaps it is required.

As I interpret it, this is how the standard should be read anyway, but
only after integrating the wording of paragraph 6.3.2.1#2 with paragraph
6.3.2.1#1 and using the footnotes/Rationale to understand that this
integration is proper.

0
Reply netocrat (497) 7/18/2005 2:54:42 AM

Netocrat wrote:
> On Mon, 18 Jul 2005 00:53:31 +0000, pete wrote:
> 
> 
>>Netocrat wrote:
>>
>>>On Sun, 17 Jul 2005 15:16:24 +0000, pete wrote:
>>>
>>>
>>>>Netocrat wrote:
>>>>
>>>>>On Sun, 17 Jul 2005 09:57:38 +0000, pete wrote:
>>>>
>>>>>>Have you yet noticed that a constant expression like (5),
>>>>>>fits the C99 definition of an Lvalue which does not refer
>>>>>>to an object and causes undefined behavior when evaluated?
>>>>>
>>>>>That has to be a test question, right?
>>>>>
>>>>>Going by N869 as I don't have the standard:
>>>>>
>>>>>You claim that the C99 definition of an lvalue "does not refer to
>>>>>an object", when it plainly does.
>>>>
>>>>No.
>>>
>>>OK, I misinterpreted that bit.  You meant (5) rather than the C99
>>>definition of an lvalue.
>>>
>>>
>>>>I claim that 5 is an lvalue.
>>>>I claim that 5 does not represent an object.
>>>
>>>Agreed that your second claim is true.
>>>
>>>The intent of the standard is that the above two claims
>>>cannot both be true (except in the case of indirection,
>>>where it's not always clear at compile-time whether an
>>>expression will evaluate to a valid object or not,
>>>and in the case where 5 represents an incomplete type,
>>>which we agree it does not).
>>
>>I know that's the intent of the standard, but
>>"An lvalue is an expression with an object  type"
>>is what it says.
> 
> 
> But not _all_ that it says.  It has several footnotes, one of which says
> that the result of a conditional expression is not an lvalue.  By your
> reasoning I should ignore that footnote because given int x, y, z, z?x:y
> has object type and that's all that matters.
> 
> Clearly the footnote should be considered as furthering the very limited
> 9-word definition you quote, _just_ as the wording of paragraph 2 which
> describes "the designated object" furthers this definition by clarifying
> that merely having object type is insufficient, the expression must
> actually be an object.

Alas, footnotes in the standard are not normative.

The C99 standard is broken w.r.t. lvalues and if you apply
"very comprehensive" reading to make it not so, you essentially
can make it say anything in other places, by the same methods
of argumentation.
So, one accepts that 5 may be an lvalue and looks to the
modifiable lvalues for something to work with :-/


Cheers
  Michael
-- 
E-Mail: Mine is an   /at/ gmx /dot/ de   address.
0
Reply Michael.Mair (1492) 7/18/2005 5:57:55 AM

Netocrat <netocrat@dodo.com.au> writes:

> On Sun, 17 Jul 2005 12:09:39 -0700, Tim Rentsch wrote:
> 
> > Quoting ISO/IEC 9899:1999(E) 6.3.2.1 p1:
> > 
> >     An lvalue is an expression with an object type or an
> >     incomplete type other than void;
> > 
> > This definition is hopelessly broken.  
> 
> At this point in the thread after reading and considering (hopefully most
> of the relevant parts of) the standard, I have to disagree.  [snip]

I think you missed the point of what I was saying.  I know that
what was intended is (almost certainly) not what was written.
The problem is that, as written, the standard says almost all
expressions are lvalues.  I neglected when giving the original
quote to write:

     6.3.2.1

  1  An <i>lvalue</i> is an expression with an object type or an
     incomplete type other than void;  ...

The italics makes this statement the (only) definition, per
section 3, paragraph 1.


> Footnotes (numbering from N869):
> 76,83,85)  [cast, conditional expression, comma operators not lvalues]

Thank you for pointing these out (they appear in the standard as
footnotes numbers 85, 92, 94).  Even if footnotes were normative
(which they aren't), the presence of these footnotes doesn't
affect the basic point that, per a literal reading of the
standard text, most expressions are lvalues.


> > Almost every expression is
> > an lvalue, including 
> 
> I'll take your cases one by one.
> 
> > '(3+4)',
>[snip] 
> > '&i' (after 'int i;'),
>[snip]
> > and even 'f' (after
> > 'int f(void);'), because a function type is converted to
> > pointer-to-function, which is an object type.

The expressions '(3+4)', '&i', and 'f' are lvalues because
they meet the definition given in 6.3.2.1 p1, and no other
writing anywhere in the standard says anything different.


> > I realize this isn't how CLC'ers
> > are used to thinking of what "lvalue" means, but if one looks at what
> > the document actually says, essentially all expressions are lvalues.
> 
> This suggests that the standard is not clearly enough worded, although
> if you dig deep enough as we have been doing in this thread, you get to
> the nuggets.

The "nuggets", as you call them, are redundant per the definition
and simply further evidence that the writing is broken.

The C standard document is one that should be interpreted
according to a literal reading;  anything less means the authors
aren't doing their job.  No criticism of committee members or
others closely involved in the process intended - it's extremely
tough to write prose that is exactly literally accurate.  But for
a reference/standard document of this kind, that level of
accuracy should always be the goal.


Also, if you wouldn't mind a personal suggestion - your postings
make some good points, but they would be better if you spent a
little longer doing the writing, and made the writing itself a
bit shorter.
0
Reply txr (1104) 7/18/2005 7:10:22 AM

On Sun, 17 Jul 2005 20:25:35 +0000, S.Tobias wrote:

> Netocrat <netocrat@dodo.com.au> wrote:
>> On Sat, 16 Jul 2005 17:18:54 +0000, S.Tobias wrote:
>>> Netocrat <netocrat@dodo.com.au> wrote:
>>>> On Sat, 16 Jul 2005 01:05:01 +0000, S.Tobias wrote:
>>>>> Netocrat <netocrat@dodo.com.au> wrote:
>>>>>> On Fri, 15 Jul 2005 14:32:42 +0000, S.Tobias wrote:
> 
>>>>>> N869, 6.3.2.1
>>>>>> "An lvalue is an expression with an object type or an incomplete type
>>>>>> other than void; if an lvalue does not designate an object when it is
>>>>>> evaluated, the behavior is undefined."
>>>>> 
>>>>> The `*ps' is not evaluated in this case.
>>>> 
>>>> As I've noted elsewhere, you are correct for C99 but in C90 *ps is
>>>> evaluated - at least conceptually i.e. this is a constraint violation:
>>>> 
>>>> int *n = NULL.
>>>> int *p;
>     int i;
>     n = is_moon_waxing ? NULL : &i;
>>>> p = &*n;
>     CV in C90?  It is undiagnosable here.

OK, so no compiler will be to issue the required diagnostic and
technically a conforming implementation is impossible.  That doesn't
prevent it from being a CV according to DR#076.

>>> I don't think so.
>> 
>> Perhaps if you read the defect reports that I referenced you would. 
>> Those reports are #012, #076 and #106, the one dealing specifically
>> with this case being #076.
> [snip]
> 
> It just means that the Standard had its problems, that's why they keep
> correcting it.  It still has...

They may just be problems, but so saying doesn't remove them from the
standard.  You still have to consider them as part of C90.

>>> In C90 6.2.2.1 there's a paragraph which is equivalent of 6.3.2.1#2 in
>>> C99 (in n869.txt)  (they seem the same, but I haven't checked).  It
>>> means that at least the value of `*n' is not taken (because it is
>>> operand to "&").
>> 
>> I only have access to n869 and the C89 draft.  n869 has no 6.2.2.1 and
>> the C89 draft has different numbering, so I don't know where to find
>> that paragraph.  Quote it if you can.
> 
> In the C89 draft that would be 3.2.2.1 par 2.  It's basically the same
> as C99 6.3.2.1#2.

OK, I see it.  But C89 draft "3.3.3.2 Address and indirection operators"
says that the operand of unary & must be an object, and the DRs clarify
that that section takes precedence and is the reason that the wording was
changed for N869, so that "6.5.3.2 Address and indirection operators"
specifies that &* is a no-op.

<snip>

>>> In C99 in 6.5.3.2#3 it says that in "&*n", "*" is not evaluated. What
>>> I think it means (and why it's necessary) is this:  To evaluate "*" is
>>> to establish lvalue for the object that `n' points to (it doesn't yet
>>> mean to take a value - it wouldn't occur anyway in presence of "&").
>> 
>> How could evaluating "*" - in the case where "&*" is not a no-op - not
>> result in taking a value?
> 
> Evaluating "*" and taking a value are two different processes.  
> 1. The result of "*" is an lvalue (or function designator) (6.5.3.2#4).  
> 2. If a (non-array) lvalue is not an operand to "&", "++", "--", "." or 
>    "=", it is "converted to the value stored in the designated object 
>    (and is no longer an lvalue)" (6.3.2.1#2) (that's when the value 
>    is read).> Evaluating "*" and taking a value are two different
>    processes.

OK, there's a distinction, but the only case I can see it manifest is with
structures as this snippet shows:

int r;
int k[10];
struct {int i; int *j;} s, *sp = &s;
k[3] = 20;
s.j = k;
r = (*sp).j[3];

Considering the final expression in the context of *sp, obviously
6.3.2.1#2 applies and we get back an lvalue.  But as you correctly reason,
according to 6.5.3.2#4 it is not immediately converted to the value of
*sp, but rather the "." operator is applied to obtain (*sp).j which is an
lvalue that immediate decays to a non-lvalue pointer and is indexed etc,
yielding a final lvalue which by 6.5.3.2#4 is converted to a value.

So it's true that we do not fully "take the value" of (*sp), but we do
evaluate enough of it to find (*sp).j.  At the least this requires
that we determine the address in sp and add the offset of j to it, and
access the contents referenced by j.  So... that's quite a bit of "value
taking" ...

An lvalue is an object locator, so to dereference sp into an lvalue you
have to hunt around and find the object that it references, which you
will then do something with.  6.3.2.1#2 describes what that something
might be, and in all cases it requires at least some part of the value
of *sp to be taken.

I'm not sure that there's ever a situation where we "evaluate *" without
"taking its value" in at least some way.  Perhaps you can think of one
though.  The whole point of expressing an lvalue in code (apart from with
sizeof) is to store into it or read from its storage in some way...
so why _would_ there be a situation where we don't take it's value?

<snip>
 
>> Also the definition of the indirection operator is lacking in the case of
>> pointer to incomplete struct.
>> 
>> N869, 6.5.3.2#4:
>>       The  unary  *  operator  denotes  indirection.  If the
>>       operand points to a  function,  the  result  is  a  function
>>       designator;  if  it  points  to  an object, the result is an
>>       lvalue designating the object.   If  the  operand  has  type 
>>       ``pointer  to  type'',  the result has type ``type''.  If an
>>       invalid value has been assigned to the pointer, the behavior
>>       of the unary * operator is undefined.74)
>> 
>> In this case, the operand points to neither a function nor an object,
>> since an incomplete type cannot be an object (as implied by the definition
>> of lvalue).  
> 
> Pointers to incomplete types may point to objects.  An object is simply
> a range of storage (see the definition somewhere in clause 3.).

OK, I accept that.  So 6.5.3.2#4 doesn't always apply, but it may. Instead
we should look to the last sentence of 6.3.2.1#2 which says:

"If the lvalue has an incomplete type and does not have array type, the
behavior is undefined."

>>So all that is specified in this case is the type.  This is
>> where I would have expected to find wording to the effect of "if it
>> points to an incomplete type, the result is that a syntax error occurs
>> except in cases where the & operator is immediately applied".
> 
> No, the result is just an lvalue of incomplete struct type. The value is
> not accessed yet.

Well what's the point of using an lvalue in your code if you don't intend
its value to be accessed in any way???  I mean, the concept of an lvalue
is intended to be that of an "object locator".  Great, so you've located
an incomplete struct.  Now what?

> You have a bad idea of what lvalues are.  They can be incomplete type -
> see again the definition at the top of this article.

I understand that it says that, I just don't understand *why*.  I
haven't yet seen a satisfactory example where an lvalue that is of
incomplete struct type actually occurs in any context other than &*ps,
where we've established that it doesn't actually occur since the &* is
optimised away!  And if it can occur, then what type of data does it store???

>>> It is UB
>>> if an lvalue doesn't designate an object, and it would be UB if "*"
>>> were evaluated in this case;
> 
> It's not quite true perhaps; UB is invoked when an lvalue that doesn't
> designate an object is evaluated.  It seems that by not evaluating "*"
> the Standard wants to accomodate invalid pointers (cf. result of "*").

I'm not sure of your point.  I see nothing wrong with your original words.
My best interpretation is that you're saying that the &* no-op was added
to prevent conceptual NULL pointer dereferences, which I agree with.
 
>>> since it isn't, "&*n" is well defined for all values of `n'.
>> 
>> Only in C99 - this is not true in C90 as the defect reports clarify.
> 
> Yes, I was writing this only in context of C99.
> 
>>>>>>>   struct s;
>>>>>>>   struct s *ps;
>>>>>>>   & * ps;
>>>>>>> The sub-expression `*ps' has an incomplete struct type, and is an
>>>>>>> lvalue.
>>>> But seriously, clearly *ps on its own is nonsensical; surely you
>>>> agree with me on that?
>>> I agree.  But OTOH the notion of an lvalue is more of a pointer, or
>>> half-dereferenced pointer (which establishes only a position of an
>>> object, but not its range or value).  In this context "& * ps" is not
>>> strange.
>>It's not strange, but without the &* no-op, it's invalid.  In 6.3.2.1 as
>>quoted top of post: "if an lvalue does not designate an object when it
>>is evaluated, the behavior is undefined."  Since *ps represents an
>>incomplete type, it cannot be an object.  Therefore the behaviour is
>>undefined.
>
> Not automatically, only when the lvalue is evaluated. 

I assume you're referring again to C89 draft section 3.2.2.1#2:
"Except when it is the operand of .. the unary & operator .. an lvalue ..
is converted to the value stored in the designated object".

So again I'll point out C89 draft 3.3.3.2 which says that the operand of &
must be an object, and which the DRs clarify takes precedence.  It doesn't
matter whether or not it is evaluated - it is required to designate an
object.

You argued previously that it is possible that ps points to a struct fully
defined externally.  In that case, and that case only, &*ps is not a
constraint violation under C90.  So theoretically, this is fully compliant
code:

/** Begin exts1.c **/
struct exts {int i; int j;} es, *esp = &es;
void x(void);
int main(void)
{
        x();
}
/** End exts1.c **/

/** Begin exts2.c **/
struct exts *esp;
void x(void)
{
        struct exts *stmp;
        stmp = &*esp;
}
/** End exts2.c **/

Whereas if we remove the definition of esp from exts1.c, technically this
is undefined behaviour under C90.

In fact gcc gives an error in both cases in both C90 and C99 modes (but
we can assume that it is not properly implementing C99 here).

> `*ps' is not an
> object; `ps' may point to an object, `*ps' is an lvalue designating that
> object ("lvalue" == think "pointer"). When operands to "&", lvalues are
> not evaluated.

Fair enough.  But they still must represent valid objects under C90.

>>So 6.3.2.1 is effectively saying, "an expression with incomplete type is
>>an lvalue, but cannot be evaluated as one".
>>Which is pretty meaningless except for the &* case.
> (and except when the incomplete lvalue is an array type)
> 
> Generally, yes, I think.  There doesn't seem much you can do with
> incomplete lvalues.

That's the reason this thread started, so your general agreement is some
sort of closure.

<snip>

> Maybe you have a point above: since "*" is not evaluated, we don't
> really know if `*ps' is an lvalue.  But OTOH the subexpression `*ps' is
> an expression and the question whether it is an lvalue is valid.
> Everything indicates that it is.

Agreed, but what's the point?   An lvalue is meant to be accessed, and
since you agree that an incomplete struct of the form struct *ps can't 
be validly accessed as an lvalue, then why specify that it may be one? 
Fair enough in the case of C99 there are other cases of incomplete structs
(eg. with a variable array member) which could have validity as an lvalue,
but this isn't the case in C90.  So why define it as an lvalue?

<snip example>
 
> All this is very confusing

As is this thread - it's a mess that keeps getting bigger.  I'm starting
to repeat myself.

> especially that there's no good definition
> of lvalue in the Standard.  I get confused too. You have to have a brain
> size of a planet to understand C lvalues - undoubtedly I don't.

Well I've explained my interpretation in my reply to Tim Rentsch.

0
Reply netocrat (497) 7/18/2005 7:54:58 AM

On Mon, 18 Jul 2005 00:10:22 -0700, Tim Rentsch wrote:

> Netocrat <netocrat@dodo.com.au> writes:
> 
>> On Sun, 17 Jul 2005 12:09:39 -0700, Tim Rentsch wrote:
>> 
<snip>
> The problem is that, as written, the standard says almost all
> expressions are lvalues.  I neglected when giving the original
> quote to write:
> 
>      6.3.2.1
> 
>   1  An <i>lvalue</i> is an expression with an object type or an
>      incomplete type other than void;  ...
> 
> The italics makes this statement the (only) definition, per
> section 3, paragraph 1.

I can't argue against that.  The draft document I use is plain-text
without italics.

> The C standard document is one that should be interpreted
> according to a literal reading;  anything less means the authors
> aren't doing their job.

I have accessed a later draft dated May 6 2005 with the same wording. 
Can anyone comment on whether any committee members view the definition
as flawed?

0
Reply netocrat (497) 7/18/2005 9:09:46 AM

    (xpost from alt.os.development)

    Thanks! Really a brilliant explanation!

-- 
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com

"Richard Heathfield" <invalid@address.co.uk.invalid> wrote in message
news:db89ju$nrv$2@nwrdmz02.dmz.ncs.ea.ibs-infra.bt.com...
> [Followups set to comp.lang.c]
>
> Lukaszp wrote:
>
> > The problem is that array itself is a pointer to it's beginning so then
>
> Not quite. The array itself is an array. The /value/ of the array, when that
> array's name is used in a value context, is the address of the first
> element.
>
> > writing &aInt3 would give you pointer to pointer to an array. Solution
> > is to pass only aInt3 to your function.
>
> The declaration in question was:
>
>   int aInt3[5] = {0,1,2,4,9};
>
> When you write &aInt3, you're actually getting the address of the array (the
> array is not being used in a value context here); the type is int (*)[5],
> i.e. a pointer to an array, not a pointer to a pointer to an array as you
> incorrectly claimed. (This is a tricky area - it's intensely logical, but
> C's logic doesn't always map perfectly to programmers' first-cut instinct!)
>
> -- 
> Richard Heathfield
> "Usenet is a strange place" - dmr 29/7/1999
> http://www.cpax.org.uk
> mail: rjh at above domain


0
Reply maxim19 (63) 7/18/2005 8:27:49 PM

pete wrote:
> Have you yet noticed that a constant expression like (5),
> fits the C99 definition of an Lvalue which does not refer
> to an object and causes undefined behavior when evaluated?

I missed your implication that C90's definition is different.

So C90's is too restrictive and C99's too liberal, although the
intended definition is pretty clear.

A useful summary is this Keith Thompson post.

http://groups-beta.google.com/group/comp.lang.c/msg/bb8cd101f9096173?hl=en&

In future I'll try to search before replying.

0
Reply netocrat (497) 7/18/2005 11:00:12 PM

Netocrat <netocrat@dodo.com.au> wrote:
> On Sun, 17 Jul 2005 20:25:35 +0000, S.Tobias wrote:
>> Netocrat <netocrat@dodo.com.au> wrote:
>>> On Sat, 16 Jul 2005 17:18:54 +0000, S.Tobias wrote:

>>> How could evaluating "*" - in the case where "&*" is not a no-op - not
>>> result in taking a value?
>> 
>> Evaluating "*" and taking a value are two different processes.  
>> 1. The result of "*" is an lvalue (or function designator) (6.5.3.2#4).  
>> 2. If a (non-array) lvalue is not an operand to "&", "++", "--", "." or 
>>    "=", it is "converted to the value stored in the designated object 
>>    (and is no longer an lvalue)" (6.3.2.1#2) (that's when the value 
>>    is read).> Evaluating "*" and taking a value are two different
>>    processes.
> 
> OK, there's a distinction, but the only case I can see it manifest is with
> structures as this snippet shows:
> 
> int r;
> int k[10];
> struct {int i; int *j;} s, *sp = &s;
> k[3] = 20;
> s.j = k;
> r = (*sp).j[3];
> 
> Considering the final expression in the context of *sp, obviously
> 6.3.2.1#2 applies and we get back an lvalue.  But as you correctly reason,
> according to 6.5.3.2#4 it is not immediately converted to the value of
> *sp, but rather the "." operator is applied to obtain (*sp).j which is an
> lvalue that immediate decays to a non-lvalue pointer and is indexed etc,
> yielding a final lvalue which by 6.5.3.2#4 is converted to a value.

Correct.

> So it's true that we do not fully "take the value" of (*sp), but we do
> evaluate enough of it to find (*sp).j.  At the least this requires
> that we determine the address in sp and add the offset of j to it, and
> access the contents referenced by j.  So... that's quite a bit of "value
> taking" ...

Yes.  The Standard specifies that that member is read, and only that.
The implementation can read the struct as a whole, or its members
separately if it feels right, provided it doesn't make any visible
difference to the programmer, but this is not specified by the Std
(IOW what is specified is the minimum that must be done to execute
that fragment of a program).

> An lvalue is an object locator, so to dereference sp into an lvalue you
> have to hunt around and find the object that it references, which you
> will then do something with.  6.3.2.1#2 describes what that something
> might be, and in all cases it requires at least some part of the value
> of *sp to be taken.
> 
> I'm not sure that there's ever a situation where we "evaluate *" without
> "taking its value" in at least some way.  Perhaps you can think of one
> though.  The whole point of expressing an lvalue in code (apart from with
> sizeof) is to store into it or read from its storage in some way...
> so why _would_ there be a situation where we don't take it's value?

[snip]
> I understand that it says that, I just don't understand *why*.  I
> haven't yet seen a satisfactory example where an lvalue that is of
> incomplete struct type actually occurs in any context other than &*ps,
> where we've established that it doesn't actually occur since the &* is
> optimised away!  And if it can occur, then what type of data does it store???


I see your problem is: why should incomplete type expression to be
an lvalue at all?  The answer is: I don't know.  The Standard
establishes a language (metalanguage) through which it describes
the C language.  If it defines lvalue to be what it defines, you
have to follow it blindly.  I myself don't agree with it, I hate it,
I have my own, better opinion on what an lvalue should be.
But in order to interpret correctly other parts of the Standard
you have to accept what it defines.
For example,
  extern struct s s;
  struct s *sp = &s;
  void *vp = &s;
  *sp; //UB
  *vp; //okay
The first expression yields UB, and the second doesn't.  The only reason
for that is that the fist is an lvalue, while the second is not.

The Standard could define C in a different way, specifying things
case by case.  Then you would probably write today "why does the Std do 
it like that, it's so hard to remember all these cases; there's this
nice idea of lvalue, why not generalize things?".  Perhaps someone
tried to generalize, but it wasn't so easy.  Perhaps there is
a third way.

And have always in mind that people write standards, and those same 
people make errors.  It's difficult to discuss things with the lack of
proper definitions.  As you know by now, the Std defines as lvalues
expressions that should not be lvalues.  IMO that's not the biggest
sin yet.  Really bad thing is that the Std contradicts itself, because
some expressions that fulfill the definition of lvalue are explicitly
said not to be lvalues.



>>>> It is UB
>>>> if an lvalue doesn't designate an object, and it would be UB if "*"
>>>> were evaluated in this case;
>> 
>> It's not quite true perhaps; UB is invoked when an lvalue that doesn't
>> designate an object is evaluated.  It seems that by not evaluating "*"
>> the Standard wants to accomodate invalid pointers (cf. result of "*").
> 
> I'm not sure of your point.  I see nothing wrong with your original words.
> My best interpretation is that you're saying that the &* no-op was added
> to prevent conceptual NULL pointer dereferences, which I agree with.

Yes, and pointers to (and lvalues designating) one past the last array element
(it's automatically UB if they are operands to "*").



> So again I'll point out C89 draft 3.3.3.2 which says that the operand of &
> must be an object, and which the DRs clarify takes precedence.  It doesn't
> matter whether or not it is evaluated - it is required to designate an
> object.

Right.

> You argued previously that it is possible that ps points to a struct fully
> defined externally.  
(I needed an incomplete struct pointer that points to a valid object, and
it was impossible to have both complete and incomplete struct type on one line
(well, it is possible, but not as nice).)
>In that case, and that case only, &*ps is not a
> constraint violation under C90.  So theoretically, this is fully compliant
> code:

I think so, too.

> /** Begin exts1.c **/
> struct exts {int i; int j;} es, *esp = &es;
> void x(void);
> int main(void)
> {
>        x();
> }
> /** End exts1.c **/
> 
> /** Begin exts2.c **/
> struct exts *esp;
ITYM: extern struct exts *esp;
> void x(void)
> {
>        struct exts *stmp;
>        stmp = &*esp;
> }
> /** End exts2.c **/
> 
> Whereas if we remove the definition of esp from exts1.c, technically this
ITYM            remove the initialization of esp to &es
> is undefined behaviour under C90.

Yes.

> In fact gcc gives an error in both cases in both C90 and C99 modes (but
> we can assume that it is not properly implementing C99 here).

como did it fine.

One last remark: UB is not a disaster, it's just lack of a definition.
In this case I think it means C90 was defective, and C99 made a change
for better.  I seriously doubt any compiler would produce extra code
in `x()' just waiting when `esp' doesn't point to an object to let
the daemons fly in the face of an unsuspecting programmer.


-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/20/2005 12:26:47 AM

On Wed, 20 Jul 2005 00:26:47 +0000, S.Tobias wrote:

<snip>

> I see your problem is: why should incomplete type expression to be an
> lvalue at all?

Yes, exactly.  There's a need in the case of incomplete array and
partially complete struct expressions, but not but for totally
incomplete struct types.

> The answer is: I don't know.

Perhaps comp.std.c would have an answer, but since it's only a minor
redundancy it's so not much of a problem.

<snip>

> For example,
>   extern struct s s;
>   struct s *sp = &s;
>   void *vp = &s;
>   *sp; //UB
>   *vp; //okay
> The first expression yields UB,

But you seemed to agree (as per the ext1/2.c example below) that this
is only the case if s is not fully defined externally.

> and the second doesn't.  The only reason for that is that the fist is an
> lvalue, while the second is not.

Yes, 6.5.3.2 fails to define the behaviour.  vp is a pointer type as
paragraph 2 requires, but no case in paragraph 4 applies, so *vp is
undefined by ommission.

<snip>

> Really bad thing is that the Std contradicts itself, because some
> expressions that fulfill the definition of lvalue are explicitly said
> not to be lvalues.

Right - the decayed array type and the footnotes on casts,
conditionals, and commas.

<snip>

>> You argued previously that it is possible that ps points to a struct
>> fully defined externally.
> (I needed an incomplete struct pointer that points to a valid object,
> and it was impossible to have both complete and incomplete struct type
> on one line (well, it is possible, but not as nice).)
>>In that case, and that case only, &*ps is not a
>> constraint violation under C90.  So theoretically, this is fully
>> compliant code:
>
> I think so, too.
>
>> /** Begin exts1.c **/
>> struct exts {int i; int j;} es, *esp = &es; void x(void); int
>> main(void)
>> {
>>        x();
>> }
>> /** End exts1.c **/
>>
>> /** Begin exts2.c **/
>> struct exts *esp;
> ITYM: extern struct exts *esp;

I did, but in this situation extern is redundant, by 6.2.2#5.

>> void x(void)
>> {
>>        struct exts *stmp;
>>        stmp = &*esp;
>> }
>> /** End exts2.c **/
>>
>> Whereas if we remove the definition of esp from exts1.c, technically
>> this
> ITYM            remove the initialization of esp to &es

I didn't, but that's an even better example.

>> is undefined behaviour under C90.
>
> Yes.
>
>> In fact gcc gives an error in both cases in both C90 and C99 modes (but
>> we can assume that it is not properly implementing C99 here).
>
> como did it fine.

I can't see a way to compile two separate files on como online so I
can't confirm that.  Anyhow it's a difference that can't be detected at
compile time.

0
Reply netocrat (497) 7/20/2005 3:23:27 AM

Netocrat wrote:
> On Wed, 20 Jul 2005 00:26:47 +0000, S.Tobias wrote:

> >> /** Begin exts1.c **/
> >> struct exts {int i; int j;} es, *esp = &es; void x(void); int
> >> main(void)
> >> {
> >>        x();
> >> }
> >> /** End exts1.c **/
> >>
> >> /** Begin exts2.c **/
> >> struct exts *esp;
> > ITYM: extern struct exts *esp;
>
> I did, but in this situation extern is redundant, by 6.2.2#5.

After reading Me's post in the current thread "extern" I see that
6.9.2#2 applies, and extern is not redundant after all.  Excluding it
invokes UB.

> >> void x(void)
> >> {
> >>        struct exts *stmp;
> >>        stmp = &*esp;
> >> }
> >> /** End exts2.c **/
> >>

<snip rest>

0
Reply netocrat (497) 7/20/2005 5:47:13 AM

Netocrat <netocrat@dodo.com.au> wrote:
> On Wed, 20 Jul 2005 00:26:47 +0000, S.Tobias wrote:

>> For example,
>>   extern struct s s;
>>   struct s *sp = &s;
>>   void *vp = &s;
>>   *sp; //UB
>>   *vp; //okay
>> The first expression yields UB,

>> and the second doesn't.  The only reason for that is that the fist is an
>> lvalue, while the second is not.
> 
> Yes, 6.5.3.2 fails to define the behaviour.  vp is a pointer type as
> paragraph 2 requires, but no case in paragraph 4 applies, so *vp is
> undefined by ommission.

I think you got it wrong this time.  `*sp' is explicitly undefined
by 6.3.2.1#2, because it is an incomplete lvalue, and thus cannot be 
converted to a value.  

`*vp' is not an lvalue[*], and therefore it doesn't undergo
lvalue-to-value conversion, and it's behaviour is perfectly defined:
it is an expression statement, which is evalueated for its side
effects, and its (nonexistent) value is discarded.

`*vp' is as much valid an expression as a void function call is:
  void f(), *pv = &some_object;
  f();  //okay
  *pv;  //okay


[*] Well, actually 6.5.3.2#4 says it is an lvalue, but I think
the Standard is again wrong; I think it deserves a DR, but first
I'll check that this isn't already known.

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/20/2005 12:28:08 PM

On Wed, 20 Jul 2005 12:28:08 +0000, S.Tobias wrote:

> Netocrat <netocrat@dodo.com.au> wrote:
>> On Wed, 20 Jul 2005 00:26:47 +0000, S.Tobias wrote:
>
>>> For example,
>>>   extern struct s s;
>>>   struct s *sp = &s;
>>>   void *vp = &s;
>>>   *sp; //UB
>>>   *vp; //okay
>>> The first expression yields UB,
>
>>> and the second doesn't.  The only reason for that is that the fist is an
>>> lvalue, while the second is not.
>>
>> Yes, 6.5.3.2 fails to define the behaviour.  vp is a pointer type as
>> paragraph 2 requires, but no case in paragraph 4 applies, so *vp is
>> undefined by ommission.
>
> I think you got it wrong this time.  `*sp' is explicitly undefined
> by 6.3.2.1#2, because it is an incomplete lvalue, and thus cannot be
> converted to a value.

Yes of course, you are right.

> `*vp' is not an lvalue[*], and therefore it doesn't undergo
> lvalue-to-value conversion, and it's behaviour is perfectly defined:
> it is an expression statement, which is evalueated for its side
> effects, and its (nonexistent) value is discarded.

Perhaps you are right.  I find the standard a little ambiguous here.
The first two sentences of 6.5.3.2#4 don't apply since by 6.2.5, void
is not an object, it's an incomplete type.  That leaves things somewhat
undefined; the third sentence defines the resulting type, but is that
enough definition?

<snip>

> [*] Well, actually 6.5.3.2#4 says it is an lvalue, but I think
> the Standard is again wrong; I think it deserves a DR, but first
> I'll check that this isn't already known.

6.5.3.2#4 only defines the result as an lvalue "if [the operand] points
to an object".  By 6.2.5 void types are never objects.

0
Reply netocrat (497) 7/20/2005 11:38:44 PM

Netocrat <netocrat@dodo.com.au> wrote:
> On Wed, 20 Jul 2005 12:28:08 +0000, S.Tobias wrote:

>> `*vp' is not an lvalue[*], and therefore it doesn't undergo
>> lvalue-to-value conversion, and it's behaviour is perfectly defined:
>> it is an expression statement, which is evalueated for its side
>> effects, and its (nonexistent) value is discarded.
> 
> Perhaps you are right.  I find the standard a little ambiguous here.
> The first two sentences of 6.5.3.2#4 don't apply since by 6.2.5, void
> is not an object, it's an incomplete type.  

[snip]

> 6.5.3.2#4 only defines the result as an lvalue "if [the operand] points
> to an object".  By 6.2.5 void types are never objects.

You still confuse lvalues with objects.  Lvalue is an expression
(it's those characters - or rather tokens - that you type into your
editor: "*" and "vp" together).  Object is an untyped range of bytes
in the run-time, nothing more.  Type of an object is the type of 
the lvalue that it is accessed with.  (There's also an idea of "effective 
type", but I don't want to go into details here.)

Lvalue is not an object.  Lvalue designates an object (points to an
object, as if with a finger), and it is UB if there's no object.

All of these pointers point to the (same) object:
  void *pv = malloc(10);  assert(pv);
  int *pi = pv;
  struct s *ps = pv;
And lvalues designating that object are:
  *pv;
  *pi; //UB if sizeof(int) > 10
  *ps; //UB (because of incomplete type)

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/22/2005 9:59:51 AM

S.Tobias wrote:
> Netocrat <netocrat@dodo.com.au> wrote:
> > On Wed, 20 Jul 2005 12:28:08 +0000, S.Tobias wrote:
>
> >> `*vp' is not an lvalue[*], and therefore it doesn't undergo
> >> lvalue-to-value conversion, and it's behaviour is perfectly defined:
> >> it is an expression statement, which is evalueated for its side
> >> effects, and its (nonexistent) value is discarded.
> >
> > Perhaps you are right.  I find the standard a little ambiguous here.
> > The first two sentences of 6.5.3.2#4 don't apply since by 6.2.5, void
> > is not an object, it's an incomplete type.
>
> [snip]
>
> > 6.5.3.2#4 only defines the result as an lvalue "if [the operand] points
> > to an object".  By 6.2.5 void types are never objects.
>
> You still confuse lvalues with objects.

Not true.  I've been through enough back-and-forth in this thread and
others to at least have _that_ straight.

My argument was actually that since the void type is empty, a void
pointer could be considered as pointing to nothing, no matter what its
value.  That is, a void pointer can never actually point to an object.

0
Reply netocrat (497) 7/22/2005 12:42:06 PM

Netocrat <netocrat@dodo.com.au> wrote:
> S.Tobias wrote:
>> Netocrat <netocrat@dodo.com.au> wrote:

>> You still confuse lvalues with objects.
> 
> Not true.  
[snip]

Okay. :-|  Then there must be something else wrong with you:

>That is, a void pointer can never actually point to an object.

Why do you keep writing things like this?  Void pointer *can*
point to *any* object, that's its main purpose.  It's that 
the object's value cannot be accessed through void type, 
the pointer has to be converted to other type (a different 
pointer), but the whole expression is still based on the original 
void pointer that points to that object, eg:
   *(int*)vp;

If a void pointer doesn't point to an object, then where does
it point to?

-- 
Stan Tobias
mailx `echo siXtY@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
0
Reply siXtY (303) 7/22/2005 5:18:25 PM

S.Tobias wrote:
> Netocrat <netocrat@dodo.com.au> wrote:
> > S.Tobias wrote:
> >> Netocrat <netocrat@dodo.com.au> wrote:
>
> >> You still confuse lvalues with objects.
> >
> > Not true.
> [snip]
>
> Okay. :-|  Then there must be something else wrong with you:

I wonder what it could be?  I understand your arguments, and I don't
require you to agree with mine, but if you give them more
consideration, you will possibly find that they're at least reasonable.

> >That is, a void pointer can never actually point to an object.
>
> Why do you keep writing things like this?

Because the void type is unique.  It is the only type that is both
empty, incomplete and can never be completed.  In that sense how can we
consider a void pointer as pointing to "an object"? - it is clear that
void is the only type that can never represent an object, so a void
pointer can never point to an object.  It may, however, be validly
converted to a pointer type that does.

> Void pointer *can*
> point to *any* object, that's its main purpose.

Informally I would also use a statement like that.  But I don't think
it's strictly true (and the standard's wording convinces me that it's
not).

We rely on 6.3.2.3#1 to be able to convert without information loss
from a non-void pointer to a void pointer and back again, but they are
different pointer types, and nothing in the standard says that when the
information is stored in a void pointer, that we must consider the
conceptual thing that is the "void pointer" to be "pointing to an
object"; the definition of the void type actually makes that wrong in
the strict sense (I agree that informally we might use such wording).

> It's that
> the object's value cannot be accessed through void type,

No value at all (==void,empty) can be accessed through the void type,
let alone an object.

> the pointer has to be converted to other type (a different
> pointer), but the whole expression is still based on the original
> void pointer that points to that object, eg:
>    *(int*)vp;
>
> If a void pointer doesn't point to an object, then where does
> it point to?

How would you instead answer "what does it point to?"

I don't see a strictly correct answer to your question (informally I
would say that it points to the original object as you have been
claiming).  I'd describe a void pointer rather as holding pointing
information in a form that can be transferred back to an object pointer
without loss of meaning.  But I wouldn't describe it as an object
pointer itself.

Now regarding whether dereferencing a void pointer is defined
behaviour, I said:

> I find the standard a little ambiguous here.
> The first two sentences of 6.5.3.2#4 don't apply since by 6.2.5, void
> is not an object, it's an incomplete type.

Can you see where that statement is coming from now?  6.5.3.2#4 says
that "if  [the operand] points to an object, the result is an lvalue
designating the object", but since a void pointer by definition does
not point to an object, this statement doesn't apply to a void pointer.

> That leaves things somewhat
> undefined; the third sentence defines the resulting type, but is that
> enough definition?

So all that's defined is that the result has void type.  OK, given that
there is no such thing as a void object anyway, I'll answer my
question: yes, that's enough definition.  So I'm satisfied that at
least this part of the standard defines and allows for the
dereferencing of a void pointer.

Now you wrote:

> [*] Well, actually 6.5.3.2#4 says it is an lvalue, but I think
> the Standard is again wrong; I think it deserves a DR, but first
> I'll check that this isn't already known.

You have misinterpreted 6.5.3.2#4.  The result is only an lvalue if the
operand points to an object, which strictly speaking a void pointer
doesn't.

The paragraph says: "if it points to an object, the result is an lvalue
designating the object".  Your argument implies that the object type
that the lvalue locates can be different to the type of expression
representing it (in this case, void), which is plainly untrue.

Don't get me wrong - I know that it's sometimes legal to access objects
through an lvalue of type other than the object's originally designated
type (in particular I'm a thinking of a character type), but in those
cases conceptually the lvalue _is_ accessing an object of the same type
as itself (eg. a char can be considered as a special object within a
bigger object of different type), whereas in the case of void it is not
(void is not an object like char is; it is empty and incompletable, so
an lvalue represented by a void expression cannot validly locate any
"object" within a different object).

0
Reply netocrat (497) 7/23/2005 12:23:43 AM

Netocrat wrote:
> 
> S.Tobias wrote:

> > If a void pointer doesn't point to an object, then where does
> > it point to?
> 
> How would you instead answer "what does it point to?"

Allocated memory.

N869
       7.20.3.3  The malloc function
       Synopsis
       [#1]
               #include <stdlib.h>
               void *malloc(size_t size);
       Description
       [#2] The malloc function allocates space for an object whose
       size  is specified by size and whose value is indeterminate.
       Returns
       [#3] The malloc function returns either a null pointer or  a
       pointer to the allocated space.

-- 
pete
0
Reply pfiland (6613) 7/23/2005 1:03:21 AM

pete wrote:
> Netocrat wrote:
> > S.Tobias wrote:
>
> > > If a void pointer doesn't point to an object, then where does
> > > it point to?
> >
> > How would you instead answer "what does it point to?"
>
> Allocated memory.

Spot on and concise as usual.

The distinction between allocated memory and an object is that an
object has a type -  its definition requires it to be capable of
representing values.

So Stan perhaps you would be willing to consider that a void pointer is
best defined as pointing to allocated memory and not an object.

0
Reply netocrat (497) 7/23/2005 1:23:58 AM

Netocrat wrote:
> 
> pete wrote:
> > Netocrat wrote:
> > > S.Tobias wrote:
> >
> > > > If a void pointer doesn't point to an object, then where does
> > > > it point to?
> > >
> > > How would you instead answer "what does it point to?"
> >
> > Allocated memory.
> 
> Spot on and concise as usual.
> 
> The distinction between allocated memory and an object is that an
> object has a type -  its definition requires it to be capable of
> representing values.

Maybe not.

N869
       7.20.3  Memory management functions

       Each such allocation shall yield a pointer to
       an object disjoint  from  any  other  object.

-- 
pete
0
Reply pfiland (6613) 7/23/2005 9:31:41 AM

[I have been meaning to reply to this for quite a while now.]

>On 11 Jul 2005 17:35:54 GMT, Chris Torek 
>   <nospam@torek.net> wrote:
>> [Given "int arr[N];" and considering "&arr" vs "&arr[0]")
>> 
>> I think the real question boils down to whether &arr and &arr[0]
>> will compare equal under *all* "well-defined" conversions -- which
>> may even be only those to "char *" and "void *" -- and then I think
>> the answer is "yes", so that we can in fact say that the converted
>> values are always identical as long as we do a sensible conversion.

In article <slrndd5f3d.kod.chris@ccserver.keris.net>
Chris Croughton  <chris@keristor.net> wrote:
>Is that actually defined by the standard?

At least implicitly, yes (I think).  I have tried to argue the
opposite and always seem to run into a contradiction somewhere.
This is not entirely a satisfactory proof. :-)

>I remember that in some pre-standard C compilers arrays were
>actually implemented as pointers, so int arr[5]; would actually
>expand to the equivalent in pseudo-assember:
>
>  arr:  dw  &_arr
>  _arr: dw  ?[5]
>
>(The array pointer itself might be declared in a read-only segment.)

This was true in BCPL and B, and even in NB, but not in C.  See
<http://cm.bell-labs.com/cm/cs/who/dmr/chist.html> (the section
titled "Embryonic C").  Indeed, the elimination of the pointer
seems to have been the point at which the language's name changed
from "New B" to "C".

>Is this sort of expansion actually banned by the standard ...

Yes.  See Dennis' example with a struct that contains an array.
Where will you put the pointer?
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40�39.22'N, 111�50.29'W)  +1 801 277 2603
email: forget about it   http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
0
Reply nospam252 (1722) 7/23/2005 6:30:57 PM

On Sat, 23 Jul 2005 09:31:41 +0000, pete wrote:

> Netocrat wrote:
>> 
>> pete wrote:
>> > Netocrat wrote:
>> > > S.Tobias wrote:
>> >
>> > > > If a void pointer doesn't point to an object, then where does it
>> > > > point to?
>> > >
>> > > How would you instead answer "what does it point to?"
>> >
>> > Allocated memory.
>> 
>> Spot on and concise as usual.
>> 
>> The distinction between allocated memory and an object is that an
>> object has a type -  its definition requires it to be capable of
>> representing values.
> 
> Maybe not.
> 
> N869
>        7.20.3  Memory management functions
> 
>        Each such allocation shall yield a pointer to an object disjoint 
>        from  any  other  object.

I hadn't looked at library functions.  So what I described as "informal
wording" is actually used in the standard.

Stan said in a higher up post, "Object is an untyped range of bytes in the
run-time, nothing more.  Type of an object is the type of the lvalue that
it is accessed with."

My idea of an object includes type (probably influenced by OO), but it
seems that Stan is correct and that C99 only requires that it "may" be
considered typed.

I don't think it's quite that clear-cut in C in general (the C89 draft
definition of an object, for example, doesn't use the word "type" at all
so there is no "may" qualification, but it specifically mentions
bit-fields, suggesting that type is an implicit property of an object).

So I will accept Stan's argument that by 6.5.3.2#4 dereferencing a void
pointer yields an lvalue which contradicts 6.3.2.1#1.

This problem would be solved if an object were necessarily typed and we
defined a void pointer as pointing to untyped storage, not an object, but
that's off-topic.

0
Reply netocrat (497) 7/23/2005 10:11:30 PM

Netocrat wrote:
> On Sat, 23 Jul 2005 09:31:41 +0000, pete wrote:
> 
> 
>>Netocrat wrote:
>>
>>>pete wrote:
>>>
>>>>Netocrat wrote:
>>>>
>>>>>S.Tobias wrote:
>>>>
>>>>>>If a void pointer doesn't point to an object, then where does it
>>>>>>point to?
>>>>>
>>>>>How would you instead answer "what does it point to?"
>>>>
>>>>Allocated memory.
>>>
>>>Spot on and concise as usual.
>>>
>>>The distinction between allocated memory and an object is that an
>>>object has a type -  its definition requires it to be capable of
>>>representing values.
>>
>>Maybe not.
>>
>>N869
>>       7.20.3  Memory management functions
>>
>>       Each such allocation shall yield a pointer to an object disjoint 
>>       from  any  other  object.
> 
> 
> I hadn't looked at library functions.  So what I described as "informal
> wording" is actually used in the standard.
> 
> Stan said in a higher up post, "Object is an untyped range of bytes in the
> run-time, nothing more.  Type of an object is the type of the lvalue that
> it is accessed with."
> 
> My idea of an object includes type (probably influenced by OO), but it
> seems that Stan is correct and that C99 only requires that it "may" be
> considered typed.
> 
> I don't think it's quite that clear-cut in C in general (the C89 draft
> definition of an object, for example, doesn't use the word "type" at all
> so there is no "may" qualification, but it specifically mentions
> bit-fields, suggesting that type is an implicit property of an object).
> 
> So I will accept Stan's argument that by 6.5.3.2#4 dereferencing a void
> pointer yields an lvalue which contradicts 6.3.2.1#1.
> 
> This problem would be solved if an object were necessarily typed and we
> defined a void pointer as pointing to untyped storage, not an object, but
> that's off-topic.
> 
You seem to insist of making simple things complicated. An object is 
simply a region of one or more bytes of storage. It doesn't require a 
type to be an object. Consider..

typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned short ushort;

typedef struct {
    uchar version;     /* 00 0x03 or 0x83 (with .dbt file) */
    uchar date[3];     /* 01 Date YY MM DD in binary */
    ulong numrecs;     /* 04 Number of records in data file */
    ushort hdrlen;     /* 08 Offset to first record */
    ushort reclen;     /* 0A Length of each record */
    uchar reserved[20];/* 0C Balance of 32 bytes */
} HEADER;

HEADER *head;
void *vp;
vp = malloc(sizeof *head);

Assuming success, malloc returns an address of type (void*) which points 
to 32 bytes of storage. This address is assigned to vp. Now vp points to 
an object of unspecified type of 32 bytes. No part of the object can be 
accessed through vp.

head = vp;

Now head points to the same object as vp. But head has a complete type 
(namely HEADER*) and so the object can be accessed as *head and any of 
its members as (*head).hdrlen or head->hdrlen.

-- 
Joe Wright
"Everything should be made as simple as possible, but not simpler."
                     --- Albert Einstein ---
0
Reply joewwright (1737) 7/23/2005 11:18:30 PM

Netocrat <netocrat@dodo.com.au> writes:
> On Mon, 18 Jul 2005 00:10:22 -0700, Tim Rentsch wrote:
>
>> Netocrat <netocrat@dodo.com.au> writes:
>> 
>>> On Sun, 17 Jul 2005 12:09:39 -0700, Tim Rentsch wrote:
>>> 
> <snip>
>> The problem is that, as written, the standard says almost all
>> expressions are lvalues.  I neglected when giving the original
>> quote to write:
>> 
>>      6.3.2.1
>> 
>>   1  An <i>lvalue</i> is an expression with an object type or an
>>      incomplete type other than void;  ...
>> 
>> The italics makes this statement the (only) definition, per
>> section 3, paragraph 1.
>
> I can't argue against that.  The draft document I use is plain-text
> without italics.
>
>> The C standard document is one that should be interpreted
>> according to a literal reading;  anything less means the authors
>> aren't doing their job.
>
> I have accessed a later draft dated May 6 2005 with the same wording. 
> Can anyone comment on whether any committee members view the definition
> as flawed?

When I raised this issue on comp.std.c some time ago, Doug Gwyn, who
is a committee member, seemed to think that my concerns were
incorrect.  He did say at one point that the standard's definition of
lvalue needed more work, but not for the reasons I had been arguing;
he never explained what he meant.

The fundamental point of disagreement, I think, was about what a
"definition" should be.  My position is that the definition of a
"foo", for example, should be sufficient to determine what is a foo
and what is not a foo.  The definition can depend on terms defined
elsewhere, but once those terms are understood the definition itself
should be complete and unambiguous.  Otherwise, though it may be a
useful statement about foos, it isn't a definition, and should not be
presented as one.  This is especially important for invented terms
that are not expected to be understood

The C99 standard's so-called "definition" of the term "lvalue" is a
useful statement about lvalues, but since it does not allow the reader
to determine whether a given expression is an lvalue or not, I don't
consider it to be a valid definition.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
San Diego Supercomputer Center             <*>  <http://users.sdsc.edu/~kst>
We must do something.  This is something.  Therefore, we must do this.
0
Reply kst-u (21467) 7/23/2005 11:55:42 PM

On Sat, 23 Jul 2005 19:18:30 -0400, Joe Wright wrote:

> Netocrat wrote:

[conceding that the standard defines an object as storage not necessarily
typed, and that wording that I initially called informal is actually used
by the standard (ie that a void pointer points to an object)]
 
>> So I will accept Stan's argument that by 6.5.3.2#4 dereferencing a void
>> pointer yields an lvalue which contradicts 6.3.2.1#1.
>> 
>> This problem would be solved if an object were necessarily typed and we
>> defined a void pointer as pointing to untyped storage, not an object,
>> but that's off-topic.
>> 
> You seem to insist of making simple things complicated.

Right, the current understanding that a void pointer points to potentially
untyped storage is better and more correct than considering its
destination as necessarily untyped.

But I do think that an object is best defined as typed.

Referring to object as not-necessarily-typed storage, the standard says:

6.5#6
  If a value is stored into an object having no declared  type through  an
  lvalue  having  a  type that is not a character type, then the type of
  the lvalue becomes the effective type of  the  object  for that access
  and for subsequent accesses that do not modify the stored value.

So using the standard:
untyped storage: a storage area returned by a memory allocation routine
and not yet assigned to.

In all other cases type has either been declared or determined by being
assigned to.

Untyped storage is the exception rather than the rule, and to me the term
object always connotes a type.  So to me it is simpler to remove the
exceptional and unintuitive circumstance from the definition of object and
deal with it separately.

We could define:
an object: a typed data storage area
allocated storage: any defined or allocated data storage area including
untyped storage (as defined above)

Allocated storage would correspond to the current object definition.

All pointers must then point to allocated storage, which may or may not be
an object, and even if it is an object may not be the correct type.  This
definition change would be reflected in the wording of 6.5.3.2#4, although
it wouldn't correct the current problem that dereferencing a void pointer
results in an lvalue.

I'm starting to get off-topic for clc though.

> An object is
> simply a region of one or more bytes of storage. It doesn't require a
> type to be an object. Consider..

<snip>

Given that I'd already conceded that C99 defines an object that way, your
example didn't really add anything.

0
Reply netocrat (497) 7/24/2005 2:09:13 AM

Keith Thompson wrote:

> The fundamental point of disagreement, I think, was about what a
> "definition" should be.

Do you have IS0 1087 ?




ISOllEC 2382-l : 1993

Other terms such as vocabulary, concept, term and definition, 
are used in this part of ISO/IEC 2382 
with the meaning defined in IS0 1087.


-- 
pete
0
Reply pfiland (6613) 7/24/2005 7:58:25 AM

pete <pfiland@mindspring.com> writes:
> Keith Thompson wrote:
>
>> The fundamental point of disagreement, I think, was about what a
>> "definition" should be.
>
> Do you have IS0 1087 ?
>
>
>
>
> ISOllEC 2382-l : 1993
>
> Other terms such as vocabulary, concept, term and definition, 
> are used in this part of ISO/IEC 2382 
> with the meaning defined in IS0 1087.

No, do you?  Can someone summarize its definition of "definition"?

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
San Diego Supercomputer Center             <*>  <http://users.sdsc.edu/~kst>
We must do something.  This is something.  Therefore, we must do this.
0
Reply kst-u (21467) 7/24/2005 9:25:13 AM

Keith Thompson wrote:
 
> When I raised this issue on comp.std.c some time ago, Doug Gwyn, who
> is a committee member, seemed to think that my concerns were
> incorrect.  He did say at one point that the standard's definition of
> lvalue needed more work, but not for the reasons I had been arguing;
> he never explained what he meant.
> 
> The fundamental point of disagreement, I think, was about what a
> "definition" should be.  My position is that the definition of a
> "foo", for example, should be sufficient to determine what is a foo
> and what is not a foo. 

I remember that.

http://groups-beta.google.com/group/comp.std.c/msg/d40b6ab86fc45c00?hl=en&

-- 
pete
0
Reply pfiland (6613) 7/24/2005 6:37:38 PM

On Mon, 18 Jul 2005 12:44:00 +1000, Netocrat <netocrat@dodo.com.au>
wrote:

> On Mon, 18 Jul 2005 02:21:44 +0000, Dave Thompson wrote:
> 
> > On Mon, 11 Jul 2005 09:22:08 +1000, Netocrat <netocrat@dodo.com.au>
> > wrote:
> > 
> >> On Mon, 11 Jul 2005 00:53:27 +0400, Alexei A. Frounze wrote:
> > <snip>
> >> > Anyway, can you suggest a declaration of the function's argument that
> >> > would simultaneously satisfy:
> >> > - being a pointer to an array of a fixed number of elements, say, 5
> >> > integers - this array is to be constant, in other words, its elements
> >> > can't be altered by using the pointer
> >> 
> >> No.  To the best of my understanding it is not possible according to the
> >> standard.
> >> 
> > Yes. Exactly as the OP did, use a pointer actually to the array as a
> > whole, not to its elements, which is what you normally use in C and
> > what (C) programmers normally mean by "pointer to the/an array".
<snip>
> Yup, that all works fine Dave but what Alexei actually wanted was more
> specific and can't be done - at least no one who's responded to this
> thread so far has found a way.
> 
> He wanted to pass a pointer to an array of a certain number of elements
> (i.e. your first prototype is insufficient, but your second is sufficient)
> into a function and be guaranteed that the contents of the array could not
> be modified within the function.  But he also wanted to be able to pass in
> parameters of a type that were not const-qualified in any way eg.
> int(*)[5], and have them automatically cast to the required type ie const
> int (*)[5].  But this seems to be prohibited by C's automatic
> const-conversion rules.

Sorry, I forgot that part and answered only what he and you said in
these particular posts. Yes, there is no way to silently add const
within an array. Unfortunately.
- David.Thompson1 at worldnet.att.net
0
Reply david.thompson1 (1042) 7/25/2005 5:41:49 AM

> If a void pointer doesn't point to an object, then where does
> it point to?

What is the sound of one hand clapping? :-)

I haven't been following the whole thread, but to my mind saying
"a void pointer doesn't point to an object" is a lot like saying
"a const object can't be modified".  Well, you *can* modify a
const object (or try to), but you'll need a cast, to get rid of
the constness so that the compiler won't complain about it.
Similarly, you can take a void pointer and pretend it points to
an object, and here you don't always even need an explicit cast,
because the compiler will perform the conversion implicitly if
you, for example, assign the void pointer to a variable of object
pointer type.

The analogy is far from perfect, of course, because trying to
modify a const object is usually cheating (else you wouldn't have
declared it const in the first place).  Taking a void pointer,
on the other hand, and "pretending" that it points to an actual
object, is usually not cheating, because usually we're not
pretending -- we *know* that, just beneath the void * veneer,
the pointer actually does point to a real object.

(I suspect everyone understands this just fine, and that the
debate is one of those how-many-angels-can-dance-on-the-head-
of-a-pin ones having to do with excruciatingly close readings of
the Standard, and if so, sorry for restating the merely obvious.)

					Steve Summit
					scs@eskimo.com
0
Reply scs (301) 7/25/2005 1:11:16 PM

173 Replies
55 Views

(page loaded in 1.184 seconds)

Similiar Articles:


















7/24/2012 2:34:09 AM


Reply: