tags:

views:

94

answers:

4

In C, I am trying to set a pointer's value by sending it to a function, but the value wont change outside of the function. Here is my code:

#include <stdio.h>
void foo(char* str) {

    char* new_str = malloc(100);
    memset(new_str, 0, 100);
    strcpy(new_str, (char*)"new test");

    str = new_str;
}


int main (int argc, char *argv[]) {

    char* str = malloc(100);
    memset(str, 0, 100);

    strcpy(str, (char*)"test");

    foo(str);

    printf("str = %s\n", str);
}  

I want to print out:

str = new test 

but this code prints out:

str = test

Any help will be appreciated. Thanks in advance.

+6  A: 

There is no pass-by-reference in C. If you provide str as the argument to a function in C, you are always passing the current value of str, never str itself.

You could pass a pointer to str into the function:

void foo(char** pstr) {
    // ...
    *pstr = new_str;
}

int main() {
    // ...
    foo(&str);
}

As Eiko says, your example code leaks the first memory allocation. You're no longer using it, and you no longer have a pointer to it, so you can't free it. This is bad.

Steve Jessop
"There is no pass-by-reference in C" - Isn't arrays passed by reference?
naivnomore
@naivnomore Basically you pass a pointer which is (at least syntactically) different to passing references in C++.
Eiko
No, they're not. Arrays decay to pointers when passed as parameters, but that's still not a pass-by-reference because changing the value of the pointer argument itself will not affect the array.
Tyler McHenry
@naivnomore: Well, they're passed as a pointer to the first element. Pass-by-pointer serves pretty much all the practical purposes of pass-by-reference, but it's not the same thing. C has the syntactic quirk that you can declare a function parameter `char[]`. That's synonymous with `char*`, but combined with array-to-pointer decay it looks like a pass-by-reference in inadequate light. The way to tell the difference is to use `sizeof` on the parameter inside the function, or assign to it. True pass-by-reference would maintain the type, as when you pass an array reference in C++.
Steve Jessop
It is true, though, that arrays aren't passed by value in C either, perhaps leading to the thought that by a process of elimination, they must be passed by reference. In fact they can't be passed as parameters at all, just pointers to them. It's technically incorrect (although commonly done, and normally understood) to refer to "passing an array" in C, or for that matter "passing a string".
Steve Jessop
@Steve Yep, there's a similar pass-by-reference misconception about Java. But it's simple: everything is passed by value. Sometimes you pass references (Java) or pointers (C) but the references/pointers themselves are still being passed by value.
Tyler McHenry
ladenedge
@ladenedge: well, maybe, but I think it would be more misleading to say that there is pass-by-reference in C. As I said: pass-by-pointer fulfils the practical needs, but it's not the language feature which exists in other languages under the name "pass-by-reference". It's passing *a* reference, sure. But the reference is a pointer, and it's passing the pointer by value.
Steve Jessop
@ladenedge It's not a matter of opinion. "pass by reference" and "pass by value" (and the much more rarely-used things like "pass by name") are well-defined technical terms in the domain of programming languages. Passing a pointer passes a *value* to the function. The fact that that value represents a memory address at which something else is stored is immaterial. Passing a reference in C++ is truly pass-by-reference because there is no value being passed; the parameter is in all senses a true alias name for the argument, which a pointer is not.
Tyler McHenry
+3  A: 

You need to use pointer to the pointer, untested:

#include <stdio.h>

void foo(char** str)
{
    char* new_str = malloc(100);
    memset(new_str, 0, 100);
    strcpy(new_str, (char*)"new test");
    if (str) { /* if pointer to pointer is valid then */
        if (*str)   /* if there is a previous string, free it */
            free(*str);
        *str = new_str;  /* return the string */
    }
}


int main (int argc, char *argv[])
{
    char* str = malloc(100);
    memset(str, 0, 100);

    strcpy(str, (char*)"test");

    foo(&str);

    printf("str = %s\n", str);
}
Dummy00001
You probably mean `if(str)` rather than `if(*str)`. Although even with that change, in the case where `str` is null, `foo` leaks an allocation.
Steve Jessop
@Steve: oops. thanks. corrected.
Dummy00001
Also: `free(0)` does nothing. But it's not actually wrong to check for null before calling `free`.
Steve Jessop
@Steve: yes. old habits die hard.
Dummy00001
Also, don't forget to include <stdlib.h>
robUK
+2  A: 

You are just reassigning a pointer, which is a local variable in foo.

If you want to copy the string, use strcpy(str, new_str);

You could pass a reference to the pointer instead and reassign, but this can easily lead to memory leaks and is hard to maintain.

Edit: For the pseudo pass by reference see the answer by Steve.

Eiko
A: 

Hello,

I did it this way by returning the pointer from the function. There is no reason to use malloc in this case, so you don't have to worry about freeing.

gcc 4.4.3 c89

char* print_test(char *str)
{
    char *new_str =  "new_test";
    printf("new_str [ %s ]\n", new_str);
    str = new_str;
    return str;
}

int main(void)
{
    char *str = "test";

    printf("str [ %s ]\n", str);

    str = print_test(str);

    printf("str [ %s ]\n", str);

    return 0;
}
robUK