Question about array declarators

F

Francis Moreau

Hello,

I'm trying to understand the 6.7.5 section of C99 which is about
"Declarators" but I'm quite confused and has hard time to understand
what's written.

For example I'm trying to retrieve the type of 'foo':

int foo[4][8];

I know it's basically an array of array of int, but I'd like to
retrieve this by using the spec.

I may need to use 6.7.5.2.p3 but I don't understand what it's written
in it, specially the "derived-declarator-type-list T" thing.

Could anybody help me to clarify this point ?

thanks
 
P

Peter Nilsson

Francis Moreau said:
Hello,

I'm trying to understand the 6.7.5 section of C99 which
is about "Declarators" but I'm quite confused and has
hard time to understand what's written.

That's because C's grammar makes it extremely difficult
to formally define something quite intuitive. It attempts
to explain semantics in terms of a grammar by specifying
a pseudo grammar that defines what you might see in cdecl.
For example I'm trying to retrieve the type of 'foo':

int foo[4][8];

I know it's basically an array of array of int, but I'd
like to retrieve this by using the spec.

I may need to use 6.7.5.2.p3 but I don't understand what
it's written in it, specially the "derived-declarator-
type-list T" thing.

N1336 shows they are still working on it (20 years on)...

Array, function, and pointer types are collectively
called _derived declarator types_. A _declarator type
derivation_ from a type T is the construction of a
derived declarator type from T by the application of
an array-type, a function-type, or a pointer-type
derivation to T.
Could anybody help me to clarify this point ?

Well, Doug Gwyn's responses tend to leave more questions
than answers... ;)

<http://groups.google.com/group/comp.std.c/browse_frm/thread/
8081302dc1210871/6db0f3b48954d786>

The purpose of derived-declarator-type-list, and the
relevant paragraphs where they're used, is to define
a grammar for 'array of', 'pointer of' and 'function
returning' sequences (or lists) to 'base' types.

But you already knew that. ;-)
 
F

Francis Moreau

Hello

Francis Moreau said:
I'm trying to understand the 6.7.5 section of C99 which
is about "Declarators" but I'm quite confused and has
hard time to understand what's written.

That's because C's grammar makes it extremely difficult
to formally define something quite intuitive. It attempts
to explain semantics in terms of a grammar by specifying
a pseudo grammar that defines what you might see in cdecl.
For example I'm trying to retrieve the type of 'foo':
int foo[4][8];
I know it's basically an array of array of int, but I'd
like to retrieve this by using the spec.
I may need to use 6.7.5.2.p3 but I don't understand what
it's written in it, specially the "derived-declarator-
type-list T" thing.

N1336 shows they are still working on it (20 years on)...

  Array, function, and pointer types are collectively
  called _derived declarator types_. A _declarator type
  derivation_ from a type T is the construction of a
  derived declarator type from T by the application of
  an array-type, a function-type, or a pointer-type
  derivation to T.
Could anybody help me to clarify this point ?

Well, Doug Gwyn's responses tend to leave more questions
than answers... ;)

<http://groups.google.com/group/comp.std.c/browse_frm/thread/
8081302dc1210871/6db0f3b48954d786>

The purpose of derived-declarator-type-list, and the
relevant paragraphs where they're used, is to define
a grammar for 'array of', 'pointer of' and 'function
returning' sequences (or lists) to 'base' types.

Thanks for these information.

I'll take some time to read this and most important to understand it
and I'll come back later.
But you already knew that. ;-)

you meant I already guessed that...

thanks
 
J

James Kuyper

Francis said:
Hello,

I'm trying to understand the 6.7.5 section of C99 which is about
"Declarators" but I'm quite confused and has hard time to understand
what's written.

For example I'm trying to retrieve the type of 'foo':

int foo[4][8];

I know it's basically an array of array of int, but I'd like to
retrieve this by using the spec.

I may need to use 6.7.5.2.p3 but I don't understand what it's written
in it, specially the "derived-declarator-type-list T" thing.

What 6.7.5.2p3 says is:
> If, in the declaration ‘‘T D1’’, D1 has one of the forms:
> D[ type-qualifier-listopt assignment-expressionopt ]
> D[ static type-qualifier-listopt assignment-expression ]
> D[ type-qualifier-list static assignment-expression ]
> D[ type-qualifier-listopt * ]
> and the type specified for ident in the declaration ‘‘T D’’ is
> ‘‘derived-declarator-type-list T’’, then the type specified for ident
> is ‘‘derived-declarator-type-list array of T’’

