Confused about accessing structure members via a pointer
I am new to C and I am confused with the results I get when referencing a structure member using a pointer. See the following code for an example. What happens when I reference the tst-> number the first time? What is the main thing am I missing here?
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int number;
} Test;
Test* Test_New(Test t,int number) {
t.number = number;
return &t;
}
int main(int argc, char** argv) {
Test test;
Test *tst = Test_New(test,10);
printf("Test.number = %d\n",tst->number);
printf("Test.number = %d\n",tst->number);
printf("Test.number = %d\n",tst->number);
}
Output:
Test.number = 10
Test.number = 4206602
Test.number = 4206602
source to share
When you pass a test to your Test_New function, you are passing it by value, and therefore a local copy is made on the stack for the function scope of your Test_New function. Since you're returning the address of a variable as soon as the function returns the stack, it's useless, but you've returned a pointer to a structure on the old stack! So you can see that your first call is returning the correct value as nothing has overwritten your stack value, and subsequent calls (which all use the stack) overwrite your value and give you erroneous results.
To do this, rewrite the Test_New function correctly to take a pointer and pass a pointer to a structure to the function.
Test* Test_New(Test * t,int number) {
t->number = number;
return t;
}
int main(int argc, char ** argv) {
Test test;
Test * tst = Test_New(&test,10);
printf("Test.number = %d\n",tst->number);
printf("Test.number = %d\n",tst->number);
printf("Test.number = %d\n",tst->number);
}
source to share
Regardless struct
, it is always wrong to return the address of a local variable . It is usually also wrong to put the address of a local variable in a global variable or store it in an object allocated on the heap with malloc
. Typically, if you need to return a pointer to an object, you either need to get someone else to specify the pointer for you, or you will need to allocate space with a help malloc
that will return the pointer. In this case, part of the API for your function should specify who is responsible for calling free
when the object is no longer needed.
source to share
You are returning the address t
as specified in the method Test_New
, not the address test
that you passed to that method. That is, it is test
passed by value, and you should instead pass a pointer to it.
So this is what happens when you call Test_New
. A new structure is created test
with a name t
and t.number
set to a value test.number
(which you have not initialized). Then you set t.number
equal to the parameter number
you passed to the method and then you return the address t
. But it t
is a local variable and goes out of scope as soon as the method ends. This way you are returning a pointer to data that no longer exists and therefore you end up in garbage.
Change your ad Test_New
to
Test* Test_New(Test* t,int number) {
t->number = number;
return t;
}
and call it through
Test *tst = Test_New(&test,10);
and everything will go as you expect.
source to share
The problem is that you are not passing a reference to Test_New
, you are passing a value. Then you return the memory location of the local variable. Consider this code that demonstrates your problem:
#include <stdio.h>
typedef struct {
} Test;
void print_pass_by_value_memory(Test t) {
printf("%p\n", &t);
}
int main(int argc, char** argv) {
Test test;
printf("%p\n", &test);
print_pass_by_value_memory(test);
return 0;
}
The output of this program on my computer:
0xbfffe970 0xbfffe950
source to share
Just to expand on BlodBath's answer, think about what's going on in memory when you do this.
When you enter your main routine, a new automatic test structure is created - on the stack, as it is automatic. So your stack looks something like
| return address for main | will be used at bottom | argc | copied onto stack from environment | argv address | copied onto stack from environment -> | test.number | created by definition Test test;
c ->
, pointing the stack pointer to the last used stack element.
Now you call Test_new()
and it updates the stack like this:
| return address for main | will be used at bottom | argc | copied onto stack from environment | argv address | copied onto stack from environment | test.number | created by definition Test test; | return addr for Test_new | used to return at bottom | copy of test.number | copied into the stack because C ALWAYS uses call by value -> | 10 | copied onto stack
When you return &t
, what address do you get? Answer: the address of the data on the screen. BUT THEN you return, the stack pointer is decremented. When you call printf
these words are reused on the stack, but your address still matches them. It happens that a number in the place on the stack that is interpreted as an address points to the value 4206602, but this is a pure chance; in fact, it was kind of a bad luck, as luck would have been something that caused a segmentation fault, letting you know that something was indeed violated.
source to share
The test t declared in Test_New () is a local variable. You are trying to return the address of a local variable. When the local variable is destroyed after the function exists, the memory will be freed, the compiler can put a different value in the location where your local variable was stored.
In your program, when you try to access the value a second time, the memory location may have been assigned to another variable or process. Hence, you are getting the wrong result.
Your best bet would be to pass the structure from main () by reference, not by value.
source to share
You passed the test content by value Test_New. IOW a new copy of the test structure was allocated on the stack when Test_New was called. This is the address of this test that you return from the function.
When you use tst-> number, the first time the value 10 is popped, because although that stack was unwound, no other use of that memory was made. However, once that first printf has been called, the stack memory is reused for whatever it needs, but tst still points to that memory. Hence, sub-sequential use of tst-> number returns whatever printf is left there in that memory.
Use Test & t in your function signature instead.
source to share
You can do something like this to make it a little easier:
typedef struct test {
int number;
} test_t;
test_t * Test_New(int num)
{
struct test *ptr;
ptr = (void *) malloc(sizeof(struct test));
if (! ptr) {
printf("Out of memory!\n");
return (void *) NULL;
}
ptr->number = num;
return ptr;
}
void cleanup(test_t *ptr)
{
if (ptr)
free(ptr);
}
....
int main(void)
{
test_t *test, *test1, *test2;
test = Test_New(10);
test1 = Test_New(20);
test2 = Test_new(30);
printf(
"Test (number) = %d\n"
"Test1 (number) = %d\n"
"Test2 (number) = %d\n",
test->number, test1->number, test2->number);
....
cleanup(test1);
cleanup(test2);
cleanup(test3);
return 0;
}
... As you can see, it's easy to allocate space for several completely different instances of test_t, for example if you need to keep the existing state of one so you can come back later .. or for some reason.
Unless of course there is some reason why you should keep it local .. but I really can't think of it.
source to share