Can a static array contain a dynamic array of pointers?

P

Peter B. Steiger

The latest project in my ongoing quest to evolve my brain from Pascal to C
is a simple word game that involves stringing together random lists of
words. In the Pascal version the whole array was static; if the input
file contained more than [MAX_WORDS] entries, tough.

This time I want to do it right - use a dynamic array that increases in
size with each word read from the file. A few test programs that make use
of **List and realloc( List, blah blah blah) worked fine but when I get to
the actual program things start falling apart.

The problem is that there is not one list of words, but three. I know
from the start that there will always be three, so that part is static:

typedef struct _onelist
{
int wordcount;
char **Words;
} OneList;

....

OneList BigList[ 3 ];

At this point, BigList should contain three static copies of type OneList.
If I pass any one element of BigList to my read-from-file function, all
is well:

ReadFromFile( MyFile, &BigList[ 0 ] );

Over in function ReadFromFile, as long as I'm just receiving the pointer
to a single copy of type OneList, it works exactly as desired:
int ReadFromFile( FILE *MyFile, OneList *List )
{
char **newlistsize;
char *newword;
char buffer[ 100 ];
int lSuccess = 0;

fgets( buffer, sizeof( buffer ), MyFile );

newlistsize = realloc( List, (List->wordcount + 1) * sizeof( newword ) );
if (newlistsize != NULL )
{
if (newword = malloc( (char *) malloc( strlen( buffer ) + 1 ))
{
List = newlistsize;
List->wordcount++;
List->Words[ wordcount ] = newword;
}
}
}

So far, so good. But if, instead, I pass the whole 3-element BigList to
ReadFromFile, it loses track of where the address for each element begins:
// from main:
ReadFromFile( &BigList );

....
int ReadFromFile( OneList *BigList[] );

If I print out the address of BigList in main(), then print out the
address of BigList in ReadFromFile(), the latter shows an address about 4
bytes less than the one main() sent. The address of BigList[ 0 ] (within
the called function) is null, so any attempts to access BigList[ n
]->wordcount or BigList[ n ]->Words[ y ] results in a segfault or other
unpredictable behavior.

I can easily code this a different way so at any given time I only pass
a single OneList type element of the BigList array around, but the point
of this is not just to find something that works, but to learn the right
way of doing it. Is it simply not possible to pass a pointer to the
static three-element BigList array around and still be able to access
individual elements from it?

And to head off any advice about using linked lists instead... once the
five^H^H^H^Hthree word lists are built, my game randomly picks one word
from each list, so I need to be able to grab a single word [n] out of each
array as needed. I could walk through a linked list [n] times, but that
(to me) seems much slower and more tedious than simply referencing Word[n]
out of an array.
 
E

Eric Sosman

typedef struct _onelist
{
int wordcount;
char **Words;
} OneList;

...

OneList BigList[ 3 ];

[...]

ReadFromFile( &BigList );

...
int ReadFromFile( OneList *BigList[] );

The argument you provide in the call is not the same
type as the parameter the function expects:

- `&BigList' has the type "pointer to array of three
`OneList' objects.

- With `OneList *BigList[]', the function expects to
receive a "pointer to the start of an array of
unknown size, containing `OneList' pointers."

Please see Questions 6.3, 6.4, 6.8, 6.12, and -- aww,
the heck with it: read all of Section 6 in the comp.lang.c
Frequently Asked Questions list

http://www.eskimo.com/~scs/C-faq/top.html

The diagram in Question 6.2 may be especially helpful.
 
P

Peter B. Steiger

Please see Questions 6.3, 6.4, 6.8, 6.12, and -- aww,
the heck with it: read all of Section 6 in the comp.lang.c Frequently
Asked Questions list

http://www.eskimo.com/~scs/C-faq/top.html

The diagram in Question 6.2 may be especially helpful.

Would you believe me if I said that's the first place I looked before
posting here? That's what led me to believe that by sending (from main)
&BigArray would send a pointer to the whole three-element array of
structures, from which the called function could then access
BigArray[0]->nAmount, BigArray[0]->pointer-to-dynamic-array-of-strings,
etc.
- `&BigList' has the type "pointer to array of three
`OneList' objects.

- With `OneList *BigList[]', the function expects to
receive a "pointer to the start of an array of unknown size, containing
`OneList' pointers."

Right, but from that it sounds like &BigList and *BigList[] would at least
start at the same address, wouldn't they?

6.16, and 6.18-6.20 seem closer to my question, but they still assume a
much simpler setup - where the double pointer stands alone as a single
data type (e.g., char **ThisArray or int **SomeNumbers). It sounds like
my problem is that by embedding the dynamic array as an element within a
structure, I'm making it impossible to traverse the array from one char**
element to the next.

In my mind, I'm seeing the memory laid out like this (assuming a 2-byte
integer and a 4-byte pointer, which I realize varies from one platform
from the next but just for the sake of illustrating the math please
indulge me...)

