[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 210
  • Last Modified:

modifying a pointer passed in as NULL

Suppose I have the following

typedef struct some_struct {
/* a whole bunch of fields */
} // SOME_STRUCT;

And a function that has the signature:

void handle_my_struct(SOME_STRUCT *ss)

{
  SOME_STRUCT local_ss;
 
  memset(local_ss, 0, sizeof(SOME_STRUCT);
  if (ss == NULL)
    ss = &local_ss;
}

Now when I call handle_my_struct with

handle_my_struct( (SOME_STRUCT*) NULL);

of course the IF clause above  will trigger.

My concern:
I'm setting ss to point to local_ss, which is at SOME memory location.
However, I have two concerns:

1. I never passed an actual structure or pointer to ss, so what happens when I return
to the calling function?  WHAT will be pointing to local_ss....???

2.  And of course, local_ss goes out of scope once I exit this function, furthering my concern.


Am I asking for trouble here? Or can I get away with it?

Also would appreciate the answer if I had set ss to point to a GLOBAL var. (global_ss).

Thanks
steve
0
Stephen Kairys
Asked:
Stephen Kairys
  • 10
  • 7
  • 4
  • +3
2 Solutions
 
griesshCommented:
Hi stevefromc,

You already noticed, local_ss is local and will be gone onece you return. Since you are only copying the pointer to the struct your ss will be a stale pointer.

Remedy: copy all elements of local_ss to *ss
 
======
Werner
0
 
griesshCommented:
about passing the NULL pointer:

You are passing in the address of a variable and change it in the function. But since you do not use a variable (static or local to the calling function) for that call you won't do anything wrong, the stack variable just goes away.
You are NOT changing NULL, since that is a #define in one of your header files which will be replaced by the precompiler.
0
 
griesshCommented:
If your goal is to populate a new variable you have a malloc() memory for that variable first.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
Not trying to populate a new variable. I have the local variable local_ss which is of course declared.
All I wanted to do is to have ss* point to it.
0
 
grg99Commented:
You have at least two probloems:

As you noted you cant pass back a local variable address, it's only valid while inside the function.

And the bigger problem, you CANT change the calling pointer at all, as it's passed in by value.

You could do it by passing in the address of the pointer, as a SOMESTRUCT* *. :  handle_my_struct( &s );

That makes it clear why you cant change NULL this way, you'd have to call it with   handle_my_struct( &NULL );,
which has no meaning.



0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:

Grg99,

Well I don't want to change the calling pointer here. It's a given it's being passed in as NULL.
However, because we cannot have the pointer be NULL when it's referenced in the function,
we need to assign it to SOMETHING; ergo, the above code.  All I'm trying to determine
is if I will really mess up something (in memory, or otherwise), by doing the above when
NULL is passed in.

Btw, if this thread sounds vaguely familar, it should :).  It's actually some further work
I'm doing related to the recent thread about passing a pointer to a char to strcmp() that
you helped me with earlier this week.

http://www.experts-exchange.com/Programming/Programming_Languages/C/Q_21192376.html

Thanks.
Steve
0
 
efnCommented:
When control returns to the calling function, nothing is pointing to local_ss.  This is a problem if you care about what's in local_ss after the function returns.  Otherwise, it's not.

Similarly, it's only a problem that local_ss goes out of scope if you care about it after the function returns.

Aside from the possibility of losing whatever is in the local structure, the technique you showed is safe and won't corrupt memory.

If you set ss to point to a global structure and something changes that structure, those changes will be retained after the function returns.
0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
>>If you set ss to point to a global structure and something changes that structure, those changes will be retained after the function returns.<<

...but what will be pointing to the global structure once I return from my function?

..wouldn't that answer still be NOTHING, since the parameter ss no longer exists once I
leave the function

...which brings me to another question. Pointers are not "free of charge"  i.e. they take
up, I believe 4 bytes of memory.  Let's take the "Usual" case where I might actually
be passing a non-NULL pointer to handle_my_struct(). i.e.

handle_my_struct(ss_ptr); // where ss_ptr is of type  SOME_STRUCT*

Now, ss_ptr is a 4-byte pointer so it has to take up memory itself. For example:
ss_ptr is at address 0x200 and POINTS to memory at 0x400.
Then, when I pass it to
handle_my_struct(),
the parameter *ss also is a 4-byte entity that resides at
0x200 and POINTS to 0x400, right?

So far so good :)

