Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!att!bu.edu!purdue!haven!mimsy!chris From: chris@mimsy.umd.edu (Chris Torek) Newsgroups: comp.lang.c Subject: Re: sizeof() confusion Message-ID: <27432@mimsy.umd.edu> Date: 5 Nov 90 07:38:01 GMT References: <9156@latcs1.oz.au> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 104 In article <9156@latcs1.oz.au> jacob@latcs1.oz.au (Jacob L. Cybulski) asks why, given the quoted code, sizeof(Pattern) != sizeof(x): >typedef unsigned char Pattern[8]; >void foo(Pattern x) { >/* 0 */ printf("%d\n", sizeof(Pattern); /* prints 8 */ >/* 1 */ printf("%d\n", sizeof(x)); /* prints 4 */ >The intuition says that sizeof(Pattern) = sizeof(x) regardless of the >Pattern definition. Almost true---but *not* regardless of the `x' definition. (Incidentally, your example is clearly not out of a working program, as it has a missing close parenthesis.) >Well, not in C, if Pattern is an array then it is implemented as a >pointer so sizeof(x) is a pointer length .... Bad referent for `it' (core dumped) :-) In C, there is no such thing as an array parameter. In a language much like C, but more strict, compilers would be required to reject any parameter declaration whose type is `array N of T', for any N and T. But the C language grants explicit rope with which you may hang yourself: you may declare any parameter as an array, and the compiler will say `Oh, that's not an array N of T, that's a pointer to T. I'll just tidy up here and pretend you wrote ``pointer to T''.' But there *are* array objects, and any other declaration of type `array N of T' really does mean `array N of T'. Since `typedef' does not make a new type, but rather a synonym for an old type, `typedef unsigned char Pattern[8];' simply makes `Pattern' a synonym for `array 8 of unsigned char'. The compiler sees your function definition as Declare foo as a function returning void, in which there is one argument whose type is `array 8 of unsigned char'. Define the function with the contents of the block. The compiler does its bit with the type of the argument and changes this to: Declare foo as a function returning void, in which there is one argument whose type is `pointer to unsigned char'. Thus, you have not actually written `void foo(Pattern x)'. You have actually written `void foo(unsigned char *x)'. >/* 2 */ printf("%d\n", sizeof(*x)); /* prints 1 */ >*x is an address of the first array element so it's length is that >of its elements No: `x' is `pointer to unsigned char', so `*x' is `unsigned char', and sizeof(unsigned char) is 1. >/* 3 */ printf("%d\n", sizeof((Pattern) x); /* illegal */ >the cast of x into its type is illegal possibly because x is >implicitly defined as a (Pattern *) No, the cast is illegal because there is no such thing an an array value, and the result of a cast is a value, not an object. It is therefore illegal to use an array type (such as `array 8 of unsigned char', aka `Pattern') in a cast. >/* 4 */ printf("%d\n", sizeof(*((Pattern *) x)); /* prints 8 */ >finally a convoluted casting and redirection gives you the right answer Sort of. `x' is a pointer to array 8 of unsigned char, and can be cast to a different pointer type (with implementation-defined semantics for the actual value produced, although here the value is not used and must be generated by the compiler). `Pattern *' is another name for `pointer to array 8 of unsigned char'. Indirecting (the first unary `*' above) produces an object---the result of an indirection is always an object, much as the result of a cast or address-of operator is never an object---of type `array 8 of unsigned char'. In a value context, this object would be transformed into a value of type `pointer to unsigned char', but unlike an actual function parameter, sizeof() does not call for a value. Thus, the object is in object context and remains an array, and hence has size 8*sizeof(unsigned char) or 8*1 or 8. >Any answers? Never use array parameters. :-) More seriously: for fixed opaque types that happen to be implementable as an array, you are often better off making the type be a structure whose only member is an array. Instead of typedef unsigned char Pattern[8]; write typedef struct Pattern { unsigned char p_foo[8]; } Pattern; You now have a compound object which acts more like simple objects, rather than one that displays the peculiarity of changing type (and hence also value) whenever it appears in a value context. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris