Wednesday, October 7, 2009

Pointer arguments and returns, a concept clarified

While working on my C++ project, I faced a stupid problem. It was the concept that I learned in my first two semesters of undergraduate studies but over time while working on different technologies the concept faded into my subconscious. It was about passing pointer as arguments and returning them, while we are assigning new memory to them within the method. I passed the pointer to a method, then allocated memory in the method and assigned it to the variable. After returning from method, the pointer value that I worked out within method was not available.

It was because I never rewrite the pointer variable with the new address. Just look at the sample codes and discussions below:


Sample 1:
void PointerArg::ArgIntUpdate(SamClass* _SamClassPtr)
{
_SamClassPtr = new SamClass();
_SamClassPtr->SetID(7);
_SamClassPtr->ShowMessage();
printf("");
printf(" : Address is : %X", _SamClassPtr);
}

printf("\n\nAddress before : %X", SamClassPtr);
this->ArgIntUpdate(SamClassPtr);
SamClassPtr->ShowMessage();
printf("");
printf(" : Address is : %X", SamClassPtr);

Sample 1 output:
Address before : 422BA0
ID is 7 : Address is : 422BE0
ID is 3 : Address is : 422BA0

Discussion
In sample 1, we pass pointer variable SamClassPtr as argument to method ArgIntUpdate. In ArgIntUpdate we assign the pointer variable with new address of newly allocated class object using new. When we return from this method, the original class level variable still points to old address, i.e., 422BA0 in our case.

Sample 2:
void PointerArg::ArgIntUpdateMalloc(SamClass* _SamClassPtr)
{
_SamClassPtr = (SamClass*)malloc(sizeof(SamClass));
_SamClassPtr->SetID(9);
_SamClassPtr->ShowMessage();
printf("");
printf(" : Address is : %X", _SamClassPtr);
}

printf("\n\nAddress before : %X", SamClassPtr);
this->ArgIntUpdateMalloc(SamClassPtr);
SamClassPtr->ShowMessage();
printf("");
printf(" : Address is : %X", SamClassPtr);

Sample 2 output:
Address before : 422BA0
ID is 9 : Address is : 422C20
ID is 3 : Address is : 422BA0

Discussion
In sample 2, we pass pointer variable SamClassPtr as argument to method ArgIntUpdateMalloc. In ArgIntUpdateMalloc we assign the pointer variable with new address of newly allocated class object using malloc. When we return from this method, the original class level variable still points to old address, i.e., 422BA0 in our case.

Sample 3:
SamClass* PointerArg::RetUpdate(void)
{
SamClass _SamClassObj;
_SamClassObj.SetID(11);
(&_SamClassObj)->ShowMessage();
printf("");
printf(" : Address is : %X", &_SamClassObj);
return &_SamClassObj;
}

printf("\n\nAddress before : %X", SamClassPtr);
SamClassPtr = this->RetUpdate();
SamClassPtr->ShowMessage();
printf("");
printf(" : Address is : %X", SamClassPtr);

Sample 3 output:
Address before : 422BA0
ID is 11 : Address is : 13FD50
ID is -858993460 : Address is : 13FD50

Discussion
In sample 3, we return a pointer variable SamClassPtr from method RetUpdate. In RetUpdate we assign the pointer variable with new address of newly allocated class object. When we return from this method, the original class level variable is overwritten with new address, i.e., 13FD50 in our case, however, since the object was allocated with in method it is no longer available after method is returned and we found garbage at the same location.

Sample 4:
SamClass* PointerArg::RetUpdateNew(void)
{
SamClass* _SamClassObj = new SamClass();
_SamClassObj->SetID(13);
_SamClassObj->ShowMessage();
printf("");
printf(" : Address is : %X", _SamClassObj);
return _SamClassObj;
}

printf("\n\nAddress before : %X", SamClassPtr);
SamClassPtr = this->RetUpdateNew();
SamClassPtr->ShowMessage();
printf("");
printf(" : Address is : %X", SamClassPtr);

Sample 4 output:
Address before : 13FD50
ID is 13 : Address is : 422C60
ID is 13 : Address is : 422C60

Discussion
In sample 4, we return a pointer variable SamClassPtr from method RetUpdateNew. In RetUpdateNew we assign the pointer variable with new address of newly allocated class object using new operator. When we return from this method, the original class level variable is overwritten with new address, i.e., 422C60 in our case, however, this time since the object was allocated using new it is still available at the same address after method is returned.

Sample 5:
SamClass* PointerArg::RetUpdateMalloc(void)
{
SamClass* _SamClassObj = (SamClass*)malloc(sizeof(SamClass));
_SamClassObj->SetID(15);
_SamClassObj->ShowMessage();
printf("");
printf(" : Address is : %X", _SamClassObj);
return _SamClassObj;
}

printf("\n\nAddress before : %X", SamClassPtr);
SamClassPtr = this->RetUpdateMalloc();
SamClassPtr->ShowMessage();
printf("");
printf(" : Address is : %X", SamClassPtr);

Sample 5 output:
Address before : 422C60
ID is 15 : Address is : 422CA0
ID is 15 : Address is : 422CA0

Discussion
In sample 5, we return a pointer variable SamClassPtr from method RetUpdateMalloc. In RetUpdateMalloc we assign the pointer variable with new address of newly allocated class object using malloc operator. When we return from this method, the original class level variable is overwritten with new address, i.e., 422CA0 in our case, however, this time since the object was allocated using malloc it is still available at the same address after method is returned.

Conclusions:
1) When passing the pointers by argument, make sure we assign it the memory before passing the variable and we are not overwrite the memory within method.

2) If we want to allocate new class object within method, we should make sure we do it using new or malloc instread of direct object instantiation. This will ensure that memory is still available after method is returned.