But what is happening when I say handle_my_struct ( (SOME_STRUCT*)NULL) ????
something's pointing to NULL alright, but is the runtime allocating some 4-byte
entity IN THE CALLING function (for the sake of argument,
at address 0x500)  to point to NULL?

It can't be that "thin air" is pointing to the NULL, right?

I just hope the above does not stir up a hornet's nest ;)

Thanks
Steve

0
 
efnCommented:
> ...but what will be pointing to the global structure once I return from my function?

> ..wouldn't that answer still be NOTHING, since the parameter ss no longer exists once I
> leave the function

Yes.  However, functions can still find the global structure by its name, so that would be a way to get data out of the function.

> Then, when I pass it to
> handle_my_struct(),
> the parameter *ss also is a 4-byte entity that resides at
> 0x200 and POINTS to 0x400, right?

Nope.  The run-time code makes a temporary copy of the value of ss_ptr and passes the temporary copy to the function.  The function can change its parameter ss without affecting ss_ptr.  After the function returns, the memory space used for the temporary copy can be reused for some other purpose.

> But what is happening when I say handle_my_struct ( (SOME_STRUCT*)NULL) ????
> something's pointing to NULL alright, but is the runtime allocating some 4-byte
> entity IN THE CALLING function (for the sake of argument,
> at address 0x500)  to point to NULL?

The function parameter is still a temporary object.  The run-time code stores a hard-coded value of zero in it and passes it to the function.  As before, after the function returns, the temporary object's memory can be reused.

--efn
0
 
griesshCommented:
>> All I wanted to do is to have ss* point to it.

A local variable is created on the stack. Once the function is gone the variable is gone, too. By assigning the address of a local variable to a variable of another scopr you create a stale pointer in the calling function.

By passing in the NULL pointer you are not using avariable at all, so you don't have a reference in th calling function.

If you REALLY want to do it that way I suggest

static   SOME_STRUCT local_ss;

because this will create a 'static' ariable that continues to exist and you can always reference it (within the scope of this file).
0
 
PaulCaswellCommented:
You have two choices:

void handle_my_struct(SOME_STRUCT **ss)
{
  SOME_STRUCT local_ss;
 
  memset(local_ss, 0, sizeof(SOME_STRUCT);
  if (ss == NULL)
    *ss = local_ss;
}

or

SOME_STRUCT handle_my_struct(void)
{
  SOME_STRUCT local_ss;
 
  memset(local_ss, 0, sizeof(SOME_STRUCT);
  return(local_ss);
}


These will both achieve the same effect.

Paul
0
 
ravs120499Commented:
Hi

> 1. I never passed an actual structure or pointer to ss, so what happens when I return
> to the calling function?  WHAT will be pointing to local_ss....???

Nothing. When you return to the calling function, nothing will be pointing to local_ss.
ss is a stack variable (the pointer is allocated on stack), and the entire stack frame will get removed when the function returns.
local_ss itself is allocated on stack and will be removed when the function returns.

> 2.  And of course, local_ss goes out of scope once I exit this function, furthering my concern.

If the calling function expects ss to point to a valid SOME_STRUCT when handle_my_struct() returns, then you have a problem. Otherwise, not. For example, in a situation like this:
* handle_my_struct() uses a SOME_STRUCT *entirely* for its internal logic.
* handle_my_struct() can use a default SOME_STRUCT, but the calling function can also override the default by providing a SOME_STRUCT itself.
* And, you don't want to put the entire SOME_STRUCT on the stack when calling handle_my_struct(), so you are passing in a pointer.

In this scenario, what I would say it is, make the code (and its prototype in the .h file) very very well commented so there is no confusion that ss is an INPUT only parameter.

HTH
Ravs
0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
Ravs,

Actually, there are some cases where I might be passing a living, breathing instance of
SOME_STRUCT to handle_my_struct().  The way the code should work, however (and I'm
actually in the process of fixing it):
* If NULL is passed to ss, then ss should never be modified
* If a real structure is passed, I would allow it to be modified.

Obviously, to do so, I would need protection in the code such as:
if (ss != NULL)....

Thanks
0
 
griesshCommented:
>>* If NULL is passed to ss, then ss should never be modified
>>* If a real structure is passed, I would allow it to be modified.

void handle_my_struct(SOME_STRUCT *ss)
{
  if (ss != NULL)
  memset(ss, 0, sizeof(SOME_STRUCT);
}
0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
Well, the consensus seems to be that I can "safely" pass NULL
to ss and that "nothing" (outside of the function parameter) will get modified if I modify ss.  That being said, I realize that
this technique may not be the cleanest code on earth,
esp. regarding the issue of having ss point to a local
var. and then "go away".

It's been an educational discussion.
Thanks!
0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
I am ready to close this question, but want to give
anyone out there a final chance to accept/refute the
above "summary" before I do so
and assign points. Will check back in a few days.
Thanks.
0
 
griesshCommented:
>> this technique may not be the cleanest code on earth
There is no doscussion about cleanliness, is useless code.

Unless you changed your function in a way Paul or myself suggested it will fail for any 'real' parameter.

So, what is your function now?
0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
The function now stands as follows (simplifying things):

void handle_my_struct(SOME_STRUCT *ss)
{
    SOME_STRUCT local_ss;

    if (ss == NULL)
    // I need to set it to point to SOMETHING, since  within this function, I refer to
    // fields such as ss->name
    {
        memset(&ss, 0, sizeof(SOME_STRUCT);
        ss = &local_ss;
   }
   else
      I don't need to reassign ss since it comes in as valid

   ...rest of function...
 
}

So....if I call it with
handle_my_struct( (SOME_STRUCT*)NULL),
the IF clause executes.  Based on what you all have told me, once I leave the function,
nothing will point to local_ss anymore, which is what I want. (Correct???)

and....if I call it with
handle_my_struct(&calling_function_ss)
handle_my_struct() will do what it needs to do with what's passed in. Any fields
in ss (at the function level) are valid (e.g. ss->name), and if I modify a field
(say: ss>status = PROCESSED), I'm fine with the fact that at the caller level
calling_function.status will also become PROCESSED.

Sound OK?
Thanks.
0
 
griesshCommented:
</unsubscribe>
0
 
efnCommented:
Steve,

It's hard to call it a consensus when a majority of the experts are answering a question you didn't ask, but what you stated as the consensus and reiterated in your last comment is correct.

You can test a pointer parameter to see if it is null or not.

You can change the value of a (non-const) parameter inside a function, even if the parameter is a pointer.  You can set a pointer parameter to the address of a local object of a suitable type.  Doing so will have no effect on either the pointer that was passed as the parameter or the object that pointer addresses.

When control returns from the function, both the pointer and the local object will disappear, and nothing will be corrupted or leaked or left dangling.

It might be considered slightly more readable to use a local pointer variable rather than change the parameter itself.

SOME_STRUCT* thepointerIamgoingtouse;
if (ss)
  thepointerIamgoingtouse = ss;
else
  thepointerIamgoingtouse = &local_ss;

While it is legal, it is not common for functions to change their parameters, so this might be less confusing to readers who assume that ss will always be what the calling function passed.  But this is just a point of style.

--efn
0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
Efn,
Hold on...I'm missing something, I think....

>>You can change the value of a (non-const) parameter inside a function, even if the parameter is a pointer.  You can set a pointer parameter to the address of a local object of a suitable type.  Doing so will have no effect on either the pointer that was passed as the parameter or the object that pointer addresses.<<

Suppose SOME_STRUCT  is defined as
typedef struct some_struct
{
    char name[30];
    int age;
};

I have in the calling  funciton
{
  SOME_STRUCT calling_function_ss;

  handle_my_struct(&calling_function_ss);
}

in handle_my_struct
{
   if (ss)
   {
       strcpy(ss->name, "TEST");
       ss->age =25;
   }
}

obviously ss-> name will be "TEST"  and ss->age will be 25
at this level.

But...since I passed  the parameter b y REFERENCE
wouldn't, at the CALLING level
calling_function_ss.name be "TEST" and
calling_function_ss.age be 25?

thx



0
 
efnCommented:
> But...since I passed  the parameter b y REFERENCE
> wouldn't, at the CALLING level
> calling_function_ss.name be "TEST" and
> calling_function_ss.age be 25?

Yes.  However, if handle_my_struct had "set a pointer parameter to the address of a local object of a suitable type," the answer would be "no."  Your example did not include that crucial step.  If the calling function passes the address of calling_function_ss and handle_my_struct does not change its ss parameter, then changes to *ss affect calling_function_ss.  But if handle_my_struct changes ss to point to something else (such as local_ss), then changes to *ss do not affect calling_function_ss.

--efn
0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
Exactly.  Thanks for clarifying :)
0
 
Stephen KairysTechnical Writer - ConsultantAuthor Commented:
Thanks to all for their help!!
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 10
  • 7
  • 4
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now