Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!ut-sally!im4u!rutgers!sri-unix!sri-spam!mordor!lll-tis!ptsfa!ihnp4!homxb!houxm!whuts!whuxm!davew From: davew@whuxm.UUCP (WONNACOTT) Newsgroups: comp.lang.c Subject: Re: static char (*b)[6]; /* an unusual declaration */ Message-ID: <535@whuxm.UUCP> Date: Fri, 12-Jun-87 14:11:42 EDT Article-I.D.: whuxm.535 Posted: Fri Jun 12 14:11:42 1987 Date-Received: Sun, 21-Jun-87 01:40:33 EDT References: <761@bsu-cs.UUCP> Organization: AT&T Bell Laboratories Lines: 159 In article <761@bsu-cs.UUCP>, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes: > The April 1987 issue of DEC Professional magazine has this interesting > example of possible obfuscation in C. > > What is the meaning of the following declaration? > > static char (*b)[6]; > Parenthesis can be used in declarations to alter the meaning of the declaration. Baiscly, you read the part of the declaration in parenthesis first, followed by whats outside of the parenthesis. For example: char *argv[6]; /* [] has higher precedence than *, so argv is an array of 6 pointers to char */ char (*b)[6]; /* now the * is in parenthesis, so we read the * first b is a pointer to an array of 6 chars */ char *gets(); /* () has higher precedence than *, so gets is a function returning pointer to char */ char (*fp)(); /* now the * is in parethesis, so we read it first fp is a pointer to a function returning char */ The compiler and lint seem to > treat the declaration of b as equivalent to char ** but the following > strange things happen. "seem to" is right -- just as char *ptr and char array[6] "seem to" be the same to many c novices -- but ptr allocates a pointer and array allocates space for 6 characters. > > main() > { > static char (*b)[6]; /* an unusual declaration */ b should be used to point to an array of 6 characters. > static char a[] = "Hello"; > char *c = a; > char **d = &c; > b = d; /* So b == d ... Watch. */ b is used here to point to a pointer to a character -- not to an array of 6 characters, as it should be used. Perhaps C compilers should catch this sort of abuse of pointers... b = &a; would be better in some sense, but most C compilers assume that you really meant &a[0] if you write &a, and ignore the &, and you get b = a; instead, which is an illegal pointer combination, so you have to put in an explicit cast: b = (char (*)[6]) a; which works (it casts a from type char * into the type of b: pointer to an arrya of 6 chars). Many people find the above code (mine and the original) too ugly to read, and take to using typedef to clarify the situation (a vary good idea, in my opinion): typedef char (* pointer_to_array_of_6_chars)[6]; static pointer_to_array_of_6_chars b; b = (pointer_to_array_of_6_chars) a; In any case, back to what happened in the original and why: > > printf ("%s\n", *d); /* prints "Hello" */ yes -- d is a pointer to a pointer to a character, so *d is a pointer to a character (the H in Hello, to be specific) > printf ("%s\n", *b); /* prints 0 */ The variable b is a pointer to an array of 6 chars, so *b is an array of 6 bytes starting where b points (that is, at the address of c). Arrays in C are passed by passing the address of the first element of the array. So -- since *b is an array of 6 characters starting with the first byte of the pointer c, C will pass the address of the variable c, not the address of the variable a. b------b c------c a------a | | / | |<-- | H | | | | | | | | e | | |----->| | |--+---->| l | | | | | | | | l | b------b | c------c | | o | | d------d | | \0 | | | | | a------a \ | | | | |--' | | d------d I've done my best to draw this with the limited capabilities of an ASCII terminal: Assuming 4 byte pointers, and that c and d happen to be contiguous on the stack. Try to look at the above paragraph and the diagram at the same time (they'll both fit on the screen together). b and d point to the same address -- the beginning of c. But *d is a character pointer located there, and *b is a 6 byte array located there. *d (which == c) will print the string "Hello". *b (the array of 6 bytes encompassing the variable c and some of the bytes of d), will not print anything sensible. You would get the same printout from printf("%s\n", &c); > > Other code fragments omitted. The columnist continues, "...This means > that *b is a constant and while (b) can be modified, *b never will > change its original value. just as you cannot assign a new value to an array name. > Thus, setting b = d does not mean that > *b == *d. Also, while *b is treated as a constant (unchangeable), the > value of b can vary." note that the above is true in other cases as well: int *b; float f = 1.234; float *d = &f; b = d; /* illegal on modern compilers without a cast, but similar to the example above in spirit */ /* in this case, *b != *d */ > > He goes on to ask if other C compiles do the same thing, and wonders > if Ritchie had this in mind when he defined the language. Other C compilers do the same thing -- its consistent with the C treatment of arrays. The trick to understanding it is to remember that b is a pointer to an array of 6 characters, not a pointer to a pointer. > > -- > Rahul Dhesi UUCP: {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi I hope this has helped the original poster and any others who didn't understand what was going on in the original article. -- David Wonnacott "They said Van Gogh was crazy, didn't they?" whuxm!davew or rz3bb!davew AT&T Corporate Education The above opinions are not necessarily those of AT&T, or of anyone else anywhere