typedef struct _onelist
{
int wordcount; /* assume 2 bytes */
char **wordlist; /* assume 4 bytes */
} OneList;

OneList BigList[ 3 ];

memory map (assuming BigList starts at 1000):
1000-1017: entire BigList array, 18 bytes (3 elements * 6 bytes each)
1000-1005: All of BigList[ 0 ], a single OneList entry
1000-1001: BigList[ 0 ].wordcount
1002-1005: BigList[ 0 ].wordlist
1006-1011: All of BigList[ 1 ], the next OneList entry
1006-1007: BigList[ 1 ].wordcount
1008-1011: BigList[ 1 ].wordlist
1012-1017: All of BigList[ 2 ], the last OneList entry
1012-1013: BigList[ 2 ].wordcount
1014-1017: BigList[ 2 ].wordlist

So if I reference BigList[ 0 ], that would be a pointer to a structure and
get me the address 1000. BigList[ 1 ].wordcount would be a single data
type and would get me the actual number stored there, but &BigList[ 1
].wordcount would get me 1006. Likewise, BigList[ 1 ].wordlist would
return the address 1008, but the contents of that address should be NULL
until I populate it with some pointers acquired by malloc() or realloc().

All of that works as I understand it should work - in main() where the
array of structures is first defined. But pass all of BigList to another
function, and suddenly BigList[ 1 ] gets me the address 1004, as though I
were referencing the second pointer rather than the second OneList
element. I find that if I leave off the pointers (e.g., SomeFunc( BigList
) and SomeFunc has the parameter OneList BigList[ 3 ]), I can at least
travers from BigList[ 0 ] at 1000 to BigList[ 1 ] at 1006 correctly. But
because BigList[ 1 ].wordlist is a static address (1008), I can't assign
BigList[ 1 ].wordlist = (char *) malloc( nWords * sizeof( char ))... I'm
screwing up the static pointers in BigList and that's where I get the
segfault.

The more I read about how mixing pointers and arrays complicates things,
the more convinced I am that this is just the wrong approach and I should
only pass a single OneList structure, or even just the OneList.wordlist
dynamic pointer list, around as a function parameter.
 
S

Stephen L.

Peter B. Steiger said:
The latest project in my ongoing quest to evolve my brain from Pascal to C
is a simple word game that involves stringing together random lists of
words. In the Pascal version the whole array was static; if the input
file contained more than [MAX_WORDS] entries, tough.

This time I want to do it right - use a dynamic array that increases in
size with each word read from the file. A few test programs that make use
of **List and realloc( List, blah blah blah) worked fine but when I get to
the actual program things start falling apart.

The problem is that there is not one list of words, but three. I know
from the start that there will always be three, so that part is static:

typedef struct _onelist
{
int wordcount;
char **Words;
} OneList;

...

OneList BigList[ 3 ];

At this point, BigList should contain three static copies of type OneList.
If I pass any one element of BigList to my read-from-file function, all
is well:

"BigList" _is_ an array of three "OneList" items.
Think of - "char foo[5]" is an array of five _type_ char items.
Saying "static copies" has no meaning here that I can see.

