[comp.lang.c] Point me in the right direction

vlcek@mit-caf.MIT.EDU (Jim Vlcek) (02/17/89)

rbutterworth@watmath.waterloo.edu (Ray Butterworth) writes:

>Another handy style guideline to follow is trying to avoid writing
>empty [] declarations.  If the [] is empty, then you are talking
>about an array of unknown size and in the event that you really do
>want the array there is no way that the compiler will know how big
>it is.  If you don't know how big an array is, then perhaps you
>shouldn't be talking about arrays but about pointers.

No one caught this?  A compiler must be able to determine for itself
the size of one of the dimensions of an array (I believe the last
dimension in the case of a superlinear array, no?), if that array is
to be initialized at compile time, provided that unambiguous
initializers are provided.   In fact, for an array which is fully
initialized at compile time, an empty [] declaration is preferable:

double fund_consts[] = {
	3.14159265,
	2.7182818,
	6.02E23,
};

Should I want to add Euler's constant later, I merely splice it in at
the end -- I don't have to change the dimension parameter.  More
important, there's no danger of forgetting to change that parameter.

Also, I frequently work with static structures which are initialized at
compile time, and which I pass around via pointers.  Since

struct foo *bar = { /* Some aggregate initialization */ };

is illegal, I use

struct foo bar[] = { { /* Some aggregate initialization */ } };

which is legal and works so long as all of the elements of the
struct can be initialized (unfortunately, unions cannot).  This
construct achieves in one declaration what I need, although it does
somewhat abuse the concept of an array.  I'd be interested in hearing
comments on this technique.

>In particular never declare a function parameter as an array.
>e.g. use "char **argv;", not "char *argv[];".  Since the compiler
>will silently convert the second parameter declaration into the
>first for you, you might as well declare it that way in the first
>place and avoid confusing yourself and others.

I wouldn't say this.  I like the use of ``foo *bar[]'' to make it
clear that ``bar'' does indeed represent an array of pointers, through
which one might traverse.  Typically, however, I would use the first
form for the actual pointer used for the traversal:

some_fun(arglp) char *arglp[]; {
	char **next_arg = arglp;

	while (*next_arg++) {
	/* Party 'til you drop */
	}
}

Presuming, of course, that you want to preserve the head of the list
arglp, otherwise you could increment it directly.  Conceptually, the
above method is more clear in that next_arg is intended to refer to a
pointer to a single item, hence the use of char **.
-- 
Jim Vlcek
vlcek@caf.mit.edu
!{harvard,rutgers}!mit-eddie!mit-caf!vlcek

hascall@atanasoff.cs.iastate.edu (John Hascall) (02/17/89)

In article <1841@mit-caf.MIT.EDU> vlcek@mit-caf.UUCP (Jim Vlcek) writes:
>rbutterworth@watmath.waterloo.edu (Ray Butterworth) writes:
>
>>Another handy style guideline to follow is trying to avoid writing
>>empty [] declarations.

>                         ... In fact, for an array which is fully
>initialized at compile time, an empty [] declaration is preferable:

>double fund_consts[] = {
>	3.14159265,
>	2.7182818,
>	6.02E23,
>};

   Of course, adding:

     #define N_FCONSTS (sizeof(fund_consts)/sizeof(double)) 

   then you can use stuff like:

     extern fund_consts[N_FCONSTS];

   elsewhere.



John Hascall
ISU Comp Center

rbutterworth@watmath.waterloo.edu (Ray Butterworth) (02/17/89)

In article <1841@mit-caf.MIT.EDU>, vlcek@mit-caf.MIT.EDU (Jim Vlcek) writes:
> rbutterworth@watmath.waterloo.edu (Ray Butterworth) writes:
> >Another handy style guideline to follow is trying to avoid writing
> >empty [] declarations.  If the [] is empty, then you are talking
> >about an array of unknown size and in the event that you really do
> >want the array there is no way that the compiler will know how big
> >it is.  If you don't know how big an array is, then perhaps you
> >shouldn't be talking about arrays but about pointers.
> 
> No one caught this?  A compiler must be able to determine for itself
> the size of one of the dimensions of an array (I believe the last
> dimension in the case of a superlinear array, no?), if that array is
> to be initialized at compile time, provided that unambiguous
> initializers are provided.   In fact, for an array which is fully
> initialized at compile time, an empty [] declaration is preferable:
> double fund_consts[] = {
>     3.14159265,
>     2.7182818,
>     6.02E23,
> };

