tags:

views:

644

answers:

12

Okay i've seen this done somewhere before where you have a function that takes a pointer parameter and returns a pointer.

However you can choose not to pass a parameter and it will return a dynamically allocated pointer but if you do pass a pointer then it just fills it in instead of creating one on the heap. This is a single function not overloaded.

My question is how is this safely done?

I had a guess it was something like this:

point* funct(point *p = some_value)
{
 if p == some_value
   //create a point *p on the heap

 else
   //create use the given *p and fill it in

 return p
}

Now i can't think if this is right way to do it and if it is then what could be a good some_value? it can't be NULL because when you pass empty pointer it will also be NULL and not sure if it is safe to have it greater than 0. Also you can't have negative numbers on pointers either, so whats a good safe value?

Any good way to do this that is also PORTABLE across platforms?

EDIT:

Okay maybe i didn't explain properly basically i want the function to be used like this:

point *x;
point *y;

x = func();
func(y);

Not

x = func(NULL);

if I use NULL i get an error segmentation fault only when i do func(y);

The reason for this is:

either the user passes a pointer he manages such as one created on the stack OR the function will give a dynamic one back if none is given. I don't want to force the return of only dynamic memory or only accepting a pointer to fill.

I know I have seen this done somewhere before.

+8  A: 

Normal solution is to have 'if NULL allocate' - that's what 'c' realloc does.

It looks like you want to have a function that uses existing memory if provided or else allocates it's own. It's not clear what you should return if you are passed a valid pointer.
Should it return that pointer?
Or should it be null to ensure that there aren't two copies of pointers to the same bit of memory - which will cause problems if they are freed.

It might be safer to pass a **pointer into the function and require an argument - even if that arguement is a pointer to null.

Martin Beckett
actually thats exactly what I want to do but not with local pointer but allocate a new pointer and return that
iQ
+3  A: 

Well obviously if a someone ever was to call funct(NULL) then it would result in a crash anyway. so why not have some_value=NULL so to make

if(p==NULL){
  p=dynamic_memory;
}

where dynamic_memory is allocated in the constructor(not thread-safe!) or replace dynamic_memory with a call to new

Edit:

Or. If you must have 3 states in your function, one for if no argument is supplied, one for if a valid pointer is passed, and one for if NULL is passed, then you can use pointer-to-pointers.

like