Now, the first thing you have to understand is that the meaning of
"ident" is explained in 6.7.5p4:
> In the following subclauses, consider a declaration
> T D1
> where T contains the declaration specifiers that specify a type T
> (such as int) and D1 is a declarator that contains an identifier
> ident.

The second thing to understand is that derived-declarator-type-list is
just a placeholder, whose meaning is actually defined by the sentence
where it is used. What that sentence means is that
derived-declarator-type-list has whatever meaning it needs to have in
order to make "derived-declarator-type-list T" a correct description of
the type specified for ident by a declaration of the form "T D".

The tricky part is that we have to apply 6.7.5.2p3 twice:

1. T D1 is int foo[4][8]
T is int
D1 is foo[4][8]
D is foo[4]
ident is foo

Now, derived-declarator-type-list has whatever value it needs to have in
order to say that the type declared for "foo" by the declaration "int
foo[4]" is "derived-declarator-type-list int". However, before we can
identify that type, we have to apply 6.7.5.2p3 a second time:

2. T D1 is int foo[4]
T is int
D1 is foo[4]
D is foo
ident is foo

Now, derived-declarator-type-list has whatever value it needs to have in
order to say that the type declared for "foo" by the declaration "int
foo" must match "derived-declarator-type-list int". But the type
declared for "foo" by "int foo" is "int", so for this case

derived-declarator-type-list is ""

Therefore, applying 6.7.5.2p3, we find that the type declared by "int
foo[4]" is "array of int". applying 6.7.5.2p4, we find it is more
specifically "array with length 4 of int", though 6.7.5.2p4 doesn't
specify exactly how the specification of the length should be worded. I
had to experiment with several wordings to come up with one that sounded
vaguely like proper English.

1'. Going back to the first application of 6.7.5.2p3,
"derived-declarator-type-list int" must match "array with length 4 of
int", which means that

derived-declarator-type-list is "array with length 4 of"

Applying 6.7.5.2, we find that the type declared by "int foo[4][8]" is
"array with length 4 of array of int". Applying 6.7.5.2p4, it is more
specifically "array with length 4 of array with length 8 of int".

I hope that explanation clarifies things?
 
F

Franck

Francis said:
I'm trying to understand the 6.7.5 section of C99 which is about
"Declarators" but I'm quite confused and has hard time to understand
what's written.
For example I'm trying to retrieve the type of 'foo':
int foo[4][8];
I know it's basically an array of array of int, but I'd like to
retrieve this by using the spec.
I may need to use 6.7.5.2.p3 but I don't understand what it's written
in it, specially the "derived-declarator-type-list T" thing.

What 6.7.5.2p3 says is:

 > If, in the declaration ‘‘T D1’’, D1 has one of the forms:
 >          D[ type-qualifier-listopt assignment-expressionopt ]
 >          D[ static type-qualifier-listopt assignment-expression ]
 >          D[ type-qualifier-list static assignment-expression ]
 >          D[ type-qualifier-listopt * ]
 > and the type specified for ident in the declaration ‘‘T D’’ is
 > ‘‘derived-declarator-type-list T’’, then the type specified for ident
 > is ‘‘derived-declarator-type-list array of T’’

Now, the first thing you have to understand is that the meaning of
"ident" is explained in 6.7.5p4:

 > In the following subclauses, consider a declaration
 >         T D1
 > where T contains the declaration specifiers that specify a type T
 > (such as int) and D1 is a declarator that contains an identifier
 > ident.

The second thing to understand is that derived-declarator-type-list is
just a placeholder, whose meaning is actually defined by the sentence
where it is used. What that sentence means is that
derived-declarator-type-list has whatever meaning it needs to have in
order to make "derived-declarator-type-list T" a correct description of
the type specified for ident by a declaration of the form "T D".

The tricky part is that we have to apply 6.7.5.2p3 twice:

1.      T D1  is int foo[4][8]
        T     is int
        D1    is foo[4][8]
        D     is foo[4]
        ident is foo

Now, derived-declarator-type-list has whatever value it needs to have in
order to say that the type declared for "foo" by the declaration "int
foo[4]" is "derived-declarator-type-list int". However, before we can
identify that type, we have to apply 6.7.5.2p3 a second time:

2.      T D1  is int foo[4]
        T     is int
        D1    is foo[4]
        D     is foo
        ident is foo

Now, derived-declarator-type-list has whatever value it needs to have in
order to say that the type declared for "foo" by the declaration "int
foo" must match "derived-declarator-type-list int". But the type
declared for "foo" by "int foo" is "int", so for this case

        derived-declarator-type-list is ""