I guess I didn't make my context clear.
I didn't mean in cases where the array is actually being defined.
Your examples are perfectly legitimate, and unless one really wants
to explicitly indicate how many elements there should be,
it probably is best to use the empty brackets.

What I was talking about was mostly intended to refer to
parameter declarations.  e.g.
    void func(list) int list[]; { list[0] = 42; }
In this case the compiler does not need to know the dimension of the list.

On the other hand, I'd be concerned about how an external array is used.  e.g.
    extern int vec[];
    for (i=0; vec[i] != END_LIST; ++i) ...
In this case, the list has a special value flagging its end,
so this use is probably ok.
    extern int list[];
    for (i=0; i<LIST_SIZE; ++i) ...
In the second case, the code "knows" how big the list is,
even though the list is defined in a different source file
and someone may have added or deleted a member of that list.
This is not ok.  It would be much better to have
    extern int list[LIST_SIZE];
    for (i=0; i<LIST_SIZE; ++i) ...
or even
    for (i=0; i<sizeof(list)/sizeof(list[0]); ++i) ...
Then lint would notice and complain if the "extern int list[LIST_SIZE]"
referenced something with a different size than the actual list
(which should be defined with "list[]={...}" NOT with "list[LIST_SIZE]=").

> >e.g. use "char **argv;", not "char *argv[];".  Since the compiler
> >will silently convert the second parameter declaration into the
> >first for you, you might as well declare it that way in the first
> >place and avoid confusing yourself and others.
> 
> I wouldn't say this.  I like the use of ``foo *bar[]'' to make it
> clear that ``bar'' does indeed represent an array of pointers, through
> which one might traverse.

So long as you realize that in
    void func(list) int list[]; {extern int vec[]; ...}
the identifiers list and vec have very different types.
vec is an array and list is a pointer.

Many people find this feature to be a cause for confusion.
Here's an example I used a while back to help a local user that
couldn't understand why things were behaving the way they were:

%cat sizeof.c

#include <stdio.h>

test(f, d, s, c, a)
    float f;
    double d;
    short s;
    char c;
    int a[10];
{
    auto float af;
    auto double ad;
    auto short as;
    auto char ac;
    auto int aa[10];

    fprintf(stdout,
        "sizeof autos:  float=%d  double=%d  short=%d  char=%d  int[10]=%d\n",
        (int)sizeof(af), (int)sizeof(ad), (int)sizeof(as),
        (int)sizeof(ac), (int)sizeof(aa));
    fprintf(stdout,
        "sizeof args:   float=%d  double=%d  short=%d  char=%d  int[10]=%d\n",
        (int)sizeof(f), (int)sizeof(d), (int)sizeof(s),
        (int)sizeof(c), (int)sizeof(a));
}

int array[10];

main()
{
    test(0., 0., 0, 0, array);
    return 0;
}

% lint sizeof.c
sizeof.c:
sizeof.c(4): warning: float type changed to double
sizeof.c(8): warning: array[10] type changed to pointer
"fprintf" result is always ignored.

% cc sizeof.c
% ./a.out
sizeof autos:  float=4  double=8  short=2  char=1  int[10]=40
sizeof args:   float=8  double=8  short=2  char=1  int[10]=4

                     ^                                     ^
                     *                                     *

Note that (just as lint said it would be), the parameter declarations
    float f;
    int a[10];
were silently converted by the all too helpful compiler into
    double f;
    int *a;
since the first declarations were types that are not legal as
function arguments.
Since they are impossible as they stand, the compiler tries to
help you by changing them to what you obviously meant.

If you find that confuses you more than it helps you,
you are not alone.

But for good or bad, this is the way the C language is defined to work.

On the other hand, the BSD compiler did not promote the types of
the short and char parameters.  That is probably a bug.

