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."