Therefore, applying 6.7.5.2p3, we find that the type declared by "int
foo[4]" is "array of int". applying 6.7.5.2p4, we find it is more
specifically "array with length 4 of int", though 6.7.5.2p4 doesn't
specify exactly how the specification of the length should be worded. I
had to experiment with several wordings to come up with one that sounded
vaguely like proper English.

1'. Going back to the first application of 6.7.5.2p3,
"derived-declarator-type-list int" must match "array with length 4 of
int", which means that

        derived-declarator-type-list is "array with length 4 of"

Applying 6.7.5.2, we find that the type declared by "int foo[4][8]" is
"array with length 4 of array of int". Applying 6.7.5.2p4,  it is more
specifically "array with length 4 of array with length 8 of int".

I hope that explanation clarifies things?

Great explanation, although I needed to read it 4 times before getting
the main picture.

This example should integrated to the spec IMHO. I'm wondering if it
has been written by humans actually...

Ok let me try on "int (*foo)[4]" declarator.

The syntax defined by 6.7.5 tells me to decompose the above
declaration as follow:

D1 is (*foo)
D2 is D1[5]

so to get the type of the whole expression, I first need to get the
type of "T1 D1" then the one of "T1 D2".

T D1 is int (*foo)

which is equivalent (according to 6.7.5.6) to

T D1 is int *foo

Applying 6.7.5.1p1 and with 'derived-declarator-type-list' is "", the
type of "T1 D1" is "pointer to int".

Now let's go back to "T1 D2" type.

T1 D2 is T1 D1[5]

now we know that the type specified by 'T1 D1' is 'pointer to int' so
'derived-declarator-type-list' is 'pointer to' and T is 'int'. So
applying 6.7.5.2p3 the type of "T1 D2" is 'pointer to array of int'.

Looks correct, doesn't it ?

Thanks a lot !
 
F

Francis Moreau

Francis said:
I'm trying to understand the 6.7.5 section of C99 which is about
"Declarators" but I'm quite confused and has hard time to understand
what's written.
For example I'm trying to retrieve the type of 'foo':
int foo[4][8];
I know it's basically an array of array of int, but I'd like to
retrieve this by using the spec.
I may need to use 6.7.5.2.p3 but I don't understand what it's written
in it, specially the "derived-declarator-type-list T" thing.

What 6.7.5.2p3 says is:

 > If, in the declaration ‘‘T D1’’, D1 has one of the forms:
 >          D[ type-qualifier-listopt assignment-expressionopt ]
 >          D[ static type-qualifier-listopt assignment-expression ]
 >          D[ type-qualifier-list static assignment-expression ]
 >          D[ type-qualifier-listopt * ]
 > and the type specified for ident in the declaration ‘‘T D’’ is
 > ‘‘derived-declarator-type-list T’’, then the type specified for ident
 > is ‘‘derived-declarator-type-list array of T’’

Now, the first thing you have to understand is that the meaning of
"ident" is explained in 6.7.5p4:

 > In the following subclauses, consider a declaration
 >         T D1
 > where T contains the declaration specifiers that specify a type T
 > (such as int) and D1 is a declarator that contains an identifier
 > ident.

The second thing to understand is that derived-declarator-type-list is
just a placeholder, whose meaning is actually defined by the sentence
where it is used. What that sentence means is that
derived-declarator-type-list has whatever meaning it needs to have in
order to make "derived-declarator-type-list T" a correct description of
the type specified for ident by a declaration of the form "T D".

The tricky part is that we have to apply 6.7.5.2p3 twice:

1.      T D1  is int foo[4][8]
        T     is int
        D1    is foo[4][8]
        D     is foo[4]
        ident is foo

Now, derived-declarator-type-list has whatever value it needs to have in
order to say that the type declared for "foo" by the declaration "int
foo[4]" is "derived-declarator-type-list int". However, before we can
identify that type, we have to apply 6.7.5.2p3 a second time:

2.      T D1  is int foo[4]
        T     is int
        D1    is foo[4]
        D     is foo
        ident is foo

Now, derived-declarator-type-list has whatever value it needs to have in
order to say that the type declared for "foo" by the declaration "int
foo" must match "derived-declarator-type-list int". But the type
declared for "foo" by "int foo" is "int", so for this case

        derived-declarator-type-list is ""

Therefore, applying 6.7.5.2p3, we find that the type declared by "int
foo[4]" is "array of int". applying 6.7.5.2p4, we find it is more
specifically "array with length 4 of int", though 6.7.5.2p4 doesn't
specify exactly how the specification of the length should be worded. I
had to experiment with several wordings to come up with one that sounded
vaguely like proper English.