guy@auspex.UUCP (Guy Harris) (02/18/89)

 >>double fund_consts[] = {
 >>	3.14159265,
 >>	2.7182818,
 >>	6.02E23,
 >>};
 >
 >   Of course, adding:
 >
 >     #define N_FCONSTS (sizeof(fund_consts)/sizeof(double)) 

So far, so good - this is an old C trick, which is quite useful, but may
not be known as well as it should be - but...

 >   then you can use stuff like:
 >
 >     extern fund_consts[N_FCONSTS];

(try "extern double fund_consts[...]"; that declaration makes it an
array of "int")

 >   elsewhere.

...only if "fund_consts" is *defined* in the same "elsewhere", or at
least declared with a size other than N_FCONSTS (since the value of the
expression "(sizeof(fund_consts)/sizeof(double))" depends on the size
declared for "fund_consts"), which seems unlikely - if it's defined
there, why declare it all over again later in the same module?

evil@arcturus.UUCP (Wade Guthrie) (02/22/89)

In article <1043@auspex.UUCP>, guy@auspex.UUCP (Guy Harris) writes:
[someone started with:]

> double fund_consts[] = {
> 	3.14159265,
> 	2.7182818,
> 	6.02E23,
> };

[and someone else added:]

> Of course, adding:
> 	#define N_FCONSTS (sizeof(fund_consts)/sizeof(double)) 

[and Guy said:] 

> So far, so good - this is an old C trick, which is quite useful, but may
> not be known as well as it should be - but...

[and I say:]

This is all well and good, but what about arrays of structures?  Am I
S.O.L. because of inter-array-element padding if I:

	struct already_defined snarf[] = {...};

and then try to get the size by doing:

	#define N_WHATEVER (sizeof(snarf)/sizeof(struct already_defined))

or can I (portably)

	int size;
	. . .
	size = sizeof(snarf)/(&snarf[1]-&snarf[0]);

(assuming, of course, that snarf has at least 2 elements. . .)


Wade Guthrie
evil@arcturus.UUCP
Rockwell International
Anaheim, CA

(Rockwell doesn't necessarily believe / stand by what I'm saying; how could
they when *I* don't even know what I'm talking about???)

chris@mimsy.UUCP (Chris Torek) (02/23/89)

In article <3735@arcturus> evil@arcturus.UUCP (Wade Guthrie) writes:
>This is all well and good, but what about arrays of structures?  Am I
>S.O.L. because of inter-array-element padding if I:
>
>	struct already_defined snarf[] = {...};
>
>and then try to get the size by doing:
>
>	#define N_WHATEVER (sizeof(snarf)/sizeof(struct already_defined))

This is guaranteed to work: sizeof() includes any padding.  (But
`(sizeof(snarf) / sizeof(snarf[0]))' is more concise---or substutite
`*snarf' for `snarf[0]' to save a whole two more characters :-) .)

>or can I (portably) [take] sizeof(snarf)/(&snarf[1]-&snarf[0]) ...
>(assuming, of course, that snarf has at least 2 elements. . .)

snarf need not have two elements; the address of the n'th (where the
first is the 0'th) element is computable, and pointer subtraction is
legal.  But this will give you sizeof(snarf)/1.  Also, while I would
have to check the pANS, I believe &var[off] is not a constant, so you
could not use that as (e.g.) a case label.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (02/24/89)

In article <3735@arcturus> evil@arcturus.UUCP (Wade Guthrie) writes:

| This is all well and good, but what about arrays of structures?  Am I
| S.O.L. because of inter-array-element padding if I:
| 
| 	struct already_defined snarf[] = {...};
| 
| and then try to get the size by doing:
| 
| 	#define N_WHATEVER (sizeof(snarf)/sizeof(struct already_defined))

	This will work. The sizeof struct may not be the same as the sum
of the sizes of each element, but that's not what you want here, just
the number of elements. Struct padding is a real case of "the whole is
greater than the sum of its parts."

| or can I (portably)
| 
| 	int size;
| 	. . .
| 	size = sizeof(snarf)/(&snarf[1]-&snarf[0]);
                              ^^^^^^^^^^^^^^^^^^^
	This will (portably) evaluate to one... doubt like hell that's
