'Declaration reflects use'

B

BartC

I've heard of 'declaration reflects use' many times, in connection with
helping decode complex type declarations, although it's never really worked
for me. Until I played with the following code. The declaration and use of
the indirect 'c' array match perfectly!

#include <stdio.h>
#include <stdlib.h>

#define N 10

int main(void) {
int a[N];
int *b;
int (*c)[];
int i;

b=malloc(N*sizeof(int));
c=malloc(N*sizeof(int));

for (i=0; i<N; ++i) {
a=i*20;
b=i*30;
(*c)=i*40;
}

for (i=0; i<N; ++i) {
printf("%d: %4d %4d %4d\n",i,a,b,(*c));
}
}

'a' declares a normal int array of a size known at compile time.

'b' declares a pointer to an int array to be set up at runtime, but in the
conventional way used in C, by declaring instead a pointer to an int, the
element type.

'c' does it a little differently, by declaring a pointer to an actual int
array.

'Declaration reflects use' describes c aptly, but seems to have no bearing
on the declaration and use of b!

(I can understand why 'b' is preferred, the indexing is much simpler and
more readable, and ties in with pointers and arrays being interchangeable.
But I'm generating C source from a language where the 'c' way makes far more
sense type-wise, plus I don't have look at the C code very often.)

(BTW, on my machine, I use a magic version of malloc() that will never fail
for these tiny test programs.)
 
B

Barry Schwarz

I've heard of 'declaration reflects use' many times, in connection with
helping decode complex type declarations, although it's never really worked
for me. Until I played with the following code. The declaration and use of
the indirect 'c' array match perfectly!

#include <stdio.h>
#include <stdlib.h>

#define N 10

int main(void) {
int a[N];
int *b;
int (*c)[];

I'm pretty sure you need [N] here.
int i;

b=malloc(N*sizeof(int));
c=malloc(N*sizeof(int));

for (i=0; i<N; ++i) {
a=i*20;
b=i*30;
(*c)=i*40;
}

for (i=0; i<N; ++i) {
printf("%d: %4d %4d %4d\n",i,a,b,(*c));
}
}

'a' declares a normal int array of a size known at compile time.

'b' declares a pointer to an int array to be set up at runtime, but in the
conventional way used in C, by declaring instead a pointer to an int, the
element type.


And setting it to point to the first element of the array.
'c' does it a little differently, by declaring a pointer to an actual int
array.

'Declaration reflects use' describes c aptly, but seems to have no bearing
on the declaration and use of b!

(I can understand why 'b' is preferred, the indexing is much simpler and
more readable, and ties in with pointers and arrays being interchangeable.

Which of course they are not. You can code
b = a;
but you cannot code
a = b;
But I'm generating C source from a language where the 'c' way makes far more
sense type-wise, plus I don't have look at the C code very often.)

In your sample, N was known at compile time and you could have coded
c = &a;
But in those situations where N is determined at run time, your only
option is b (unless your system supports variable length array, which
is optional).
 
B

BartC

Barry Schwarz said:
#define N 10

int main(void) {
int a[N];
int *b;
int (*c)[];

I'm pretty sure you need [N] here.

I've just checked with CDECL and is says that c is: 'pointer to array of
int' (and not array of something which /would/ have needed a dimension). You
had me worried for a minute...

(I suppose it's possible to have had 'pointer to array N of int', but for
these dynamic arrays that's not useful.)
Which of course they are not. You can code
b = a;
but you cannot code
a = b;

I meant that you can do b[0] and b as well as *b and *(b+i). (And the
same with 'a' I guess.)
In your sample, N was known at compile time and you could have coded
c = &a;
But in those situations where N is determined at run time, your only
option is b (unless your system supports variable length array, which
is optional).

No, I don't agree (I can't otherwise my scheme won't work!). But I checked
with a few more compilers, and it seems I can declare a pointer to an array
of indeterminate size; allocate enough space for whatever size I like; and
access the elements as I've shown.
 
B

Ben Bacarisse

BartC said:
Barry Schwarz said:
#define N 10

int main(void) {
int a[N];
int *b;
int (*c)[];

I'm pretty sure you need [N] here.

I've just checked with CDECL and is says that c is: 'pointer to array of
int' (and not array of something which /would/ have needed a dimension). You
had me worried for a minute...

(I suppose it's possible to have had 'pointer to array N of int', but for
these dynamic arrays that's not useful.)

If you do know the size (and in this case you do), it is helpful to
include it. Given

int (*c)[N];

the array can be allocated using only c = malloc(sizeof *c); but it's
more common to use a pointer to an array when the final array will be a
2D one -- the pointer pointing to the first row. The allocation can then
proceed using the standard idiom:

c = malloc(rows * sizeof *c);

For 2D arrays, c, with a size, is more readable, at least when
allocating and indexing.

No, I don't agree (I can't otherwise my scheme won't work!). But I checked
with a few more compilers, and it seems I can declare a pointer to an array
of indeterminate size; allocate enough space for whatever size I like; and
access the elements as I've shown.

It's not unreasonable that people here will comment on the C code, as is
if were being written by a person. The example you gave had a constant
size, and in those cases it pays to put it in the declaration. To
forestall such comments, it's better to post an example closer to the
original. Presumably where (a) you don't know the size at compile time,
and (b) you don't want to assume the C system supports variably modified
types.
 
K

Kaz Kylheku

I've heard of 'declaration reflects use' many times, in connection with
helping decode complex type declarations, although it's never really worked
for me. Until I played with the following code. The declaration and use of
the indirect 'c' array match perfectly!

"Declaration follows use" simply means that the type constructor operators *,
and [] used in declarators have a similar associativity and precedence as the
unary * and postfix [] and () in expressions.

If we can use x as (*x[4])(42) and obtain an int by doing that, then we can
declare it as int (*x[SIZE])(int). In the case of postfix (), arguments
change to param declarations, and the array indexing value to the actual size.

But otherwise the declaration looks like we are indexing x (so it must be an
array), then dereferncing the result (so it must be an array of pointers), and
then calling it (so they are pointers to function).

Implicit conversion and other flexibilites in expressions break the
principle. For instance, if F is af unction pointer, we don't have
to call it using (*F)(), but can just use F(). The declaration can
no longer follow it. If P is a pointer and used as P[], the
declaration does not follow.

However, in these two cases, declaration can still follow use if it
is a parameter declaration:

void foo(void func(int)) /* pointer param */
{
func(42); /* use similar to declarator */
}

int foo(char arr[], int x) /* pointer param */
{
return arr[x];
}

Structures and unions are the true departure from the principle.
The use of a structure member doesn't resemble anything in the
declartion of a structure, in which . and -> operators do not appear.
 
B

BartC

Implicit conversion and other flexibilites in expressions break the
principle. For instance, if F is af unction pointer, we don't have
to call it using (*F)(), but can just use F(). The declaration can
no longer follow it. If P is a pointer and used as P[], the
declaration does not follow.

However, in these two cases, declaration can still follow use if it
is a parameter declaration:

void foo(void func(int)) /* pointer param */
{
func(42); /* use similar to declarator */
}

int foo(char arr[], int x) /* pointer param */
{
return arr[x];
}

(That reminds me that I haven't tested generating parameter types yet, which
I remember are a little off-kilter, to prevent anyone declaring an array
type that might be interpreted as a value type.

So your char arr[] declaration means a char* pointer in a parameter list,
but an ordinary char[] array anywhere else. (Which gets confusing when you
think about it too much. Couldn't they just have used exactly the same type
specifiers everywhere, but disallowed anything that looked like a value
array type in a parameter list?))
 
B

Barry Schwarz

Barry Schwarz said:
#define N 10

int main(void) {
int a[N];
int *b;
int (*c)[];

I'm pretty sure you need [N] here.

Obviously I was wrong.
I've just checked with CDECL and is says that c is: 'pointer to array of
int' (and not array of something which /would/ have needed a dimension). You
had me worried for a minute...

Consider the following

int a1[2][5] = {10,11,12,13,14,15,17,17,18,19};
int a2[2][10] = {100,101,102,103,104, 105,106,107,108,109,110,
111,112,113,114, 115,116,117,118,119};
int a3[5] = {500,501,502,503,504};
int (*p1)[5]
int (*p2)[10];
int (*p3)[];

p1 = a1;
printf("%d %d\n", p1[0][2], p1[1][4]); /*12 19*/
p2 = a2;
printf("%d %d\n", p2[0][7], p2[1][8]); /*107 118*/
p2 = a1; <============== error - different array subscripts
p1 = a2; ditto
p3 = a1; ditto
printf("%d %d\n", p3[0][2], p3[1][4]); <=== error - unknown size
p3 = a3; <======= error - different levels of indirection
p3 = &a3; <======= error - different array subscripts
printf("%d %d\n", p3[2], p3[4]); <=== error - unknown size
p3 = (void*)a3; <===effectively the same as malloc
printf("%d %d\n", (*p3)[2],(* p3)[4]);

This last one is a bit mind boggling. By definition, *p3 is identical
to *(p3) is identical to *(p3+0) is identical to p3[0]. Why is
(*p3)[2] valid while p3[0][2] is not? Since p3 is a pointer to an
incomplete type, I' wondering if maybe the problem is that +0. While
we know it doesn't change the value of the expression, the compiler
probably thinks it is doing pointer arithmetic which is not legal for
incomplete types.

In any event, it appears that your pointer to array of unspecified
number of int is usable only for an object treated as a 1D array.
(I suppose it's possible to have had 'pointer to array N of int', but for
these dynamic arrays that's not useful.)
Which of course they are not. You can code
b = a;
but you cannot code
a = b;

I meant that you can do b[0] and b as well as *b and *(b+i). (And the
same with 'a' I guess.)
In your sample, N was known at compile time and you could have coded
c = &a;
But in those situations where N is determined at run time, your only
option is b (unless your system supports variable length array, which
is optional).

No, I don't agree (I can't otherwise my scheme won't work!). But I checked
with a few more compilers, and it seems I can declare a pointer to an array
of indeterminate size; allocate enough space for whatever size I like; and
access the elements as I've shown.


Yup. My bad. Have a Dos Equis on me.
 
B

BartC

Consider the following

int a1[2][5] = {10,11,12,13,14,15,17,17,18,19};
int a2[2][10] = {100,101,102,103,104, 105,106,107,108,109,110,
111,112,113,114, 115,116,117,118,119};
int a3[5] = {500,501,502,503,504};
int (*p1)[5]
int (*p2)[10];
int (*p3)[];

p1 = a1;
printf("%d %d\n", p1[0][2], p1[1][4]); /*12 19*/
p2 = a2;
printf("%d %d\n", p2[0][7], p2[1][8]); /*107 118*/
p2 = a1; <============== error - different array subscripts
p1 = a2; ditto
p3 = a1; ditto
printf("%d %d\n", p3[0][2], p3[1][4]); <=== error - unknown size
p3 = a3; <======= error - different levels of indirection
p3 = &a3; <======= error - different array subscripts
printf("%d %d\n", p3[2], p3[4]); <=== error - unknown size
p3 = (void*)a3; <===effectively the same as malloc
printf("%d %d\n", (*p3)[2],(* p3)[4]);

This last one is a bit mind boggling. By definition, *p3 is identical
to *(p3) is identical to *(p3+0) is identical to p3[0]. Why is
(*p3)[2] valid while p3[0][2] is not?
In any event, it appears that your pointer to array of unspecified
number of int is usable only for an object treated as a 1D array.

Dealing with multi-dimensional arrays in C usually gives me a headache (and
I avoid them where possible), but here goes.

With a 2D array, that can either be implemented as a single block of memory,
or as a 1D array of pointer to a separate set of 1D arrays. I've tried
various combinations in the code below.

'a' is the 'reference' case of an array allocated by the compiler

'b' and 'f' are the traditional C way of doing things (once set up, indexing
is just as simple as for 'a')

'c', 'd' and 'e' are what I want to use (because they fit in more neatly
with my own ideas of how array types should work).

'a', 'b', 'c', 'd' have the 2D array as a single block of memory; 'e' and
'f' use an array of pointers to individual blocks for each row.

Fortunately they all seem to work. (I haven't tried 3D and 4D arrays...)

And again, 'declaration follows use' (or whatever the saying is) uncannily
matches the declarations and use of my 'c', 'd' and 'e' arrays even more
closely, while the 'b' and 'f' cases are totally different!

#define N 3 /* For test purposes; usually known at runtime */
#define M 5

int main(void) {
int a[N][M]; /* flat value array */
int *b; /* pointer to int to be used for 2D array */
int (*c)[][M]; /* pointer to 2D array; 2nd dim known at compile
time*/
int (*d)[N][M]; /* pointer to 2d array; both dims known */
int (*(*e)[])[]; /* pointer to row pointers to 1D array */
int **f; /* pointer to pointer to int; version of 'e' */

int i,j;

b=malloc(N*M*sizeof(int));
c=malloc(N*M*sizeof(int));
d=malloc(N*M*sizeof(int));
e=malloc(N*sizeof(int*));
f=malloc(N*sizeof(int*));

for (i=0; i<N; ++i) {
(*e)=malloc(M*sizeof(int));
f=malloc(M*sizeof(int));
}

for (i=0; i<N; ++i) {
for (j=0; j<M; ++j) {
a[j]=i*20+j;
b[i*M+j]=i*30+j;
(*c)[j]=i*40+j;
(*d)[j]=i*50+j;
(*(*e))[j]=i*60+j;
f[j]=i*70+j;
}
}

for (i=0; i<N; ++i) {
for (j=0; j<M; ++j) {
printf("%d,%d: %4d %4d %4d %4d %4d
%4d\n",i,j,a[j],b[i*M+j],(*c)[j],(*d)[j],
(*(*e))[j], f[j]);
}
}
}
 

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,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top