Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!cs.utexas.edu!csd4.milw.wisc.edu!uxc!tank!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.lang.c Subject: Re: vector initialization Keywords: vectors, char** Message-ID: <17940@mimsy.UUCP> Date: 7 Jun 89 07:33:42 GMT References: <2730@oregon.uoregon.edu> <18226@unix.cis.pittsburgh.edu> <687@lakesys.UUCP> Distribution: usa Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 113 In article <687@lakesys.UUCP> chad@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@mimsy.umd.edu Path: uunet!mimsy!chris