CompuServe Thread

#char (*tp[N])[] ?

18 messages in this thread
#36224From: Jeff PrattAug 6, 1993 1:47 AM
Help! I'm having a difficult time figuring out how to declare something in c: I've got several arrays of char *, defined in different header files — they're all constants, but the strings are various lengths, and the arrays are of different sizes. I want to make an array of pointers to these arrays… and it's giving me a great headache! Here's the best (ie, fewest compile errors) that I've done so far: (extracts from code, with much unrelated stuff left out) In "mt.h": #ifndef MT_H #define MT_H #define MT_NFIELDS 15 #ifdef _FB_MT char *mt_fieldnames[MATSTYLE_NFIELDS] = { "MAT_NAME", "INDEX_OF_R", "SURF_DEPT", "FIN_DEPT", "DAYS" }; #else extern char *mt_fieldnames[MT_NFIELDS]; #endif #endif (note "xx_fieldnames" below is defined in another include file similar to the one above; different number of strings of differing length.) In "fb.h": #ifndef _FB_H #define _FB_H #include <mt.h> #include <that other header file> #define N_FB_FILES 2 #ifdef _FB_FILES #define WHERE /*public*/ /* …an N_FB_FILES-element array of ptrs to x-element arrays of char? */ WHERE char (*db_fieldnames[N_FB_FILES])[] = { &xx_fieldnames, &mt_fieldnames }; #else #define WHERE extern WHERE char (*db_fieldnames[N_FB_FILES])[]; #endif #endif If anyone cares to, please feel free to give me some "pointers" on how to do this…. tutorial book references appreciated too ( I've got K&R & the Waite-group's book)…. Thanks…. 🙂 (auto-piloted from) JcP
#36226From: Dale LarsonAug 6, 1993 5:10 AM
I'm guessing that you don't mean everything that you say. I think that you want an array of pointers to strings (which are themselves arrays of chars). An array, X, of pointers to char *, is declared as char *X[y];, where "y" is the length of the desired array. It absoultely doesn't matter how long any of the strings are. This array is only going to take (y*sizeof(char *)) no matter what any of the pointers point to. What this does is to create an array of "y" pointers. If you haven't auto-initialized your array (told the compiler what strings you want to be pointed to), you could initialize it and use it something like this: main() { char *X[3]; X[0] = "test"; X[1] = "this is"; X[2] = "a"; printf("%s %s %s.\n", X[1], X[2], X[0]); } Looking at your source: First, don't put a declaration in a header. (There are two words I can never remember to keep straight — one is where you tell the compiler about the type of some variable you want to refernce, the other is where you tell the compiler that *and* ask it to set aside memory for the variable — I'm talking about the later.) You could have "extern char *mt_fieldnames[]" in your .h, but you shouldn't have the rest there. I don't get your #define of WHERE. I don't understand the syntax and I don't understand what you are trying to do. Dale Larson – An Amiga Software Engineer with some time on his hands.
#36234From: Jeff PrattAug 6, 1993 8:15 PM
Dale, the WHERE stuff I should have left out of the example. The way I use it, I #define something before including the header if and only if the module that's including it is the one where I want the space for the variable to be; then in the header file I define "WHERE" to be just a blank (well, a comment actually) if so; otherwise, WHERE gets defined to be "extern"… Your example of char *'s is quite clear, thanks… now, how would I go about setting up an array of pointers to arrays of char *'s ? thanks for taking a look at that mess…. (auto-piloted from) JcP
#36235From: Dale LarsonAug 6, 1993 8:45 PM
What are you going to use it for? Let's see. if char *X[] is an array of pointers to char, then char (*X[])[] is an array of arrays of pointers to char and char *(*X[])[] should be "an array of pointers to arrays of char *'s." I should check this with the nice little program that converts English to declarations and back, but I don't have it handy, so I'm not 100% sure. I usually have either used the construct or have an example handy or realize there is an easier way to do things. Dale Larson – An Amiga Software Engineer with some time on his hands.
#36268From: Jeff PrattAug 8, 1993 12:13 AM
Well, I did figure out how to do what I needed to do — partly, "realized there was an easier way…": #define NF 3 char *fruits[NF] = {"Apples", "Oranges", "Bananas"}; #define NV 4 char *vegetables[NV] = {"Carrots", "Brocoli", "Turnips", "Rutabagas"}; #define NP 2 char **plants[NP] = { fruits, vegetables }; #define NGROUPS 2 int nkinds[NGROUPS] = { NV, NP }; /* print out foods: */ int n, i; char **name_ptr; for(n=0;n<NGROUPS;n++) { name_ptr = plants[n]; for (i=0;i<nkinds[n];i++) printf("\n I like to eat %s", *name_ptr++ ); } …. this is a simplified example. The use for it is to organize and access some file-names, along with a variable number of field-names for each file. (plus some other arrays for file & field handles, etc….) thanks for the suggestions. (auto-piloted from) JcP
#36280From: Dale LarsonAug 8, 1993 1:09 PM
One more suggestion. You might NULL terminate your arrays just like strings are NUL-terminated. char *fruits[] = {"apples", "oranges", bananas", NULL}; This should let you not have to keep track of NF if you don't want to, you just increment an index and compare fruits[i] to NULL each time. I'm not sure whether that would work if "\0" is a valid string for your purposes. Dale Larson – An Amiga Software Engineer with some time on his hands.
#36310From: Jeff PrattAug 9, 1993 9:10 PM
Thanks. I don't know if that will be useful or not in this application, but it is a point well worth considering. (auto-piloted from) JcP
#36399From: Doug WalkerAug 15, 1993 10:22 AM
Jeff, Here's another neat trick if and only if the size of the arrays is needed in the same file that they are defined: char *strings[] = {"A", "B", "C"}; #define NSTRINGS (sizeof(strings)/sizeof(char *)) This will automatically update the NSTRINGS define if you add strings. The sizeof() expression is evaluated at compile time, so it doesn't add to the size of your program. Note that you CAN'T use this method if you are declaring the data item as an extern and not specifying a number inside the []. –Doug
#36413From: Jeff PrattAug 15, 1993 10:56 PM
Doug, Ah yes! I'd seen something similar, but thanks for the reminder…. (auto-piloted from) JcP
#36426From: Vic WagnerAug 16, 1993 10:06 PM
Doug, I have a macro I use in many of my C programs which you may find useful: #define span(x) (sizeof(x)/sizeof(x[0])) very useful for undimenisoned/initialized arrays.
#36450From: Greg Comeau@Comeau CmptgAug 18, 1993 3:12 PM
>#define span(x) (sizeof(x)/sizeof(x[0])) > >very useful for undimenisoned/initialized arrays. Just to clarify what Vic means, in addition to the normal sometype array[somedim]; he's talking about sometype array[] = { init'ers }; where in this case (ONLY!) the compiler will fill in the [] with the number of initializers. Hence, by the } in this case, the incomplete array is completed and hence we can sizeof it.
#36449From: Greg Comeau@Comeau CmptgAug 18, 1993 3:12 PM
> Here's another neat trick if and only if the size of the arrays is >needed in the same file that they are defined: > > char *strings[] = {"A", "B", "C"}; > #define NSTRINGS (sizeof(strings)/sizeof(char *)) Yes, though that macro is too "close" to the id 'strings' for me. Instead I prefer a more generic version: #define HBOUND(arr) (sizeof(arr) / sizeof(arr[0]) as it will get the 1st-d of any array. >Note that you CAN'T use this method if you are declaring the data item >as an extern and not specifying a number inside the []. That still remains true here… The array type must be complete by the time of the sizeof request(s).
#36448From: Greg Comeau@Comeau CmptgAug 18, 1993 3:12 PM
>char *fruits[NF] = {"Apples", "Oranges", "Bananas"}; >char *vegetables[NV] = {"Carrots", "Brocoli", "Turnips", "Rutabagas"}; >char **plants[NP] = { fruits, vegetables }; Right. The issue is that fruits is of type 'char *[NF]' and vegatables is of type 'char *[NV]'. In certain contexts both these type can collapse into char **, which is what each plants[?] is. >int nkinds[NGROUPS] = { NV, NP }; But this is most always a mistake waiting to happen. Why? One should generally avoid referring to constants at almost all costs and one should generally avoid even referring to named constants unless practical or necessary, especially names constants of this sort (certainly I'm not taking about something like PI here…).
#36452From: Jeff PrattAug 19, 1993 1:58 AM
Thanks for your comments. I'm not sure I see exactly why using named constants in this kind of situation should be avoided — certainly, I would not want to set up something that should be dynamically allocated as a bunch of static arrays; but in this case (my actual application needs some arrays to hold file-names, and, for each file, further arrays of field names (not the same number of fields for each file, of course… and a further array of key buffers, also of different number & size….) this seems like a fairly reasonable approach. Maybe I'm missing something ( as is so often the case )…..
#36455From: Greg Comeau@Comeau CmptgAug 19, 1993 8:25 AM
>I'm not sure I see exactly why using named >constants in this kind of situation should be avoided I'd said in general. Though I will carry the thought along to this kind of situation too. The issue is that named constants for dimension sizes can usually be done in some other way. And better IMO. This thread has seen three suggestions on this already: 1) use something like HBOUND (or span as somebody else called it). 2) Look at really what is being said by sometype arr[] = { the init'ors }; Here we avoid the number altogether. 3) Seperately, in in combo with 2, see if a sentinal value makes sense. Zero of some sort often (but not always) does. Consider: #define ARRSIZE 5 int arr[ARRSIZE] = { 1, 2, 3, 4, 5 }; For starters, one should strive to avoid the preprocessor. Other choices might consist of a const int (in C++ only) or an enumerator. (The compiler proper knows about these, the compiler proper knows nothing about the preprocessor). Even with those changes though, a traversal might be: int i; for (i = 0; i < ARRSIZE; i++) arr[i] = -arr[i]; What's been established is a related, but disjoint, identifier ARRSIZE to deal with arr's size. Hence, I might also have an ARR2SIZE for arr2. I don't want this relationship unless absolutely necessary. What are my choices though? How about: int arr[ARRSIZE+1] = { 1, 2, 3, 4, 5, 0 }; … for (i = 0; arr[i]; i++) arr[i] = arr[i]; Or since we've no more need for ARRSIZE in at least this context, forget about ARRSIZE altogether, and forget about having to forget or remeber about the +1, or to ensure that the count is right (and don't get me wrong, sometimes the number of initializers better be right) and just do: int arr[] = { 1, 2, 3, 4, 5, /* sentinal */ 0 }; The data structure is now fully capable of driving itself w/o any baggage. That of course is not always possible. For instance all possible int's can be valid init's for arr and hence there can be no one valid sentinal. That still leaves us with int arr[] = { 1, 2, 3, 4, 5 }; and perhaps: for (i = 0; i < HBOUND(arr); i++) arr[i] = -arr[i]; Still need to utter the bound somehow, but a special "funny" name doesn't always have to exist. >would not want to set up something that should be dynamically allocated as >a bunch of static arrays Actually sometimes you do. Depends upon what you compromises, tradeoffs, and goals are. This should probably typically not be ones first course of action though. I'm unclear on the relationship you're building though. Or do you just mean that if you did do a dynamic allocation that you would have to have specified some size to the malloc (or new in C++) request? >…a further array of key buffers, also of different number & size….) this >seems like a fairly reasonable approach. Maybe I'm missing something ( as >is so often the case )….. What you ended up with as I recall was the way to do it. I was just nitpicking a frosting so to speak. The deal to me is to strive to let data structures "drive" themselves and be as closeknit implemented as possible.
#36477From: Jeff PrattAug 21, 1993 5:36 AM
You're right — I did come up with an approach which seems reasonable… and I agree with your comments in general. (and everyone else's, too; there were a lot of constructive comments on this thread.)
#36447From: Greg Comeau@Comeau CmptgAug 18, 1993 3:12 PM
>char *(*X[])[] should be "an array of pointers to arrays of char *'s." It is. However, that literal syntax is usually a mistake.
#36446From: Greg Comeau@Comeau CmptgAug 18, 1993 3:12 PM
> char *mt_fieldnames[MATSTYLE_NFIELDS] = { "MAT_NAME", > "INDEX_OF_R", > "SURF_DEPT", > "FIN_DEPT", > "DAYS" }; >.. >WHERE char (*db_fieldnames[N_FB_FILES])[] = { &xx_fieldnames, > &mt_fieldnames }; Your problem here is that xx_fieldnames and mt_fieldnames are going to be of different dimensions. C does not have dynamic dimensioning of this sort. Further, this presents and incomplete type of a special sort which in general you should avoid. Further further, pointers to arrays are oft misunderstood and most always misunderstood (not to be avoided for the right reasons though). I suspect you may want to collapse the [] into a *, but not being totally clear on exactly what you want to do, that may be a misdirected comment.