5

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?

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
clumpter
  • 1,898
  • 6
  • 27
  • 38
  • 2
    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 Jan 07 '11 at 14:52
  • Yes, i can use string class, but i want to understand the core, that's why i use char* here. – clumpter Jan 07 '11 at 15:46
  • In C, do *NOT* cast the result of `malloc()`. Read here for reasoning: http://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 ` and not `#include `. 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 Feb 22 '16 at 05:35

11 Answers11

7

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.

Roger Perkins
  • 678
  • 5
  • 7
4

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.

Vikram.exe
  • 4,565
  • 3
  • 29
  • 40
3

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.

PP.
  • 10,764
  • 7
  • 45
  • 59
3

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.

Benoit
  • 76,634
  • 23
  • 210
  • 236
3

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.

Erkan Haspulat
  • 12,032
  • 6
  • 39
  • 45
2

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.

Antti
  • 11,944
  • 2
  • 24
  • 29
1

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

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • This is poor design and obscures the actual behaviour of the program. If developers don't understand pointers then they shouldn't be developing in C++. – PP. Jan 07 '11 at 14:41
  • No, I mean: passing-by-reference is always a bad idea. It makes code unclear and encourages [programming-by-coincidence](http://pragprog.com/the-pragmatic-programmer/extracts/coincidence) - there is no need for it; understanding pointers is enough - the problem is too many programmers fear pointers when really they are very simple. – PP. Jan 07 '11 at 14:50
  • 1
    @PP I disagree that passing-by-reference always is a bad idea and I think that you need to be more specific on what cases you think are a bad idea. I wouldn't say that it always is a good thing either though. Please don't use the OP's example, as it appears to be a C example rather than C++. – villintehaspam Jan 07 '11 at 15:21
  • 2
    @PP: On the contrary, there are many situations where passing-by-reference is very very very good idea, especially when you want to avoid unnecessary temporaries. It speeds up execution! – Nawaz Jan 07 '11 at 15:26
  • @villintehaspam you may disagree but you're not right. Passing by reference is an incredibly bad idea. Always. – PP. Jan 09 '11 at 09:42
  • @Nawaz you can always pass pointers. It's explicit. You know what you're doing, the compiler know what you're doing, anybody reading your code knows what you're doing. When you pass by reference you're apt to confuse any human reader of your code unless they've seen your prototypes. That's disrespectful. Passing-by-reference is no faster than passing pointers. So why confuse people for no benefit other than laziness and programming-by-coincidence? – PP. Jan 09 '11 at 09:44
  • 1
    @PP: I assume now that you are only arguing against the use of references for output parameters - i.e you are making a strong distinction from const references used for input parameters (where using a non-const reference is confusing)? – villintehaspam Jan 09 '11 at 11:47
  • @PP: So The Pragmatic Programmer is against using references in function parameter? How does it deal with unnecessary copies? – Nawaz Jan 10 '11 at 08:45
  • @PP: Do you actually program in c++? If so, you should know that (const) references _must_ be used for certain things. Saying that "const Object &" is always a bad thing is just plain wrong, if that is indeed what you are saying, I can't tell. If you are only arguing against Object &, that is another story, even if you are doing it in a somewhat weird tone. There are benefits and drawbacks with using either references or pointers for returning values, but saying "XYZ is an incredibly bad idea. Always." isn't very convincing and only takes away focus from whatever point you might have. – villintehaspam Jan 10 '11 at 13:03
1

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.

Xin
  • 113
  • 3
  • 9
0

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
xlarsx
  • 981
  • 1
  • 9
  • 8
0

You forget to pass the address of t at Test1. Change this:

 # a copy by value
 Test1(t1);

to that:

 # a reference
 Test1(&t1);

And the script will work

Franz Kurt
  • 1,020
  • 2
  • 14
  • 14
-1
void Test1( char*& t ) ; // This is a possible method signature.
Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • @Downvoter- Have you downvoted because of my previous comment `This should be the method signature` earlier or some other reason. Thanks. – Mahesh Jan 07 '11 at 14:51