Can a static array contain a dynamic array of pointers?

Discussion in 'C Programming' started by Peter B. Steiger, Apr 19, 2004.

  1. 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.

    --
    Peter B. Steiger
    Cheyenne, WY
    If you must reply by email, you can reach me by placing zeroes
    where you see stars: wypbs_**3 at bornagain.com.
     
    Peter B. Steiger, Apr 19, 2004
    #1
    1. Advertising

  2. Peter B. Steiger

    Eric Sosman Guest

    "Peter B. Steiger" wrote [ruthlessly snipped]:
    >
    > 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.

    --
     
    Eric Sosman, Apr 19, 2004
    #2
    1. Advertising

  3. On Mon, 19 Apr 2004 12:17:53 -0400, Eric Sosman sez:
    > 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.

    --
    Peter B. Steiger
    Cheyenne, WY
    If you must reply by email, you can reach me by placing zeroes
    where you see stars: wypbs_**3 at bornagain.com.
     
    Peter B. Steiger, Apr 19, 2004
    #3
  4. Peter B. Steiger

    Stephen L. Guest

    "Peter B. Steiger" wrote:
    >
    > 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
     
    Stephen L., Apr 19, 2004
    #4
  5. Peter B. Steiger

    Eric Sosman Guest

    "Peter B. Steiger" wrote:
    >
    > On Mon, 19 Apr 2004 12:17:53 -0400, Eric Sosman sez:
    > > 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.

    --
     
    Eric Sosman, Apr 19, 2004
    #5
  6. On Mon, 19 Apr 2004 13:40:15 -0400, Stephen L. sez:
    > 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.

    >> 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 ) );


    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 (newlistsize != NULL )
    >> {

    >
    > 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!
    </blush>

    > 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.

    --
    Peter B. Steiger
    Cheyenne, WY
    If you must reply by email, you can reach me by placing zeroes
    where you see stars: wypbs_**3 at bornagain.com.
     
    Peter B. Steiger, Apr 19, 2004
    #6
  7. Peter B. Steiger

    Stephen L. Guest

    "Peter B. Steiger" wrote:
    >
    > On Mon, 19 Apr 2004 13:40:15 -0400, Stephen L. sez:
    > > 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));


    >
    > >> 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 ) );

    >
    > 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 (newlistsize != NULL )
    > >> {

    > >
    > > 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!
    > </blush>
    >
    > > 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
     
    Stephen L., Apr 19, 2004
    #7
  8. On Mon, 19 Apr 2004 09:51:11 -0600, I said to myself:
    > 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...

    --
    Peter B. Steiger
    Cheyenne, WY
    If you must reply by email, you can reach me by placing zeroes
    where you see stars: wypbs_**3 at bornagain.com.
     
    Peter B. Steiger, Apr 20, 2004
    #8
  9. On Tue, 20 Apr 2004 00:54:18 -0600, "Peter B. Steiger"
    <> wrote:
    <snip>
    > 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
     
    Dave Thompson, Apr 26, 2004
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    7
    Views:
    952
    James Kanze
    Mar 20, 2009
  2. cerr

    pointers, pointers, pointers...

    cerr, Apr 7, 2011, in forum: C Programming
    Replies:
    12
    Views:
    682
  3. Roger Pack
    Replies:
    3
    Views:
    160
    Roger Pack
    Sep 28, 2010
  4. Jason Carlton
    Replies:
    11
    Views:
    248
    Dr J R Stockton
    Dec 8, 2009
  5. Replies:
    16
    Views:
    992
Loading...

Share This Page