Also, note your declaration of "char **Words;" is
a "pointer to pointer to char", usually used in the context
of an array of pointers to char(s). This is okay.
Think of the second arg to `main()', "char **argv".
ReadFromFile( MyFile, &BigList[ 0 ] );

This is okay. The prototype of your `ReadFromFile()'
function's second argument expects a pointer to
a type `OneList' which you've provided.

On the down side, you haven't initialized _any_
of the items in/of "BigList". If "BigList" is an
auto variable, then it most certainly contains garbage
(even if the garbage just happens to be interesting
values). I'm assuming you've initialized it somewhere
with -

memset(BigList, '\0', sizeof (BigList));
Over in function ReadFromFile, as long as I'm just receiving the pointer
to a single copy of type OneList, it works exactly as desired:
int ReadFromFile( FILE *MyFile, OneList *List )
{
char **newlistsize;
char *newword;
char buffer[ 100 ];
int lSuccess = 0;

fgets( buffer, sizeof( buffer ), MyFile );

Also, in the function `ReadFromFile()', you receive
a pointer TO a `OneList' item, NOT A COPY. Changes
you make to/through that pointer are reflected
in the calling function. This is a major strength
of the `C' programming language (and its greatest
libality).

Standard `fgets()' stuff (I assume error checking omitted for clarity?)
newlistsize = realloc( List, (List->wordcount + 1) * sizeof( newword ) );

As luck would have it, this _appeared_ to work for you.
But it is WRONG. You want to pass in "List->Words" to `realloc()' -

newlistsize
= realloc( List->Words, (List->wordcount + 1) * sizeof( newword ) );

Actually, you _really_ don't care 'bout "newlistsize";
It's only a temporary container. I'd declare it as -

void *newlistsize;

if (newlistsize != NULL )
{

If we succeeded, then solidify it...

List->Words = newlistsize;

Now, "List->Words" contains a valid pointer to
an array of pointer(s) to char. In the above
example, the array is large enough for a single pointer.
Note the pointer in this array is uninitialized.

I have no idea how this next section ever produced
meaningful results (are you on an AIX box by chance?)
if (newword = malloc( (char *) malloc( strlen( buffer ) + 1 ))
{
List = newlistsize; /* *** BAD BAD BAD *** */
List->wordcount++;
List->Words[ wordcount ] = newword; /* How did this line compile? */
}
}
}

Here's what I believe you meant:

if (newlistsize != NULL )
{
List->Words = newlistsize;
List->Words[ List->wordcount++ ] = strdup(buffer);
}

The `strdup()' function is a cleaner
way to do what you wanted (it performs `malloc()',
etc.) AND copies the string - something
your snippet DID NOT do.

