From: torek@horse.ee.lbl.gov (Chris Torek)
Newsgroups: comp.lang.c
Subject: char *foo[] (was Computer terms, etc)
Date: 13 Aug 1992 04:58:38 GMT
Organization: Lawrence Berkeley Laboratory, Berkeley
Reply-To: torek@horse.ee.lbl.gov (Chris Torek)
NNTP-Posting-Host: 128.3.112.15

In article <cee1.713424953@Isis.MsState.Edu> cee1@ra.msstate.edu
(Charles Evans) asks for definitions of a bunch of IBM PC specific
terms (which should generally be kept to other newsgroups) and for
explanations about:

	*foo[]
	**foo

and

>char *foo = "abc";
>char *foo[] = "abc";
>char foo[] = "abc";
>char foo[4] = "abc";  <--- is the '\0' automatcially added?

So far I have seen three answers, two containing errors and one short
but correct.  (I am not going to quote the correct one here.)

In article <19479@fritz.filenet.com> scotth@felix.filenet.com (Scott Hopson)
>*foo[] is a pointer to an array
>**foo is a pointer to a pointer

The latter is correct; the former is not.  Given a declaration of the
form

	T *x[];

(where T is some type), x will probably have this type:

	declare x as array UNKNOWN_SIZE of pointer to T

But if this is a format parameter declaration---we are obviously missing
some context, so we do not know whether this is the case---then x will
have this type instead:

	declare x as pointer to pointer to T

The reason is, as usual, an outgrowth of The Rule about arrays in C.
This Rule states that:

	Whenever an object of type `array N of T' appears in a value
	context, it is converted to a value of type `pointer to T',
	pointing to the first element of that array, i.e., the one with
	subscript 0.  (N must be some integer constant or the special
	`unknown size' marker, and T must be some valid type.)

So if we write:

	extern int i[];

	void f(a) char a[]; { ... }

then `i' is declared as an `array UNKNOWN_SIZE of int', but `a' is not
an array at all.  All actual function arguments are, by definition,
*values*, not objects---if you try, in C, to pass an object to a
function, you simply pass the value of that object.  But a formal
parameter declaration, such as that for `a' here, declares an object,
not a value.

Well, obviously there is a conflict here: we claim `a' is an object of
type `array UNKNOWN_SIZE of char', but it must come directly from some
value, and The Rule guarantees that there are no array values.  Dennis
Ritchie decided that the answer to this dilemma was to adjust the type
of `a', silently, in the compiler.  The compiler *knows* `a' cannot be
an array, so it changes `array N of T' to `pointer to T' and then carries
on as if you had written that in the first place.

Thus, the compiler adjusts the type, changing `array UNKNOWN_SIZE of char'
to `pointer to char', and this is *exactly the same* as if we wrote
instead:

	void f(a) char *a; { ... }

Thus, there are places (formal parameter declarations) where `T *foo[]'
and `T **foo' are identical in meaning.  I advise against using the
array notation in these cases.  It generally adds more confusion than
clarity.  Note that formal parameters are the ONLY place where these
are equivalent: at all other times, confusing `array N of T' with
`pointer to T' will get you in trouble.

In article <20900024@inmet> richw@inmet.camb.inmet.com writes:
>>  char *foo = "abc";
>>  char *foo[] = "abc";
>>  char foo[] = "abc";
>>  char foo[4] = "abc";

>First, let me give these "foo"s unique names:

(this is a good idea)

>    char *fooA = "abc";
>    char *fooB[] = "abc";
>    char fooC[] = "abc";
>    char fooD[4] = "abc";
>
>Here's a way of rewriting these which might make things clearer:
>
>    static char literal [] = { 'a', 'b', 'c', '\0' };
>
>    /*  "literal" is now a constant which equals the address  */
>    /*  of the first element of an initialized, 4-element     */
>    /*  "char" array					      */
>
>    char *fooA = literal;
>    char *fooB[] = (char **) literal;
>    #define fooC literal
>    #define fooD literal
>
>Thus, "fooC" and "fooD" are identical; both are constants which equal the
>address of the "literal" array's first element.

This is mostly right.  The initialization for fooB[], however, is
illegal (as it was in the original example).  If the original version
is repaired (by adding {} around "abc"), one correct translation is:

	char *fooB[] = { &literal[0] };

In addition, it is somewhat misleading to use the same `literal' for
all four, but it is just as misleading to use a different one for each:
The C language does not say which, if any, identical string contants
live at the same address and which do not.  (It also obscures details
as to whether string constants are read-only; this again is up to the
compiler.)  That is, given:

	char *p1 = "abc", *p2 = "abc";

the C language leaves unspecified whether p1==p2 or p1!=p2.  Either
is acceptable.  If p1==p2, and if string constants are writable, then
changing *p1 will also change *p2.  If string constants are read-only,
the possibility of p1==p2 is less important.

>Note that the "4" in the "char fooD[4]" declaration is entirely unnecessary;
>what's interesting are these cases:
>
>	fooD[3] = "abc";	/*  Illegal !  "fooD" is too small  */
>
>	fooD[5] = "abc";	/*  "fooD" is the constant address  */
>				/*  of the first element of a 5-    */
>				/*  element "char" array whose 5-th */
>				/*  element is uninitialized	    */

(I assume the word `char' is supposed to appear in front of each of these.)
In ANSI C,

	char s[3] = "abc";

is explicitly legal, and it makes s[] contain the three characters
'a', 'b', 'c', omitting the final '\0'.  In Classic C it is illegal.
In both Classic and New C, when

	char s[5] = "abc";

is legal, it makes s contain the five characters 'a', 'b', 'c', '\0',
'\0'.  (The initialization is legal in Classic C only if `s' has static
duration, while in ANSI C it is always legal.)  That is, the fifth
element of s (or fooD), s[4] (or fooD[4]), *is* initialized, to '\0'.

Note that this is *not* what you get if you use strcpy().  Strcpy()
and explicit initialization are different.

>Finally, note that "char *fooB[]" represents a pointer to an array, where
>each element of the array is itself a pointer to "char".  This is a
>*different* type than the type of "abc", i.e. a pointer to "char".  I'm
>surprised my C compiler accepted for "fooB" declaration.  In any case,
>I would never write such a declaration...

Nearly right: fooB is not a pointer to an array; it is an object of
type `array 1 of pointer to char'.  The initialization is indeed
illegal, and a compiler that silently accepts it is non-conformant.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 510 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov
Newsgroups: comp.lang.c