vector initialization

Chris Torek chris at mimsy.UUCP
Wed Jun 7 17:33:42 AEST 1989


In article <687 at lakesys.UUCP> chad at lakesys.UUCP (Chad Gibbons) writes:
>Question: is it possible to initialize a vector?
[where `vector' means `object of type pointer to pointer to char']

Yes.  The only legal data type that cannot be initialised is a union
(and even that can have an initialiser, albeit restricted, in pANS C).

>	I tried using the same syntax as an array of pointers, i.e.
>		char *foo[] = { "one", "two", "three", NULL };
>but this would not compile.

The definition above for `foo' is legal: `foo' is an object of type
`array ? of pointer to char' (array 4, after the initialisation is
complete) and the initialiser is of type `array 4 of pointer to char'.
The way the aggregate---everything between the left and right braces
---acquires this type is somewhat convoluted%: it picks up the `array'
part from the variable being initialised, gets the `4' from the actual
number of elements, and gets the `pointer to char' part from both the
variable and the values themselves.  The type of a double-quoted string
is `array N of char', where N is the length of the string including the
final \0 character; this reverts to an object of type `pointer to char'
in this initialiser context, so the types of the individual array
elements match.  After solidifying foo[] as an `array 4', the types
of both sides of the equal sign match and the whole thing is declared
sane (even if you are no longer, after reading this paragraph).

-----
% Some might say `Byzantine' or `Baroque', but I prefer `Rococo' :-)
-----

>Since I am initializing it, it would seem an array of pointers would
>suffice.

You need an object of type `pointer to pointer to char'.  An array of
pointer to char would suffice, if you could create one without giving
it a name, because it would decay into a pointer to pointer to char.
But you cannot create one without naming it---the initialisation for
`foo' above is legal only because there is a context allowing the array
(namely the intialiser for `foo').  C does not have `naked arrays',
with the one exception of double-quoted strings---you cannot, in the
midst of some expression, write

	({ "foo", "bar" })[i]			/* illegal */

to select either "foo" or "bar" depending on i==0 or i==1.  (You
*can* write this in GCC, by using an `initialised cast':

	((char *[]){ "foo", "bar" })[i]		/* GCC-specific */

The cast provides the shape for the aggregate.)  Normally, the only
way to construct an aggregate object (structure or array) is as an
initialiser for a named variable, and the variable provide the shape.
The variable (or, in GCC, the cast) must have an aggregate type,
not a simple pointer type.  In essence,

	char **p = { "a", "b", "c" };		/* illegal */

provides the wrong context---the compiler thinks, `Aha, we need a
pointer to pointer to char, and we have a left brace which means an
aggregate ... oops, something is wrong, help me Spock....'  It
cannot go through its type-matching waltz without first being told
`array, array!'%%.

-----
%% or `Ole, ole!'; but this works only in Spanish-speaking C compilers.
-----

Now that I have told you why you cannot do it (but not in 50 words
or less), here is the rest of the story:

>... something along these lines (from a command list vector):
>
>typedef struct _com {
>    char **words;
>    int (*fcn)();
>    short flags;
>} COM;
>
>...and then initializing the structure in another module by:
>
>COM foo[] = {
>    { "one", "two", "three", NULL }, do_num, 0,
>    { "exit", "quit", NULL }, quit, 0
>};

For each of the two COM objects foo[0] and foo[1], you need a constant
value of type `char **'.  So what we *can* do is this:

	char *xxx0[] = { "one", "two", "three", NULL };
	char *xxx1[] = { "exit", "quit", NULL };

	COM foo[] = {
		{ xxx0, do_num, 0 },
		{ xxx1, quit, 0 },
	};

(I have put back the optional braces in the initialiser for foo[],
and added the optional extra comma after foo[1]'s initialiser.)  Each
xxx array is an object of type (char *[]), which degenerates into one
of type (char **), and which is a constant expression after this
degeneration (because it is the address of a global variable).  If
we hated making up names, and did not mind restricting ourselves to
GCC, we could instead use

	COM foo[] = {
		{ (char *[]){ "one", "two", "three", NULL }, do_num, 0 },
		{ (char *[]){ "exit", "quit", NULL }, quit, 0 },
	};

but the former approach is portable, if somewhat ugly.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris at mimsy.umd.edu	Path:	uunet!mimsy!chris



More information about the Comp.lang.c mailing list