#char (*tp[N])[] ?
18 messages in this thread
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
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.
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
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.
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
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.
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
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
Doug,
Ah yes! I'd seen something similar, but thanks for the reminder….
(auto-piloted from)
JcP
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.
>#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.
> 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).
>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…).
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
)…..
>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.
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.)
>char *(*X[])[] should be "an array of pointers to arrays of char *'s."
It is. However, that literal syntax is usually a mistake.
> 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.