void *func(void** p=NULL){
  if(p==NULL) ...//user supplied no argument
  if(*p==NULL) ...//user supplied NULL
  else //user supplied valid pointer

This doesn't seem to be what you want however and then people would have to pass pointers with '&' to your function.. (is &NULL even valid?)

Earlz
A: 

Default value is what it is — default value. There's no such thing like not passing value here. It may only happen with variable parameters count, but that's different story.

And what is an "empty pointer"?

Michael Krelin - hacker
+3  A: 

My guess would be more along the lines of:

point* funct(point *p = NULL)
{
    if (p == NULL) {
        // create a point *p on the heap
        // and use it
        return(p);
    }
    else {
        //use the given *p and fill it in
        return(NULL);
    }
 }

Not too sure about having the possibility of the pointer to your point object passed in though. Could be quite hard to check for, and checking the type of an object passed in, i.e. "looking under the covers" using RTTI, is not the best OO practice.

Rob Wells
yeah but you should also replace some_value with NULL, e.g., "point* funct(point *p = NULL)"
djeidot
@djeidot, done. Thanks for the suggestion (+1)
Rob Wells
that last return is missing something...
Evan Teran
@Evan, oops, you're right. +1
Rob Wells
+1  A: 

You should pass it NULL (have null be default...) if you want to allocate, and pass an empty pointer if you want to fill in.

As jeffamaphone commented, there is a difference between an empty pointer and NULL, use your conditional statements to check if it is an empty pointer or NULL

instanceofTom
how do you tell the difference from an empty pointer and a "filled" pointer though?
Earlz
what exactly is an empty pointer? don't pointers get defaulted to NULL when nothing is assigned?
iQ
iQ: NO. Local variables are uninitialized unless you initialize them.
jmucchiello
+2  A: 

something like this:

T* func(T* p) {
    if(!p) {
        p = new T;
    }
    // do whatever with p
    return p;
}

then you can either do:

T* x = func(NULL);
// whatever
delete x;

or:

T x;
func(&x);

EDIT:

There is a non-thread safe option which several libraries use. Basically it works like this:

T* func(T* p) {
    static T buf;
    if(!p) {
        p = buf;
    }
    // do whatever with p
    return p;
}

then you can either do:

T* p = func(NULL);

or:

T x;
T* p = func(&x);

There are often "reentrant" versions of these as well which are often tied to the non-thread safe versions like this:

T* func(T* p) {
    // behaves as above example, except now we can
    // use func_r in a thread safe way if we need to
    static T buf;
    return func_r(p, buf);
}

T* func_r(T* p, T *buf) {
    if(!p) {
        p = buf;
    }
    // do whatever with p
    return p;
}
Evan Teran
`!p` is not valid, as NULL is not always defined as 0.
Earlz
@earlz: incorrect, in c++ NULL *is* 0. 0 is the null pointer constant. See the standard section 4.10.
Evan Teran
to quote that section: A *null pointer constant* is an integral constant expression (5.19) rvalue of integer type that evaluates tozero. A null pointer constant can be converted to a pointer type; the result is the *null pointer value* of thattype and is distinguishable from every other value of pointer to object or pointer to function type."Note: there is a difference between the "null pointer constant" and the "null pointer value"
Evan Teran
I've made some edits on the question it should make things clear, but I'm trying to avoid that way
iQ
NULL pet-peeve: http://stackoverflow.com/questions/423823/whats-your-favorite-programmer-ignorance-pet-peeve/1331729#1331729
Johannes Schaub - litb
@litb: seems we keep encountering people who confuse this issue over and over again :-(.
Evan Teran
+1  A: 

What's the problem with using NULL? That's the general way this is handled. If you really need to distinguish between "caller passed nothing" and "caller passed NULL", then use 0xFFFFFFFF on a 32-bit system or 0xFFFFFFFFFFFFFFFF on a 64-bit system.

point * funct(point * p = (point *)(-1))
{
    if (p == (point *)(-1))
    {
        p = new point();
    }

    if (p == NULL)
    {
        // special case handling
        return NULL;
    }

    // fill in p
    return p
}

The '-1' cast will always be the maximum pointer value as long as you are on a two's-compliment architecture. Feel free to substitute the C-style cast with a reinterpret cast if you prefer.

Derek Park
There are architectures where the (point*)(-1) cast will cause a fault. This is not portable code. If you REALLY need to distinguish between NULL and some other non-pointer value, create a second parameter with a bool or enum type.
jmucchiello
yeah initially I had the -1 idea but it seems to be unfriendly, not sure if I should take the risk
iQ
On what architecture will casting -1 to a pointer cause a fault? Attempting to use such a pointer would obviously cause a fault, but I see no reason that creating the pointer would be problematic.
Derek Park
@Derek: I'm not sure on what architectures this will be a problem, but there may well be some. I'm not keen on advocating undefined behavior on the basis that it happens to work on all the machines I'm currently familiar with.
David Thornley
I have trouble envisioning a practical architecture on which this is a problem. This being a problem implies that the system is checking for the validity of the pointer, which is an expensive operation (given that it has to ask the OS if it's valid) with no real benefit that I can see. I can't see this kind of platform being compatible with the C++ virtual machine, since C++ uses invalid pointers (one past the end) to mark the ends of arrays and containers.
Derek Park
A: 

Are you looking for this:

point* funct(point *p = some_value)
{
 if (p == NULL)
   return NULL;

 if (p == some_value) 
   p = new point;

   return p;
}
Igor Oks
the problem is if only i know what would be a nice some_value
iQ
+2  A: 

You get an error when you call func(y) because y is not initialized. It contains random bits that point to a random location in memory. You need to initialize y to NULL.

 point *x, *y;

 x = func();
 y = NULL;
 y = func(y); // so it can be deleted later you need to assign the return value
 delete x;
 delete y;

Why not do this? And avoid the heap allocation completely.

 point x, y;
 func(&x);
 func(&y);

Or:

 point *x;
 point *y = new point();
 x = func();
 func(y);
 delete x;
 delete y;

As I said in the comment above, memory ownership is confusing in your function. Functions that dynamically allocate their results should do so every time or none of the time. When they do so some of the time, the potential for a memory leak is much higher.

Personally, I would go even further and avoid all allocations, pass by reference:

 void func(point& p)
 {
      //do stuff
 }

 point x, y;
 func(x);
 func(y);
jmucchiello
I think that I will probably do, pass by reference possibly safest.
iQ
A: 

Okay I've decided not to use that idiom seems bad although I know one of the unix or linux system libraries had a function like that.

I'm not answering my own question but I just remembered the ctime idiom which is probably a much safer way to do what I want. I'm talking about the time() function.

Where it takes a pointer or returns a object by value.

I think it probably even gives better flexibility of returning by value, heap or stack allocation with pointer thats user controlled. I don't know why I didn't think of this before.

What do you guys thing, the ctime idiom is much better right?

Edit: Does that idiom use NULL check?

iQ
It would be best to ask this as a separate question. Otherwise this question will have a large number of answers to your original question, and another group of answers to the new one. That won't be easy to piece through.
Max Lybbert
+1  A: 

However you can choose not to pass a parameter and it will return a dynamically allocated pointer but if you do pass a pointer then it just fills it in instead of creating one on the heap. This is a single function not overloaded.

Instead of using default arguments, you could overload the function:

point* funct(point *p = some_value)
{
    // fills p
}

point* funct()
{
    return funct(new point());
}

It might not be the right way but somewhere I think in the linux libraries there was a function that works exactly like above not sure how it is allocated internally though

I'm not aware of any such function. I would guess you're thinking of realloc which takes a pointer and a size_t and then decides whether to allocate memory, adjust already allocated memory, or free memory.

Now i can't think if this is right way to do it and if it is then what could be a good some_value? it can't be NULL because when you pass empty pointer it will also be NULL and not sure if it is safe to have it greater than 0. Also you can't have negative numbers on pointers either, so whats a good safe value?

I think your confusion comes from a fundamental misunderstanding of the NULL pointer.

Observe:

int x = 5;
int* px = &x;
int* p_null = NULL;
int* p;
int* p_new = new int();

px points to x, so it's not NULL. p_null begins life as NULL which means it doesn't point to anything.

I think you're using the term "empty pointer" to refer to something like p or p_new. However, although p doesn't point to a valid object it wasn't set to NULL either. It's effectively an invalid pointer, and there is no portable way to tell that it's invalid (on some machines it may be possible to tell if something is obviously not valid, but even then it's not possible to catch all invalid pointers -- see note -- and you probably don't want to anyway -- second half).

And p_new points to a valid address in dynamic memory. It's not NULL. It's not empty. In fact, new will initialize the int to 0.

In other words, NULL is the value you pass to functions that expect a pointer to tell them you don't have a pointer. That's what it is. And there isn't really the idea of an empty pointer. Dangling pointers (either uninitialized pointers, or pointers to memory that you don't have access to) aren't NULL, because if they were NULL they wouldn't dangle. And it's impossible to validate pointers in all cases to determine if they are valid.


NOTE

Consider:

int* p_new2 = new int();
delete p_new2;

delete does not set p_new2 to NULL. So after the delete, p_new2 will have an address in the correct range for a valid pointer (meaning that Windows' VirtualQuery method will say "sure, a pointer could point there"), but will not have permission to actually dereference that memory address.

NOTE 2

This is a terribly bad idea, don't do it:

int* funct()
{
    int y = 5;
    return &y;
}

int* x = funct();

y ceases to exist after funct() returns. So the the pointer to y that funct() hands you points to something that doesn't exist. So you get a dangling pointer. You're not talking about doing this, but it's a common mistake, and it will bite you.

Max Lybbert
+1  A: 

You have to ask yourself why passing NULL us giving you a seg-fault. It is certainly not because NULL is not an appropriate value, it will be caused by whatever your code does when NULL is passed. However you chose not to show that code.

Haver you stepped through this code in your debugger?

Apart from that, in C++ do not use NULL. It is a macro and open to incorrect redefinition. Use plain zero (0). The language places guarantees that a zero literal constant when converted to a pointer will not be a valid address. The chances are that your NULL macro is in fact defined as zero.

If you attempt to dereference a null pointer you will get a fault.

Clifford
I did indeed have 0 at the beginning and it was working alright I think (if i remember right) but I thought NULL maybe a better portable solution. If however if the language guarantees 0 will not be valid then I may stick to it. Though now i've decided to follow a similiar idiom to ctime with the time() function
iQ