1'. Going back to the first application of 6.7.5.2p3,
"derived-declarator-type-list int" must match "array with length 4 of
int", which means that

        derived-declarator-type-list is "array with length 4 of"

Applying 6.7.5.2, we find that the type declared by "int foo[4][8]" is
"array with length 4 of array of int". Applying 6.7.5.2p4,  it is more
specifically "array with length 4 of array with length 8 of int".

I hope that explanation clarifies things?

Thanks a lot, and yes it clarifies the whole thing.

This 'derived-declarator-type-list' was the obscure part since it
introduces some notions like "list" or "derivated" which makes it
difficult to read or understand, at least for me a no native english
speaker.

Thanks again.
 
F

Francis Moreau

I hope that explanation clarifies things?

I thought it does but now I'm confused again...

I try to apply the rules on the following example but fails:

const int *foo[4];

I found that the type of this declarator was "array of pointer to
const int".

To check that I compiled the following piece of code by calling gcc
like this "gcc -Wall -std=c99 main.c":

const int *foo[4];

int main(void)
{
int i = 4;
foo[0][0] = &i;
return 0;
}

and I expected an error from gcc since i doesn't have a compatible
type with "const int". But gcc produced nothing.

Also I tried to find the type of "int * const foo[4]". I found it to
be "array of const pointer to int" but I still tried to check this by
using gcc but I failed. Is the type I found correct ?

thanks.
 
J

James Kuyper

Francis Moreau wrote:
....
I try to apply the rules on the following example but fails:

const int *foo[4];

I found that the type of this declarator was "array of pointer to
const int".

To check that I compiled the following piece of code by calling gcc
like this "gcc -Wall -std=c99 main.c":

const int *foo[4];

int main(void)
{
int i = 4;
foo[0][0] = &i;

foo[0] has a null pointer value. Therefore, the expression foo[0][0] has
undefined behavior. I think that what you actually want here is:

foo[0] = &i;
return 0;
}

and I expected an error from gcc since i doesn't have a compatible
type with "const int". But gcc produced nothing.

Are you sure you've given the code exactly as you passed it to gcc?
Using gcc 4.1.2, with the code above and the specified command line
options, I get:

moreau.c: In function ‘main’:
moreau.c:6: error: assignment of read-only location

This indicates that your description of the type of foo is correct:
foo[0][0] is const, so foo[0][0] is not a modifiable lvalue (6.3.2.1p1),
and it is therefore a constraint violation to use it as the left operand
of an assignment operator (6.5.16p2).

Your comments suggest that what you intended to do is assign &i to
something which it could, in fact, be assigned to, if the 'const' were
removed from the declaration of foo; that would be foo[0]. Your comments
also suggest that you think the 'const' should disallow such an
assignment. This is not the case.

The relevant constraint on the assignment expression is "both operands
are pointers to qualified or unqualified versions of compatible types,
and the type pointed to by the left has all the qualifiers of the type
pointed to by the right;". The compatible types here would be 'int' for
for both foo[0] and &i. The qualifiers on the type pointed at by foo[0]
are "const", the qualifiers on the type pointed at by &i are "". Since
&i has no qualifiers, it is trivial for foo[0] to meet this requirement,
so, the assignment is not a constraint violation. Here's an example of
code that would violate that constraint is:

int *bar[4];
const int j = 7;

bar[0] = &j;

In this case, the type pointed at by &j has a qualifier, "const" that is
not possessed by the type pointed at by bar[0].
Also I tried to find the type of "int * const foo[4]". I found it to
be "array of const pointer to int" but I still tried to check this by
using gcc but I failed. Is the type I found correct ?

The types you derived are correct. Your methods of testing their
correctness are apparently flawed. Here's a suitable test for int *const
foo[4]:

int main(void)
{
int i, j;
int * const foo[4] = {&i, NULL, &i, NULL};

foo[0][0] = 3; /* no problem */
foo[0] = &j; /* constraint violation: 6.5.16p2 */

return 0;
}
 
F

Franck

Francis Moreau wrote:

...


I try to apply the rules on the following example but fails:
const int *foo[4];
I found that the type of this declarator was "array of pointer to
const int".
To check that I compiled the following piece of code by calling gcc
like this "gcc -Wall -std=c99 main.c":
const int *foo[4];
int main(void)
{
    int i = 4;
    foo[0][0] = &i;

foo[0] has a null pointer value. Therefore, the expression foo[0][0] has
undefined behavior. I think that what you actually want here is:

        foo[0] = &i;
    return 0;
}
and I expected an error from gcc since i doesn't have a compatible
type with "const int". But gcc produced nothing.

