|
|
|
|
||||||
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
"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) <kst-u@mib.org> Nokia "We must do something. This is something. Therefore, we must do this." -- Antony Jay and Jonathan Lynn, "Yes Minister" |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
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/ |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#11 |
|
Messages: n/a
Hébergeur: |
In article <47a56367$0$4785$4fafbaef@reader4.news.tin.it>,
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 |
|
|
|
#12 |
|
Messages: n/a
Hébergeur: |
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[i] */ }; 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[i] */ }; 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. |
|
|
|
#13 |
|
Messages: n/a
Hébergeur: |
"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 |
|
|
|
#14 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#15 |
|
Messages: n/a
Hébergeur: |
In article <47a56367$0$4785$4fafbaef@reader4.news.tin.it>,
"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 |
|
|
|
#16 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#17 |
|
Messages: n/a
Hébergeur: |
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 s me refining the approach. Remo.D. |
|
|
|
#18 |
|
Messages: n/a
Hébergeur: |
"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. |
|
|
|
#19 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#20 |
|
Messages: n/a
Hébergeur: |
CBFalconer <cbfalconer@yahoo.com> 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. |
|
|
|
#21 |
|
Messages: n/a
Hébergeur: |
"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. IMO it's not a drawback really. The same goes with all standard library functions operating on strings which are free to assume that user gave them a valid string. > > - 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. There are plenty. For instance strerror() may (as far as I know) do such a thing. strtok() is another example of function that uses static buffer. > 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. If your function does not need to be reentrant (ie. you are not writing a multithread application this approach is (in my opinion) not bad and I'd choose that. However, if function needs to be reentrant I'd choose the "sprintf()" approach, ie. user supplies buffer. > Another drawback is that it consumes memory even if the function will > never be called. Come one... 64 pointers? That's not that much. > 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. As I've said -- if you don't care about reentrant functions and multithreading applications go ahead. But generally, I'd advice the first option as you get reentrant function with no costs. -- Best regards, _ _ .o. | Liege of Serenly Enlightened Majesty of o' \,=./ `o ..o | Computer Science, Michal "mina86" Nazarewicz (o o) ooo +--<mina86*tlen.pl>--<jid:mina86*jabber.org>--ooO--(_)--Ooo-- |
|
![]() |
| Outils de la discussion | |
|
|