what you intended. You might get what you want if you use
	((char *)&snarf[1] - (char *)&snarf[0])
but since it evaluates to "sizeof(struct already_defained)" why not
write it that way.

| (assuming, of course, that snarf has at least 2 elements. . .)

	Yes, you had better put "[2]" rather than "[]" in the original
declaration.

  I don't think there's a problem here to solve, the conventional way is
portable and works, but it was fun thinking about what the pointer
subtraction does.
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

carlp@iscuva.ISCS.COM (Carl Paukstis) (02/24/89)

In article <3735@arcturus> evil@arcturus.UUCP (Wade Guthrie) writes:
>In article <1043@auspex.UUCP>, guy@auspex.UUCP (Guy Harris) writes:
>> Of course, adding:
>> 	#define N_FCONSTS (sizeof(fund_consts)/sizeof(double)) 
>
>[and Guy said:] 
>
>> So far, so good - this is an old C trick, which is quite useful, but may
>> not be known as well as it should be - but...
>
>[and Wade says:]
>
>This is all well and good, but what about arrays of structures?  Am I
>S.O.L. because of inter-array-element padding if I:
>
>	struct already_defined snarf[] = {...};
>
>and then try to get the size by doing:
>
Method A:
>	#define N_WHATEVER (sizeof(snarf)/sizeof(struct already_defined))

Nope, you're not S.O.L.  This is well-defined in the pANS (at least in my
January draft).   From 3.3.3.4 (The sizeof operator):
"When applied to an operand that has structure or union type, the result is
the total number of bytes in such an object, including internal AND
TRAILING padding."  [emphasis mine]  

Then, from 3.5.2.1 (Structure and union specifiers):
"A pointer to a structure object, suitably cast, points to its initial
member [...], and vice versa.  There may therefore be unnamed holes within
a structure object, but not at its beginning, as necessary to achieve the
appropriate alignment.  There may also be unnamed padding AT THE END of a
structure, as necessary to achieve appropriate alignment were the structure
to be a member of an array." [emphasis mine]

I read this to require sizeof(struct already_defined) to include whatever
space is necessary to make the structure align properly, AND space
necessary to make the NEXT structure in an array align properly.

>or can I (portably)
>
>	int size;
>	. . .
Method B:
>	size = sizeof(snarf)/(&snarf[1]-&snarf[0]);
>
>(assuming, of course, that snarf has at least 2 elements. . .)

I believe I'd choose the first method.  I'm sure you realize that the above
code will always set "size" to "sizeof(snarf)", since the parenthesized
portion after the division operator will always evaluate to 1.

If you cast, e.g. 
     size = sizeof(snarf)/((char *)&snarf[1]-(char *)&snarf[0]);
or maybe
 size = sizeof(snarf)/((char *)((void *)&snarf[1])-(char *)((void *)&snarf[0]));
I'm not SURE this is portable, but I can't offhand see any reason at least
the second version wouldn't be.  It's gawdawful ugly, though.

I use method A, and have not yet had it fail me with any compiler I have
access to. (Okay, "to which I have access.")
-- 
Carl Paukstis    +1 509 927 5600 x5321  |"The right to be heard does not
                                        | automatically include the right
UUCP:     carlp@iscuvc.ISCS.COM         | to be taken seriously."
          ...uunet!iscuva!carlp         |                  - H. H. Humphrey

mark@jhereg.Jhereg.MN.ORG (Mark H. Colburn) (02/24/89)

In article <3735@arcturus> evil@arcturus.UUCP (Wade Guthrie) writes:
>> Of course, adding:
>> 	#define N_FCONSTS (sizeof(fund_consts)/sizeof(double)) 

>This is all well and good, but what about arrays of structures?  Am I
>S.O.L. because of inter-array-element padding if I:

It may be easier to use 

#define N_WHATEVER (sizeof(snarf)/sizeof(snarf[0]))

This works for any array that has a static size and constant sized
elements (the majority of the cases where you would want to use this kind
of trick anyways).


-- 
Mark H. Colburn                  "Look into a child's eye;
Minnetech Consulting, Inc.        there's no hate and there's no lie;
mark@jhereg.mn.org                there's no black and there's no white."