5
votes

it seems i'm stuck with some basics. Can someone explain me why next code:

#include <stdlib.h>

void Test1(char *t)
{
    t = (char *)malloc(11);
    strcpy(t, "1234567890");
}

void Test2(char **t)
{
    *t = (char *)malloc(11);
    strcpy(*t, "1234567890");
}

void Test3(char *t)
{
    strcpy(t, "1234567890");
}

char * Test4(char *t)
{
    t = (char *)malloc(11);
    strcpy(t, "1234567890");
    return t;
}

int main()
{
    char *t1 = NULL;
    Test1(t1);
    printf("\nTest1: %s\n", t1);

    char *t2 = NULL;
    Test2(&t2);
    printf("\nTest2: %s\n", t2);

    char *t3 = (char *)malloc(11);
    Test3(t3);
    printf("\nTest3: %s\n", t3);

    char *t4 = NULL;
    t4 = Test4(t4);
    printf("\nTest4: %s\n", t4);

    return 0;
}

gives this output:

Test1: (null)

Test2: 1234567890

Test3: 1234567890

Test4: 1234567890

What's wrong with Test1 function? And why Test4, which almost similar to Test1, works? More general question: what's the correct way to create string in function and return pointer to it?

10
You've tagged both C++ and C, and this depends on which you are using, in C++ you should use std::string (unless there is some pressing need not to!), in C, all except the first approach is possible.Nim
Yes, i can use string class, but i want to understand the core, that's why i use char* here.clumpter
In C, do NOT cast the result of malloc(). Read here for reasoning: stackoverflow.com/a/605858/1701799 (basically it's because a void * will be automatically and safely promoted to the appropriate type). I know this is tagged as C/C++, but there is no such language "C/C++". IF this was C++, you would be using #include <cstdlib> and not #include <stdlib.h>. I think this should probably just be tagged C. In C++, you would pretty much never use malloc() / free() but instead would use new/delete, or even better, smart pointers/etc..RastaJedi

10 Answers

0
votes

Consider your Test1 performs the following behavior:

char * x1 = NULL;
Test1 (x1);

Test1 is done within the following:

void test1 (char * t) / / t -> x1 -> null
{
    t = (char *) malloc (11);
    // t now points a different place (the memory allocated), and the reference of x1 continues pointing to null
    strcpy (t, "1234567890"); // t value is "1234567890" but x1 continues pointing to null
    // Error, the memory that is pointed by t is never released
}
printf ("\nTest1:%s \n", t1); / / Print the value of x1 (null) and the reference of t is lost
7
votes

The function parameters aren't working the way you think. You're passing in the value by "value" rather than by "reference" which means once inside the function, any changes to those values are local only to that function, so when the function exits the local changes are thrown away.

To fix this, either pass in a pointer to the pointer (char** t), or pass the pointer by reference (char&*) and change the function code to match.

4
votes

you have defined t1 as char* t1 = NULL; and calling the function as Test1(t1); passing the pointer variable t1 (not its address).

The function Test1 is expecting a char* void Test1(char *t)

here t is a variable local to Test1 function only. Any modification that you do inside the function will not be visible out side the function because you are actually not modifying the main function's variable t1 but the local variable t.

3
votes

Consider the function:

void Test1(char *t)
{
    t = (char *)malloc(11);
    strcpy(t, "1234567890");
}

Now, t is a local variable within the function. What does it contain? A pointer value. Initially that pointer value points to NULL because you call it like Test1( NULL );.

However the first line, t = (char *)malloc(11), re-assigns the local variable t to a newly malloc'd piece of memory.

When the function returns your main() variable t1 is still pointing to NULL because, remember I said before, the Test1 function takes a copy of the value of the pointer. At no point is the t1 variable ever modified by Test1.

If, however, you coded the function like:

void Test1(char **t) {
    *t = (char *)malloc(11);
    strcpy( *t, "1234567890" );
}

int main( void ) {
    ...
    Test1( &t1 );
    ...
}

..things would be different.

3
votes

When you pass a pointer as an argument to a function, the pointer is passed by value. You can therefore change the pointed object, but if you modify the pointer in the function, the caller won't know it.

3
votes

In Test1, you pass the variable t to your function, which is a pointer. Parameters passed to the functions lives in the stack, and when the function completes, the stack is lost. The reason t is NULL in your main() is that you stored the result of malloc in stack, and that stack no longer exists.

2
votes

In Test1, the line

t = (char *)malloc(11);

assigns to the variable t, which is local in the function Test1. The variable t1 in main() is not changed, so the NULL pointer passed to printf. Test4 works because you change t4 in main().

The "correct" way to create a string in function is either Test4 (but you don't need to supply t as parameter) or Test2 (if you prefer or need an out-parameter). In both of these cases the caller must free the string afterwards. Test3 works as well, but the caller must make sure the buffer is large enough - to prevent undefined behavior and memory leaks, the buffer size should be passed as parameter to Test3. The advantage to using Test3 is that the buffer can be allocated in the stack, eliminating the risk of a memory leak.

1
votes

Because you're writing this:

void Test1(char *t)

Change this to:

 void Test1(char* &t)

Will work in C++ Only. Demonstration here : http://www.ideone.com/OYNQo

1
votes

Firstly, in c, you only pass parameters by value, that means

char *t1 = NULL;
Test1(t1);

you pass a copy of pointer t1 to function Test1, so modifying this copy won't affect the original one. So when you try to print the string t1 points, it'll be the NULL you initialized before.

Now this one:

char *t4 = NULL;
t4 = Test4(t4);

although you also pass a copy of t4, function Test4 actually allocates a memory for you and makes this copy of t4 point to that memory, then you get that copy, you get that memory allocated for you :)

Actually for Test4, you don't have to pass an argument, just create a pointer in Test4 and return it, you'll get a copy of pointer which points to the memory you want. Like this:

   char * Test4()
   {
       char *t = (char *)malloc(11 * sizeof(char));
       strcpy(t, "1234567890");
       return t;
   }

Hope this helps.

-1
votes
void Test1( char*& t ) ; // This is a possible method signature.