Note, you end the function without
returning a value (you declared it as returning `int').

I'm going to stop here and let that digest.
You should be able to take it from here with the above.

Check the `C' FAQ for more info (many sections apply) -

http://www.eskimo.com/~scs/C-faq/top.html


Stephen
eM
 
E

Eric Sosman

Peter B. Steiger said:
Please see Questions 6.3, 6.4, 6.8, 6.12, and -- aww,
the heck with it: read all of Section 6 in the comp.lang.c Frequently
Asked Questions list

http://www.eskimo.com/~scs/C-faq/top.html

The diagram in Question 6.2 may be especially helpful.

Would you believe me if I said that's the first place I looked before
posting here? That's what led me to believe that by sending (from main)
&BigArray would send a pointer to the whole three-element array of
structures, from which the called function could then access
BigArray[0]->nAmount, BigArray[0]->pointer-to-dynamic-array-of-strings,
etc.

Glad to hear that you checked the FAQ; sorry to hear
that it wasn't helpful. I'll attempt a diagram more closely
tailored to your specific situation (but be warned: I flunked
finger-painting as a child, and my pictures may be worth less
than the statutory kiloword).

First, a quick synopsis of the data structure:

typedef struct { int wordcount; ... } OneList;
OneList BigList[3];

.... which gives you an array of three `OneList' objects:

+------------+------------+------------+
| BigList[0] | BigList[1] | BigList[2] |
+------------+------------+------------+

The function call was

ReadFromFile( &BigList );

.... which passes a pointer to that array. As Questions 6.12
and 6.13 in the FAQ explain, this pointer is probably not of
the type you want: it designates the entire array as one
inseparable unit, while what you usually need is a pointer that
can address the three array elements individually. `BigList'
and `BigList[0]' and `BigList[0].wordcount' all start at the
same memory location, but that doesn't mean that pointers to
them are interchangeable. For example, consider applying the
`++' operator to each of these three pointers: even though
they aimed at the same memory location prior to `++', they will
point to completely different locations afterward.

On with the show: The function was actually defined as
(I've changed the name of the parameter to avoid confusion):

int ReadFromFile( OneList *List[] );

Now, what is the type of this parameter? It is "Array of
pointers to OneList objects," which we can diagram as

+-----------+-----------+-----------+-
| List[0] | List[1] | List[2] | ...
+-----------+-----------|-----------+-
| | |
V | V
+---------+ V +---------+
| OneList | +---------+ | OneList |
+---------+ | OneList | +---------+
+---------+

.... where the three `OneList's could reside anywhere in memory
at all, not necessarily in a single array. This doesn't look
at all like the actual layout of `BigList', does it? The most
important difference is that `BigList' contains no `OneList'
pointers: it contains `OneList' *objects*, which are quite
different. The function is expecting to receive an array of
pointers, and when you pass it something else the merde hits
the MixMaster.
 
P

Peter B. Steiger

Also, note your declaration of "char **Words;" is
a "pointer to pointer to char", usually used in the context of an array of
pointers to char(s). This is okay. Think of the second arg to `main()',
"char **argv".

Right, this is what I'm trying to accomplish. I think I may have thrown
you off by (a) omitting large chunks of code and only posting snippets
that refer to the part that's giving me trouble and (2) probably using the
wrong terminology. By "static array" I mean the array of three OneList
elements is fixed in size, not dynamically allocated. But what I want is
for each of those three structures to contain a dynamically allocated list
of strings - indeed, exactly as **argv works. See my reply to Eric Sosman
in this thread for a better explanation of how I perceive the pointers
within the structure to work.
On the down side, you haven't initialized _any_ of the items in/of
"BigList". If "BigList" is an auto variable, then it most certainly
contains garbage (even if the garbage just happens to be interesting
values). I'm assuming you've initialized it somewhere with -
memset(BigList, '\0', sizeof (BigList));

Actually at this point the initialization part is failing just as much as
the read-from-a-file part. In the function InitLists, I have tried
void InitLists( OneList *BigList)
void InitLists( OneList *BigList[] )
void InitLists( OneList BigList[] )
and
void InitLists( OneList BigList[ 3 ] )

In theory, it should set BigList[ n ].wordcount to zero and BigList[ n
].wordlist to NULL for each of BigList[ 0.. 2 ], but I get a segfault when
I try to assign anything to BigList[ n ].wordlist, and when I print out
diagnostics showing me the address of various elements of BigList, they
are nothing like the addresses allocated when the array was delcared in
main(). I'm sure my misunderstanding of how C uses array offsets vs.
pointer offsets is behind the whole mess, but the FAQ didn't address (no
pun intended) my confusion.
As luck would have it, this _appeared_ to work for you. But it is WRONG.
You want to pass in "List->Words" to `realloc()' -

newlistsize
= realloc( List->Words, (List->wordcount + 1) * sizeof( newword ) );

My bad, I mistyped... you're right, that should have been List->Words.
Actually, you _really_ don't care 'bout "newlistsize"; It's only a
temporary container. I'd declare it as -

void *newlistsize;



If we succeeded, then solidify it...

List->Words = newlistsize;

Right, and that works fine if main() just sends one of the three list
structures to the read-from-a-file routine. What fails is if I sent the
pointer to the whole BigList array of three OneList structures, and try to
realloc() BigList[ 1 ]->Words.

This works:
int main( void )
{
OneList BigList[ 3 ];
char *garbage;
char *newword;

BigList[ 1 ].Words = NULL;
garbage = realloc( BigList[ 1 ].Words, 5 * sizeof( newword ) );
return 0;
}

This doesn't:

int StuffList( OneList BigList[ 3 ] )
{
char *garbage, *newword;
BigList[ 1 ].Words = NULL; /* segfault here */
garbage = realloc( BigList[ 1 ].Words, 5 * sizeof( newword ) ); /*
segfault here if I comment out previous line */
}
List = newlistsize; /* *** BAD BAD BAD *** */
List->wordcount++;
List->Words[ wordcount ] = newword; /* How did this line compile?

<blush> OK, I've learned my lesson - I hereby vow never to retype code
snippets by hand as an example; I'll always cut-and-paste from the
original source. That's not at all what I have in the program!
The `strdup()' function is a cleaner
way to do what you wanted (it performs `malloc()', etc.) AND copies the
string - something your snippet DID NOT do.

Well, that's where the newword = malloc() stuff came in, but I agree my
method was sloppy. I was trying to keep the "allocate memory for a
string" and "allocate memory for an array of pointers to the strings"
concepts separate. My idea was to perform error checking at each step of
the way - first read in a new word from the file; if that works, resize
the array of pointers to make room for a new pointer; if that works,
allocate memory for the new word (currently in a temp buffer); if that
works, assign the new word's pointer to the new element in the pointers
array.
I'm going to stop here and let that digest.
You should be able to take it from here with the above.

I have made a mess of this question by posting mistyped fragments and
omitting things I do know how to do (like return a result from a
function declared type int)... I think I should start over by posting the
whole thing - it's only a hundred lines give or take a few. Y'all are
certainly being patient with me, but this is a humbling experience for
someone who considers himself an experienced coder in the dead art of
Clipper.

Oh, and to answer your question - no, it's not AIX... it's a linux box,
built from source, but I'm trying to keep the whole thing ANSI so it can
compile on both linux and Wind**s without modification.
 
S

Stephen L.

Peter B. Steiger said:
Also, note your declaration of "char **Words;" is
a "pointer to pointer to char", usually used in the context of an array of
pointers to char(s). This is okay. Think of the second arg to `main()',
"char **argv".

Right, this is what I'm trying to accomplish. I think I may have thrown
you off by (a) omitting large chunks of code and only posting snippets
that refer to the part that's giving me trouble and (2) probably using the
wrong terminology. By "static array" I mean the array of three OneList
elements is fixed in size, not dynamically allocated. But what I want is
for each of those three structures to contain a dynamically allocated list
of strings - indeed, exactly as **argv works. See my reply to Eric Sosman
in this thread for a better explanation of how I perceive the pointers
within the structure to work.
On the down side, you haven't initialized _any_ of the items in/of
"BigList". If "BigList" is an auto variable, then it most certainly
contains garbage (even if the garbage just happens to be interesting
values). I'm assuming you've initialized it somewhere with -
memset(BigList, '\0', sizeof (BigList));

Actually at this point the initialization part is failing just as much as
the read-from-a-file part. In the function InitLists, I have tried
void InitLists( OneList *BigList)
void InitLists( OneList *BigList[] )
void InitLists( OneList BigList[] )
and
void InitLists( OneList BigList[ 3 ] )

In theory, it should set BigList[ n ].wordcount to zero and BigList[ n
].wordlist to NULL for each of BigList[ 0.. 2 ], but I get a segfault when
I try to assign anything to BigList[ n ].wordlist, and when I print out
diagnostics showing me the address of various elements of BigList, they
are nothing like the addresses allocated when the array was delcared in
main(). I'm sure my misunderstanding of how C uses array offsets vs.
pointer offsets is behind the whole mess, but the FAQ didn't address (no
pun intended) my confusion.

No, I really meant ->

memset(BigList, '\0', sizeof (BigList));

was all you needed to accomplish your initialization. Really...

If you HAVE TO HAVE an initialization _function_, that
function needs to know how to find the start of
the item to initialize, and its integral size.
I think (guess) that part of the confusion is that
`C' does not maintain _context_ about arrays. When
an array is used as an argument to a function, it
decays to a type pointer to the 1st element. The _size_
is lost in the scope of the receiving function (there are
people who can express this more elegantly, I'm sure).

So, your initialization function would have to,
in some way, model the how the `memset()' function
works. An example -

static void
InitLists(void *vp, int its_size)
{
memset(vp, '\0', its_size);
}

and you'd call it like ->

InitLists(BigList, sizeof (BigList));
or ->
InitLists(&BigList[ 0 ], sizeof (BigList));

As luck would have it, this _appeared_ to work for you. But it is WRONG.
You want to pass in "List->Words" to `realloc()' -

newlistsize
= realloc( List->Words, (List->wordcount + 1) * sizeof( newword ) );

My bad, I mistyped... you're right, that should have been List->Words.
Actually, you _really_ don't care 'bout "newlistsize"; It's only a
temporary container. I'd declare it as -

void *newlistsize;



If we succeeded, then solidify it...

List->Words = newlistsize;

Right, and that works fine if main() just sends one of the three list
structures to the read-from-a-file routine. What fails is if I sent the
pointer to the whole BigList array of three OneList structures, and try to
realloc() BigList[ 1 ]->Words.

This works:
int main( void )
{
OneList BigList[ 3 ];
char *garbage;
char *newword;

BigList[ 1 ].Words = NULL;
garbage = realloc( BigList[ 1 ].Words, 5 * sizeof( newword ) );
return 0;
}

This doesn't:

int StuffList( OneList BigList[ 3 ] )
{
char *garbage, *newword;
BigList[ 1 ].Words = NULL; /* segfault here */
garbage = realloc( BigList[ 1 ].Words, 5 * sizeof( newword ) ); /*
segfault here if I comment out previous line */
}
List = newlistsize; /* *** BAD BAD BAD *** */
List->wordcount++;
List->Words[ wordcount ] = newword; /* How did this line compile?

<blush> OK, I've learned my lesson - I hereby vow never to retype code
snippets by hand as an example; I'll always cut-and-paste from the
original source. That's not at all what I have in the program!
The `strdup()' function is a cleaner
way to do what you wanted (it performs `malloc()', etc.) AND copies the
string - something your snippet DID NOT do.

Well, that's where the newword = malloc() stuff came in, but I agree my
method was sloppy. I was trying to keep the "allocate memory for a
string" and "allocate memory for an array of pointers to the strings"
concepts separate. My idea was to perform error checking at each step of
the way - first read in a new word from the file; if that works, resize
the array of pointers to make room for a new pointer; if that works,
allocate memory for the new word (currently in a temp buffer); if that
works, assign the new word's pointer to the new element in the pointers
array.
I'm going to stop here and let that digest.
You should be able to take it from here with the above.

I have made a mess of this question by posting mistyped fragments and
omitting things I do know how to do (like return a result from a
function declared type int)... I think I should start over by posting the
whole thing - it's only a hundred lines give or take a few. Y'all are
certainly being patient with me, but this is a humbling experience for
someone who considers himself an experienced coder in the dead art of
Clipper.

Oh, and to answer your question - no, it's not AIX... it's a linux box,
built from source, but I'm trying to keep the whole thing ANSI so it can
compile on both linux and Wind**s without modification.

That was an AIX pun - their hardware allows de-referencing
a NULL pointer with sometimes entertaining, but always
thrilling, results... :)

-------

Okay, I'm pretending there are no typos in
the original post... The `ReadFromFile()'
function reads exactly 1 line (using `fgets()')
and plops the word in `List' and updates
the word count for that `List'. It does
not _know_ `List' is the 1st of three `OneList'
items.

If you want `ReadFromFile()' to make a single
pass over the whole `BigList', again, you
need to pass the size of `BigList' to `ReadFromFile()'.

int ReadFromFile(FILE *MyFile, OneList *List, int size)
{
void *vp;
char buffer[ 100 ];


while (size--) {
fgets(buffer, sizeof( buffer ), MyFile );

vp = realloc( List->Words, (List->wordcount + 1) * sizeof( void * )
);
if (vp != NULL )
{
List->Words = vp;
List->Words[ List->wordcount++ ] = strdup(buffer);
}
else {
break;
}

List++; /* Here's where we advance to the next item in the array
*/
}

return (success_or_failure);
}

Note that the size calculation is a little
more explicit when you call the function -

ReadFromFile(MyFile, BigList, sizeof (BigList) / sizeof (BigList[ 0 ]));

I'm sure the above is not the only way, either...


Stephen
eM
 
P

Peter B. Steiger

This time I want to do it right - use a dynamic array that increases in
size with each word read from the file. A few test programs that make use
of **List and realloc( List, blah blah blah) worked fine but when I get to
the actual program things start falling apart.

With the help of the nice folks here and about 32,768 example programs and
tutorials in googlespace, I got it working. I see now that if you want
the contents of typedef struct N modified by another function, that
function should receive a pointer to the struct... but only if it's one
instance of the struct:

typedef struct foo
{
int this;
char *that;
} FOO;

int diddlestruct( FOO *whatever )
{
whatever->this = 1;
...
}

On the other keyboard, if you have an array of struct, you do NOT need to
pass the pointer to that array:

int diddlestructarray( FOO whatever[ 5 ] )
{
whatever[ 0 ].this = 123;
whatever[ 1 ].that = strdup( buffer );
...
}

That was the whole key to my confusion. Once I got the "send a pointer or
not" puzzle solved, the rest of my numerous bugs were easy to squash. I
can't imagine why anybody would be interested, but you're welcome to take
a look at the finished version here:
http://www.steigerfamily.com/pbs/storytitles.htm

It's a word game I wrote for my kids to suggest a story title when they
want to (or must) write a short story but can't think of a theme or title
to get started. Sort of a laxative for writer's block...
 
D

Dave Thompson

On Tue, 20 Apr 2004 00:54:18 -0600, "Peter B. Steiger"
With the help of the nice folks here and about 32,768 example programs and
tutorials in googlespace, I got it working. I see now that if you want
the contents of typedef struct N modified by another function, that
function should receive a pointer to the struct... but only if it's one
instance of the struct:

typedef struct foo <snipped> FOO;

int diddlestruct( FOO *whatever )
{
whatever->this = 1;
...
}
Right. This is actually true of any nonarray type; it's just that the
type you happened to be interested in is a struct.
On the other keyboard, if you have an array of struct, you do NOT need to
pass the pointer to that array:

int diddlestructarray( FOO whatever[ 5 ] )
{
whatever[ 0 ].this = 123;
whatever[ 1 ].that = strdup( buffer );
...
}
Actually you do, it just doesn't look like it.

When you declare a function parameter as an array it actually declares
a pointer; the array bound is ignored -- neither used within the
definition nor checked against calls -- and may (arguably should) be
omitted -- here FOO whatever []. Or you can just declare it as a
pointer directly, and some people prefer that.

When you subscript a pointer that points to the first element of an
array -- or actually any element such that the subscript is within the
remaining bound -- it accesses the specified array element. In fact
even when you subscript an actual array, what happens formally is that
the array is converted to a pointer and that pointer subscripted.

And when you write an array as an argument in a function call, or
indeed any expression except as the operand of unary & or sizeof, it
"decays" (is implicitly converted) into a pointer to its first element
-- exactly what is needed for subscripting.

So even though you don't write a & operator or pointer declaration,
you are in fact passing and using a pointer.

This is probably the single most unusual feature of C, and a
fundamental one, which is why FAQ section 6 is so long and important.

- David.Thompson1 at worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top