Advice on how to return a list of values

Discussion in 'C Programming' started by Remo D., Feb 3, 2008.

  1. Remo D.

    Remo D. Guest

    Hi! I'm writing a function that returns an array of (at maximum) 64
    pointers to char. I have thought of three possibility:

    - The caller passes a pointer to a previously allocated array of 64
    pointers. Similarly to sprintf(), the caller is entirely responsible for
    handling the memory. The drawback is that my function will have to rely
    on the correctness of such pointer to work properly.

    - My function allocates the array and returns it. This is similar to
    strdup(). Here I will have to rely on the caller function to properly
    free the array, which is something I'm not very comfortable with.

    - I'll have a "static char *ret[64]" in my function and will return
    ret. I've not been able to think of a library function that behaves this
    way, so I guess it's not reccomended. The good is that I'm free from
    allocation/freeing problem. The bad is that the return values will be
    overwritten at each call; if the user wants to keep the return values
    for subsequent use he has to copy and store them somewhere. Another
    drawback is that it consumes memory even if the function will never be
    called.

    In my specific case, I was leaning toward the third option but I'd like
    to hear your opinion on pitfalls, things that I've missed or alternative
    approaches that could work better.

    Thanks,
    Remo.D.
    Remo D., Feb 3, 2008
    #1
    1. Advertising

  2. Remo D.

    Ian Collins Guest

    Remo D. wrote:
    > Hi! I'm writing a function that returns an array of (at maximum) 64
    > pointers to char. I have thought of three possibility:
    >
    > - The caller passes a pointer to a previously allocated array of 64
    > pointers. Similarly to sprintf(), the caller is entirely responsible for
    > handling the memory. The drawback is that my function will have to rely
    > on the correctness of such pointer to work properly.
    >
    > - My function allocates the array and returns it. This is similar to
    > strdup(). Here I will have to rely on the caller function to properly
    > free the array, which is something I'm not very comfortable with.
    >
    > - I'll have a "static char *ret[64]" in my function and will return
    > ret. I've not been able to think of a library function that behaves this
    > way, so I guess it's not reccomended. The good is that I'm free from
    > allocation/freeing problem. The bad is that the return values will be
    > overwritten at each call; if the user wants to keep the return values
    > for subsequent use he has to copy and store them somewhere. Another
    > drawback is that it consumes memory even if the function will never be
    > called.
    >
    > In my specific case, I was leaning toward the third option but I'd like
    > to hear your opinion on pitfalls, things that I've missed or alternative
    > approaches that could work better.
    >

    The first would be my choice.

    There are standard C (time functions for example) and many POISX
    networking functions return a pointer to shared internal data. The
    biggest drawback with this is it can cause chaos in threaded
    applications. So they (POSIX) also provide another version which accept
    the data to be written as a parameter.

    --
    Ian Collins.
    Ian Collins, Feb 3, 2008
    #2
    1. Advertising

  3. Remo D.

    Remo D. Guest

    Ian Collins ha scritto:
    > Remo D. wrote:
    >> Hi! I'm writing a function that returns an array of (at maximum) 64
    >> pointers to char. I have thought of three possibility:
    >>
    >> - The caller passes a pointer to a previously allocated array of 64
    >> pointers. [...]
    >>
    >> - My function allocates the array and returns it. [...]
    >>
    >> - I'll have a "static char *ret[64]" in my function and will return
    >> ret. I've not been able to think of a library function that behaves this
    >> way, so I guess it's not reccomended. [...]
    >>


    > The first would be my choice.
    >
    > There are standard C (time functions for example) and many POISX
    > networking functions return a pointer to shared internal data. The
    > biggest drawback with this is it can cause chaos in threaded
    > applications. [...]



    Thanks Ian, that's a very good reason not to go with the third option: I
    don't know if my function will be used in a threaded application!

    I'll go with the first one.

    Thanks again,
    Remo.D.
    Remo D., Feb 3, 2008
    #3
  4. "Remo D." <rdentato> writes:
    > Hi! I'm writing a function that returns an array of (at maximum) 64
    > pointers to char. I have thought of three possibility:
    >
    > - The caller passes a pointer to a previously allocated array of 64
    > pointers. Similarly to sprintf(), the caller is entirely responsible
    > for handling the memory. The drawback is that my function will have to
    > rely on the correctness of such pointer to work properly.


    That's probably ok if you clearly document the requirement for the
    caller.

    > - My function allocates the array and returns it. This is similar to
    > strdup(). Here I will have to rely on the caller function to properly
    > free the array, which is something I'm not very comfortable with.


    Again, document the requirement. It does make your function a little
    harder to use, but not impossibly so.

    > - I'll have a "static char *ret[64]" in my function and will return
    > ret. I've not been able to think of a library function that behaves
    > this way, so I guess it's not reccomended. The good is that I'm free
    > from allocation/freeing problem. The bad is that the return values
    > will be overwritten at each call; if the user wants to keep the return
    > values for subsequent use he has to copy and store them somewhere.
    > Another drawback is that it consumes memory even if the function will
    > never be called.


    Several functions in the standard library return pointers to static
    objects: setlocale, localeconv, getenv, strerror, asctime, ctime (I
    don't know whether that's a complete list).

    > In my specific case, I was leaning toward the third option but I'd
    > like to hear your opinion on pitfalls, things that I've missed or
    > alternative approaches that could work better.


    Since your list is limited to 64 elements, you could return a
    structure containing 64 pointers and an integer that specifies how
    many of them are valid. Since structures are passed and returned by
    value [*], this is simpler, but it could be inefficient both in time
    (copying the data rather than passing a pointer to it) and in space
    (since you allocate the entire structure even if fewer than 64
    elements are valid).

    [*] All types are passed and returned by value; the difference is,
    unlike for arrays, (a) you can declare a struct parameger, and (b)
    there's no implicit conversion to pointer for structs.

    --
    Keith Thompson (The_Other_Keith) <>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Feb 3, 2008
    #4
  5. Remo D.

    Paul Hsieh Guest

    On Feb 2, 10:47 pm, "Remo D." <rdentato> wrote:
    > Hi! I'm writing a function that returns an array of (at maximum) 64
    > pointers to char.


    What is the lifetime of the pointers? Are they simply already
    existing pointers or are they to be created by the function according
    to some restrictions?

    > [...] I have thought of three possibility:
    >
    > - The caller passes a pointer to a previously allocated array of 64
    > pointers. Similarly to sprintf(), the caller is entirely responsible for
    > handling the memory. The drawback is that my function will have to rely
    > on the correctness of such pointer to work properly.


    Well you can enforce type safety by passing in a pointer to a
    structure of 64 char * pointers. I don't suppose there is a way to
    prove that your caller is being correct. In Bstrlib I do it with a
    few consistency checks and provide a complete API for managing
    bstrings, so there's no reason for the bstring to be corrupted except
    by external incidental means. But either way, this way allows you to
    amortize your allocation costs by virtue of being intrinsically
    hoisted upwards.

    > - My function allocates the array and returns it. This is similar to
    > strdup(). Here I will have to rely on the caller function to properly
    > free the array, which is something I'm not very comfortable with.


    This is fine too, except it may introduce performance problems from
    allocation if its called very often. You can't reuse an allocation
    pool, for example.

    > - I'll have a "static char *ret[64]" in my function and will return
    > ret. I've not been able to think of a library function that behaves this
    > way, so I guess it's not reccomended.


    Are you kidding me? Look through the time functions in C. Its as if
    they could find no other way of solving this kind of problem. But of
    course, you've just prevented yourself from having even *two* such
    live lists in your program (let's not even start with
    multithreading). If this is even possible then its hard to justify
    even writing a function routine to do this. Why not just inline it
    straight into your code?

    > [...] The good is that I'm free from allocation/freeing problem.


    The first solution is a superset of functionality versus this
    solution. It can declare (static char *list [64])'s as well. In fact
    it might even declare them non-static and make many of them.

    > [...] The bad is that the return values will be
    > overwritten at each call; if the user wants to keep the return values
    > for subsequent use he has to copy and store them somewhere. Another
    > drawback is that it consumes memory even if the function will never be
    > called.
    >
    > In my specific case, I was leaning toward the third option but I'd like
    > to hear your opinion on pitfalls, things that I've missed or alternative
    > approaches that could work better.


    Well, there is the iterator function solution. I.e.:

    enum itstate { LIST_START, LIST_ITERATE, LIST_DONE };
    char * getListNext (void * ctxParms, enum itstate *);

    You would just call getListNext() over and over to get each list
    element one at a time until your itstate is LIST_DONE (or the char *
    returned is NULL.) In some cases you can simplify this to just use the
    char * in place of an enum itstate. I.e., since you are going to
    treat the thing as a list anyways, rather than requiring a specific
    separate creation step, you could create it on the first pass. If you
    intend to walk it more than once, you can allocate an array to cache
    it from the call site.

    --
    Paul Hsieh
    http://www.pobox.com/~qed/
    http://bstring.sf.net/
    Paul Hsieh, Feb 3, 2008
    #5
  6. Remo D.

    Remo D. Guest

    Keith Thompson ha scritto:
    > "Remo D." <rdentato> writes:
    >> Hi! I'm writing a function that returns an array of (at maximum) 64
    >> pointers to char. I have thought of three possibility:
    >> [...]
    >> In my specific case, I was leaning toward the third option but I'd
    >> like to hear your opinion on pitfalls, things that I've missed or
    >> alternative approaches that could work better.

    >
    > Since your list is limited to 64 elements, you could return a
    > structure containing 64 pointers and an integer that specifies how
    > many of them are valid. Since structures are passed and returned by
    > value [*], this is simpler, but it could be inefficient both in time
    > [...] and in space [...]


    Thanks for the suggestion Keith. That's surely another option I didn't
    consider. Acutally I've never felt comfortable in returning structs and,
    as you point out, it seems to imply a level of inefficiency that is not
    entirely balanced by the benefits.

    I think I'll implement the first option I had and reconsider your
    suggestion if I will decide to provide an interface that requires no
    pointer passing from the caller to my function.

    Remo.
    Remo D., Feb 3, 2008
    #6
  7. Remo D.

    Remo D. Guest

    Paul Hsieh ha scritto:
    > On Feb 2, 10:47 pm, "Remo D." <rdentato> wrote:
    >> Hi! I'm writing a function that returns an array of (at maximum) 64
    >> pointers to char.

    >
    > What is the lifetime of the pointers?

    Pointers returned are pre-existing and created outside my function.

    >
    >> [...] I have thought of three possibility:
    >>
    >> - The caller passes a pointer to a previously allocated array of 64
    >> pointers. [...]

    >
    > Well you can enforce type safety by passing in a pointer to a
    > structure of 64 char * pointers. I don't suppose there is a way to
    > prove that your caller is being correct. [...]



    >> - I'll have a "static char *ret[64]" in my function and will return
    >> ret. I've not been able to think of a library function that behaves this
    >> way, so I guess it's not reccomended.

    >
    > Are you kidding me? Look through the time functions in C. Its as if
    > they could find no other way of solving this kind of problem.

    Also Ian and Keith mention library functions that return pointer to
    internal static variables. I guess this proves my lack of knowledge of
    the library function :).

    > But of course, you've just prevented yourself from having even
    > *two* such live lists in your program (let's not even start with
    > multithreading).

    Yes, this is the major issue, I think. As you suggest later, I'll
    implement the first option and reconsider allocating the array
    statically (or returning a struct) at a later stage.

    [...]

    > Well, there is the iterator function solution.

    I see, but I already know the caller will want use the return array as
    an arry (e.g. if (ret[k][0] > ret[j][0]) ) it will be hard to justify
    an iterator approach. Thanks for the suggestion anyway.


    Remo.D
    Remo D., Feb 3, 2008
    #7
  8. Remo D.

    pete Guest

    Remo D. wrote:
    >
    > Ian Collins ha scritto:
    > > Remo D. wrote:
    > >> Hi! I'm writing a function that returns an array of (at maximum) 64
    > >> pointers to char. I have thought of three possibility:
    > >>
    > >> - The caller passes a pointer to a previously allocated array of 64
    > >> pointers. [...]
    > >>
    > >> - My function allocates the array and returns it. [...]
    > >>
    > >> - I'll have a "static char *ret[64]"
    > >> in my function and will return
    > >> ret. I've not been able to think of a library function
    > >> that behaves this
    > >> way, so I guess it's not reccomended. [...]
    > >>

    >
    > > The first would be my choice.
    > >
    > > There are standard C (time functions for example) and many POISX
    > > networking functions return a pointer to shared internal data. The
    > > biggest drawback with this is it can cause chaos in threaded
    > > applications. [...]

    >
    > Thanks Ian, that's a very good reason
    > not to go with the third option: I
    > don't know if my function will be used in a threaded application!
    >
    > I'll go with the first one.


    The first one is much more robust than the others.
    With the first one,
    you can use either automatic, static, or allocated memory
    at the discretion of the calling function.

    --
    pete
    pete, Feb 3, 2008
    #8
  9. Remo D.

    Bartc Guest

    Remo D. wrote:
    > Hi! I'm writing a function that returns an array of (at maximum) 64
    > pointers to char. I have thought of three possibility:
    >
    > - The caller passes a pointer to a previously allocated array of 64
    > pointers. Similarly to sprintf(), the caller is entirely responsible
    > for handling the memory. The drawback is that my function will have
    > to rely on the correctness of such pointer to work properly.


    Let the caller provide a pointer to an array of 64 *unassigned* and NULL
    pointers. Perhaps also a number to indicate how many of the 64 have been
    provided, if there could be any doubt. The array could be static or
    allocated; it doesn't matter and it's not your problem.

    Then allocate each pointer in your code and store in the array. Presumably
    the caller doesn't know how long these strings are so could not preallocate
    anyway.

    The caller would have to free the strings when it's done with it. That
    requires no knowledge on behalf of the caller beyond that it has a list of
    64 pointers to free. Or provide an extra function (or extra option on your
    one function) to clean up.

    Your idea to return a pointer to a static block of pointers in your code is
    full of pitfalls, unless you know your function will only be called once in
    the caller's code. More than once, the block will get overwritten -- unless
    your function will always put the same values there each call.


    --
    Bart
    Bartc, Feb 3, 2008
    #9
  10. The usual method goes like this:

    The caller passes a pointer to storage that it provides, and the size
    of available storage. The called function provides the data, and
    returns how much data is available. If the pointer passed in is a null
    pointer then the callee just provides the amount of data available,
    without calling anything. If the pointer passed is not null, but the
    size is not enough then the callee doesn't write more data than
    storage available.

    That makes it quite clear what the callee has to do. For the caller,
    there are two strategies: If you know an amount of data that is
    usually enough, then do a call with a limited amount of local data
    first; if this fails then the caller will know how much storage is
    needed and will malloc it. Alternatively, if there is no reasonable
    guess about the data size, pass a null pointer in the first call, then
    make a second call with the storage properly allocated.
    christian.bau, Feb 3, 2008
    #10
  11. In article <47a56367$0$4785$>,
    Remo D. <rdentato> wrote:

    >Hi! I'm writing a function that returns an array of (at maximum) 64
    >pointers to char. I have thought of three possibility:


    >- The caller passes a pointer to a previously allocated array of 64
    >pointers. Similarly to sprintf(), the caller is entirely responsible for
    >handling the memory. The drawback is that my function will have to rely
    >on the correctness of such pointer to work properly.


    A problem with this method is that it relies on the fact that you will
    return at most 64 pointers. Why 64? Might you want to increase the
    number in the future? If so you will have to add a new function, so
    that old code that passes only 64 does not break.

    Is the 64 a real constant of the problem, or is it just something like
    the 64 best results? If the latter, I suggest you allow the caller to
    provide an array along with its size, and you fill in the appropriate
    number of values.

    >- My function allocates the array and returns it. This is similar to
    >strdup().


    This is the obvious solution if the number of values varies. Even
    with a maximum of 64, it might be wasteful to allocate space for 64
    every time, regardless of how many are really needed.

    The overhead of malloc() is likely to be small if you are dealing with
    as many as 64 pointers.

    >Here I will have to rely on the caller function to properly
    >free the array, which is something I'm not very comfortable with.


    Whatever interface you provide, you'll have to rely on the caller
    to use it properly. Calling free() or a similar function should
    not be beyond your users' capabilities :)

    As others have pointed out, your third alternative causes problems if
    you might want two sets of results at the same time, and can be
    particularly frustrating (and hard to track down) if someone uses your
    code in a multi-threaded program.

    -- Richard
    --
    :wq
    Richard Tobin, Feb 3, 2008
    #11
  12. On Sun, 03 Feb 2008 07:47:21 +0100, "Remo D." <rdentato> wrote:

    >Hi! I'm writing a function that returns an array of (at maximum) 64
    >pointers to char. I have thought of three possibility:
    >
    >- The caller passes a pointer to a previously allocated array of 64
    >pointers. Similarly to sprintf(), the caller is entirely responsible for
    >handling the memory. The drawback is that my function will have to rely
    >on the correctness of such pointer to work properly.
    >
    >- My function allocates the array and returns it. This is similar to
    >strdup(). Here I will have to rely on the caller function to properly
    >free the array, which is something I'm not very comfortable with.
    >
    >- I'll have a "static char *ret[64]" in my function and will return
    >ret. I've not been able to think of a library function that behaves this
    >way, so I guess it's not reccomended. The good is that I'm free from
    >allocation/freeing problem. The bad is that the return values will be
    >overwritten at each call; if the user wants to keep the return values
    >for subsequent use he has to copy and store them somewhere. Another
    >drawback is that it consumes memory even if the function will never be
    >called.
    >
    >In my specific case, I was leaning toward the third option but I'd like
    >to hear your opinion on pitfalls, things that I've missed or alternative
    >approaches that could work better.


    There is no single best answer - what to do depends upon how your
    function is to be used and what kind of tradeoffs you want to
    make.

    Here are some questions to think about:

    (1) Is the function a general service routine that can be called
    from many places or is it only used in one application? In other
    words, is it idiosyncratic? If it is a general service routine
    you had best (IMNSHO) make it thread safe. If it is
    idiosyncratic you can take certain liberties.

    (2) Does it have to be recursion-safe and/or thread-safe? Your
    option three is very dangerous. Here is what can happen:

    while (some_condition) {
    data = your_function();
    do_something_clever();
    process(data);
    }

    If do_something_clever calls your_function the ball game is over;
    data has been scribbled on.

    (3) Will the function be called sporadically or will it be called
    in a loop? If it is in a loop you can reuse the data space from
    the previous iteration, e.g.,

    T * data = 0;
    ...
    /* Optionally allocate space for data here */
    while (more_to_do) {
    data = your_function(data);
    ...
    }
    free(data);

    Notice that we pass in the data pointer and return it; this is a
    fairly common technique. Now what you can do is allocate space
    within your_function if the data pointer is null. As a further
    refinement, if the loop normally terminates with a no more data
    condition you can use this code

    T * data = 0;
    ...
    while (data = your_function(data)) {
    ...
    }

    In this case your_function frees the space and returns a null
    pointer when it reaches termination. It reallocates space as
    needed. This kind of usage pattern is a special case but it is
    quite common.

    A caveat is that if a instance of the data has a greater life
    time than the loop cycle you will need to make a copy of it.

    Incidentally passing in the data pointer and returning it is good
    form; it preserves flexibility.

    (4) Is the data being returned bounded in space or unbounded?
    If bounded, is the data expected to be much smaller than the
    bound? If the data has a reasonable space bound then you may as
    well allocate that much space and be done with it. Otherwise you
    will have an error condition to deal with. When the data space
    is bounded it is simpler to let the caller provide it because it
    can then come from automatic storage. Thus in your case we might
    do

    char * data_array[64];
    ...
    your_function(data_array);

    Here we don't have to allocate and deallocate the data space; it
    is handled cheaply and automatically for us. If, however, the
    data array were very large we might not want to do this; the
    stack (automatic storage) is small compared to the size of the
    heap (allocatable storage).

    (5) When the data size can be variable do we do automatic
    resizing or user controlled resizing? User controlled resizing
    is "safer" but more cumbersome; it requires extra code and
    decisions on the calling side. Automatic resizing is more
    convenient but more prone to bugs, e.g., dangling pointers and
    erroneous deallocations.

    (6) How do we determine the end of data when it can be variable
    sized? There are two basic ways to do this; on is to return
    (somehow) the size, and the other is to put a sentinel value at
    the end of the data, typically some kind of null value. There
    are arguments for each choice. Sometimes it does matter; most of
    the time it is a matter of preferred style. However the choice
    does impact your function's API and how you allocate storage.

    If you go the sentinel route you have to allocate extra space for
    the sentinel. Thus, in your case, you would say

    char * data_array[65];

    or, more generally,

    #define NDATA 64
    ...
    data_array[NDATA+1];

    On the other hand, if you go the "return size of data" you have
    the problem of how to return it. The problem is that C has no
    good way to return two distinct things. What you have is a
    choice of hacks and kludges. Here are some:

    (a) You can pass the address of the size in the calling
    sequence, e.g.,

    data = your_function(data, &size);

    This works and is compact, which is about the best that can be
    said for it. Function design is cleaner if the inputs come in
    through the calling sequence and outputs are returned and never
    the twain are confused.

    (b) We can turn (a) around and return the size. In this case
    the call looks like:

    size = your_function(data);

    This form can be quite convenient if we have a loop that
    terminates when we run out of data and if the data size is
    bounded. This time the code looks like:

    char *data[NDATA];
    ...
    while (your_function(data) > 0) {
    ...
    }

    (c) You can return a structure that holds both the data and its
    size. For example:

    struct T_descr {
    char *data[NDATA];
    int size;
    };
    ...
    T_descr descr;
    ...
    descr = your_function(&descr);
    for (i = 0; i< descr.size;i++) {
    /* do stuff with descr.data */
    };

    This makes the code more cumbersome; however it treats the data
    as an object with properties, which may be a better way to handle
    the data.

    (d) You can pass a structure that holds the data and its size
    but return the data. This looks slightly different:

    struct T_descr {
    char *data[NDATA+1];
    int size;
    };
    ...
    T_descr descr;
    char ** data;
    ...
    data = your_function(&descr);
    for (i = 0; i< descr.size;i++) {
    /* do stuff with data */
    };

    Ordinarily data would point to descr.data but could be set to
    null on no data. An advantage of this form is that we can, so to
    speak, have our cake and eat it too. That is, we can put in a
    sentinel and use either sentinel based code or index based code,
    depending on which is more convenient.

    (e) An "industrial grade" version of (d) hides information in the
    structure, e.g.,

    struct T_descr {
    void * private;
    int size;
    };
    ...
    T_descr descr = {0,0};
    char ** data;
    ...
    data = your_function(&descr);

    The point of this form is that your_function can handle
    allocation and deallocation behind the scenes and the user does
    not have to do anything about. Within your_function private
    points to a structure that holds data that is held from one call
    to the next.

    My apologies if this is a bit on the long winded size; however I
    thought it worthwhile to go through some of the alternatives and
    issues.
    Richard Harter, Feb 3, 2008
    #12
  13. Remo D.

    CBFalconer Guest

    "Remo D." wrote:
    >
    > Hi! I'm writing a function that returns an array of (at maximum)
    > 64 pointers to char. I have thought of three possibility:
    >
    > - The caller passes a pointer to a previously allocated array of
    > 64 pointers. Similarly to sprintf(), the caller is entirely
    > responsible for handling the memory. The drawback is that my
    > function will have to rely on the correctness of such pointer to
    > work properly.
    >
    > - My function allocates the array and returns it. This is similar
    > to strdup(). Here I will have to rely on the caller function to
    > properly free the array, which is something I'm not very
    > comfortable with.
    >
    > - I'll have a "static char *ret[64]" in my function and will
    > return ret. I've not been able to think of a library function
    > that behaves this way, so I guess it's not reccomended. The good
    > is that I'm free from allocation/freeing problem. The bad is
    > that the return values will be overwritten at each call; if the
    > user wants to keep the return values for subsequent use he has
    > to copy and store them somewhere. Another drawback is that it
    > consumes memory even if the function will never be called.


    I would use the 'strdup like' option. My reason is that this is
    the only means that makes the function completely independent,
    re-entrant, and reusable anywhere. You document that the assigned
    array needs eventual freeing, and there are no more worries.
    Everything else can be misused.

    --
    [mail]: Chuck F (cbfalconer at maineline dot net)
    [page]: <http://cbfalconer.home.att.net>
    Try the download section.


    --
    Posted via a free Usenet account from http://www.teranews.com
    CBFalconer, Feb 4, 2008
    #13
  14. Remo D.

    Ian Collins Guest

    CBFalconer wrote:
    > "Remo D." wrote:
    >> Hi! I'm writing a function that returns an array of (at maximum)
    >> 64 pointers to char. I have thought of three possibility:
    >>
    >> - The caller passes a pointer to a previously allocated array of
    >> 64 pointers. Similarly to sprintf(), the caller is entirely
    >> responsible for handling the memory. The drawback is that my
    >> function will have to rely on the correctness of such pointer to
    >> work properly.
    >>
    >> - My function allocates the array and returns it. This is similar
    >> to strdup(). Here I will have to rely on the caller function to
    >> properly free the array, which is something I'm not very
    >> comfortable with.
    >>

    >
    > I would use the 'strdup like' option. My reason is that this is
    > the only means that makes the function completely independent,
    > re-entrant, and reusable anywhere. You document that the assigned
    > array needs eventual freeing, and there are no more worries.
    > Everything else can be misused.
    >

    The main drawback of that approach over the first option is it forces
    the user to use dynamic memory.

    --
    Ian Collins.
    Ian Collins, Feb 4, 2008
    #14
  15. Remo D.

    Tim Smith Guest

    In article <47a56367$0$4785$>,
    "Remo D." <rdentato> wrote:
    > - My function allocates the array and returns it. This is similar to
    > strdup(). Here I will have to rely on the caller function to properly
    > free the array, which is something I'm not very comfortable with.


    Use garbage collection and let the garbage collector deal with it.
    Here's a garbage collector for C:

    <http://www.hpl.hp.com/personal/Hans_Boehm/gc/>

    > - I'll have a "static char *ret[64]" in my function and will return
    > ret. I've not been able to think of a library function that behaves this
    > way, so I guess it's not reccomended. The good is that I'm free from
    > allocation/freeing problem. The bad is that the return values will be
    > overwritten at each call; if the user wants to keep the return values
    > for subsequent use he has to copy and store them somewhere. Another
    > drawback is that it consumes memory even if the function will never be
    > called.


    You mentioned that 64 is the largest size you'll need. What you might
    consider, if you go with the static approach, is to have more than 64
    available:

    static char * ret[1024];

    for example. Use that as a circular pool to return items from. So, the
    first caller gets &ret[0] returned. If he need 12 pointers returned,
    the next caller would get his pointers starting at &ret[12], and so on.

    Values still get overwritten, but it no longer will be on every call.
    If you can sufficiently analyze the need of the code that will call
    this, you may even be able to determine a pool size that will be able to
    delay overwriting long enough that callers will always be done by time
    their data is overwritten.

    --
    --Tim Smith
    Tim Smith, Feb 4, 2008
    #15
  16. Remo D.

    Ian Collins Guest

    Tim Smith wrote:
    >
    > Values still get overwritten, but it no longer will be on every call.
    > If you can sufficiently analyze the need of the code that will call
    > this, you may even be able to determine a pool size that will be able to
    > delay overwriting long enough that callers will always be done by time
    > their data is overwritten.
    >

    Until it gets used in a different context and the users start reporting
    bizarre data corruption problems.

    --
    Ian Collins.
    Ian Collins, Feb 4, 2008
    #16
  17. Remo D.

    Remo D. Guest

    Tim Smith ha scritto:
    > "Remo D." <rdentato> wrote:
    >> - My function allocates the array and returns it. This is similar to
    >> strdup(). Here I will have to rely on the caller function to properly
    >> free the array, which is something I'm not very comfortable with.

    >
    > Use garbage collection and let the garbage collector deal with it.
    > Here's a garbage collector for C:


    I'd love to use a Garbage Collector but I can't include in the project a
    dependency over another piece of software. Thanks for the suggestion,
    anyway.

    > You mentioned that 64 is the largest size you'll need. What you might
    > consider, if you go with the static approach, is to have more than 64
    > available:
    > [...]
    > Values still get overwritten, but it no longer will be on every call.
    > If you can sufficiently analyze the need of the code that will call
    > this, you may even be able to determine [...][an appropriate][...] pool
    > size [...]


    Unfortunately I don't have (nor I want to have) any control on how the
    caller will call my function.

    If I'll go to the static route (which is what I'm using for testing now,
    but will likely change in the future) it is reasonable that I specify
    that the returned values will only be valid between two subsequent
    calls. It will be up to the caller to save his own copy of the values
    if he needs them.

    Thanks everybody for this discussion, every message helps me refining
    the approach.

    Remo.D.
    Remo D., Feb 4, 2008
    #17
  18. Remo D.

    SM Ryan Guest

    "Remo D." <rdentato> wrote:

    # - The caller passes a pointer to a previously allocated array of 64

    # - My function allocates the array and returns it. This is similar to

    # - I'll have a "static char *ret[64]" in my function and will return

    ¥ You can pass a fixed size array in and out of function if you
    put it inside a struct.

    ¥ If the program only requires a few resources relative what you
    have on your system (for example a mere megabyte on a gigabyte vm),
    and the system recovers all resources on exit, malloc without freeing.
    The waste will not be signficant.

    ¥ÊGet a garbage collecting allocator. Allocate in the function
    and let the allocator free it when it is no longer accessible.

    --
    SM Ryan http://www.rawbw.com/~wyrmwif/
    You hate people.
    But I love gatherings. Isn't it ironic.
    SM Ryan, Feb 4, 2008
    #18
  19. Remo D.

    CBFalconer Guest

    SM Ryan wrote:
    > "Remo D." <rdentato> wrote:
    >
    >> - The caller passes a pointer to a previously allocated array of
    >> - My function allocates the array and returns it. This is similar
    >> - I'll have a "static char *ret[64]" in my function and will

    >
    > You can pass a fixed size array in and out of function if you
    > put it inside a struct.
    >
    > If the program only requires a few resources relative what you
    > have on your system (for example a mere megabyte on a gigabyte
    > vm), and the system recovers all resources on exit, malloc
    > without freeing. The waste will not be signficant.
    >
    > Get a garbage collecting allocator. Allocate in the function
    > and let the allocator free it when it is no longer accessible.


    Very good. Take a simple problem, add a gigabyte of code with
    various incompatibilities and bugs, thus avoiding a free() call.
    This is an ideal method of writing easily understandable and
    maintainable software, and should be recommended to all.
    Advertising it in a message with non-normal quote marks will
    enhance its viability. Brack.

    --
    [mail]: Chuck F (cbfalconer at maineline dot net)
    [page]: <http://cbfalconer.home.att.net>
    Try the download section.



    --
    Posted via a free Usenet account from http://www.teranews.com
    CBFalconer, Feb 4, 2008
    #19
  20. Remo D.

    SM Ryan Guest

    CBFalconer <> wrote:
    # SM Ryan wrote:
    # > "Remo D." <rdentato> wrote:
    # >
    # >> - The caller passes a pointer to a previously allocated array of
    # >> - My function allocates the array and returns it. This is similar
    # >> - I'll have a "static char *ret[64]" in my function and will
    # >
    # > You can pass a fixed size array in and out of function if you
    # > put it inside a struct.
    # >
    # > If the program only requires a few resources relative what you
    # > have on your system (for example a mere megabyte on a gigabyte
    # > vm), and the system recovers all resources on exit, malloc
    # > without freeing. The waste will not be signficant.
    # >
    # > Get a garbage collecting allocator. Allocate in the function
    # > and let the allocator free it when it is no longer accessible.
    #
    # Very good. Take a simple problem, add a gigabyte of code with

    Linking in libc is pretty much unavoidable.

    # various incompatibilities and bugs, thus avoiding a free() call.
    # This is an ideal method of writing easily understandable and
    # maintainable software, and should be recommended to all.

    Yes it is easier to understand and maintain. Thank you.

    --
    SM Ryan http://www.rawbw.com/~wyrmwif/
    A bunch of savages in this town.
    SM Ryan, Feb 5, 2008
    #20
    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. Asun Friere
    Replies:
    1
    Views:
    492
    Paul Boddie
    Aug 27, 2003
  2. Peter Hansen
    Replies:
    23
    Views:
    861
    Anton Vredegoor
    Sep 5, 2003
  3. dackz
    Replies:
    0
    Views:
    476
    dackz
    Feb 6, 2007
  4. Greenhorn
    Replies:
    15
    Views:
    801
    Keith Thompson
    Mar 6, 2005
  5. Chris Rebert
    Replies:
    1
    Views:
    672
    Bobby
    May 28, 2009
Loading...

Share This Page