Are you sure you've given the code exactly as you passed it to gcc?

Sorry my bad, I tried several things and paste the wrong line....

What I exactly compiled is:

const int *foo[4];

int main(int argc, char **argv)
{
int i = 0;

foo[0] = &i;
return 0;
}

and gcc version 4.3.0 produced no error (compiled using the command
line "gcc -Wall -std=c99 main.c")
Using gcc 4.1.2, with the code above and the specified command line
options, I get:

        moreau.c: In function ‘main’:
        moreau.c:6: error: assignment of read-only location

hmm, I'm not sure we compiled the same program...
This indicates that your description of the type of foo is correct:
foo[0][0] is const, so foo[0][0] is not a modifiable lvalue (6.3.2.1p1),
and it is therefore a constraint violation to use it as the left operand
of an assignment operator (6.5.16p2).
ok


Your comments suggest that what you intended to do is assign &i to
something which it could, in fact, be assigned to, if the 'const' were
removed from the declaration of foo; that would be foo[0]. Your comments
also suggest that you think the 'const' should disallow such an
assignment. This is not the case.

not really, what I thought is that in the declarator "const int *foo
[4];", type of foo is "array of pointer to const int". So assigning foo
[0] is correct only if the rvalue is "pointer to const int". However
doing "foo[0] = &i" in the previous example seems to be not correct
because the type of "&i" is "pointer to int", not "pointer to const
int"

thanks
 
J

James Kuyper

Franck said:
Francis Moreau wrote: ....
To check that I compiled the following piece of code by calling gcc
like this "gcc -Wall -std=c99 main.c":
const int *foo[4];
int main(void)
{
int i = 4;
foo[0][0] = &i;
foo[0] has a null pointer value. Therefore, the expression foo[0][0] has
undefined behavior. I think that what you actually want here is:

foo[0] = &i;
return 0;
}
and I expected an error from gcc since i doesn't have a compatible
type with "const int". But gcc produced nothing.
Are you sure you've given the code exactly as you passed it to gcc?

Sorry my bad, I tried several things and paste the wrong line....

What I exactly compiled is:

const int *foo[4];

int main(int argc, char **argv)
{
int i = 0;

foo[0] = &i;
return 0;
}

and gcc version 4.3.0 produced no error (compiled using the command
line "gcc -Wall -std=c99 main.c")

Correct; there's nothing erroneous about the code above, despite the
fact that foo[0] points to a const int, and &i does not.
hmm, I'm not sure we compiled the same program...

From what you've said above, we most certainly did not. Those messages
are from compilation of the version of the code that you originally posted.
Your comments suggest that what you intended to do is assign &i to
something which it could, in fact, be assigned to, if the 'const' were
removed from the declaration of foo; that would be foo[0]. Your comments
also suggest that you think the 'const' should disallow such an
assignment. This is not the case.

not really, what I thought is that in the declarator "const int *foo
[4];", type of foo is "array of pointer to const int". So assigning foo
[0] is correct only if the rvalue is "pointer to const int".

That sounds like exactly the misconception that I described. You think
that the 'const' in foo dissallows assignment if the rvalue points to a
type that isn't const-qualified. That's not the way assignment works.

Assignment to a pointer only requires that the type pointed at by the
left operand of the assignment operator have all of the qualifiers that
the type pointed at by the right operand has. It does NOT require that
the opposite be true. To put it another way; assignment can add
qualifiers to the pointed-at type, but it can never remove them.

... However
doing "foo[0] = &i" in the previous example seems to be not correct
because the type of "&i" is "pointer to int", not "pointer to const
int"

The standard imposes no such requirement.
 
F

Francis Moreau

That sounds like exactly the misconception that I described. You think
that the 'const' in foo dissallows assignment if the rvalue points to a
type that isn't const-qualified. That's not the way assignment works.

Assignment to a pointer only requires that the type pointed at by the
left operand of the assignment operator have all of the qualifiers that
the type pointed at by the right operand has. It does NOT require that
the opposite be true. To put it another way; assignment can add
qualifiers to the pointed-at type, but it can never remove them.

Ah ok, I need to read what you wrote more carefully and study others
part of the spec as well.

I'll be back.

Thanks for your enlightment !
 
F

Francis Moreau

Ah ok, I need to read what you wrote more carefully and study others
part of the spec as well.

And now I read it more carerefully, what you wrote is obviously right.
I was wrong about the const qualifier in the assignment expression.

At least I found correctly the declarator types that's not too bad ;)

Thanks a lot !
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top