Derek
Derek

Reputation: 12388

Why c string is freed?

Not familiar with C, please correct me on any mistakes.

here's some code:

void db_cstr(char* cstr, int len) {
    char* temp2 = cstr;
    cstr = (char*)malloc(len*2*sizeof(char));
    // print 1
    printf(cstr);
    printf("\n");
    //print 2
    printf(temp2);
    printf("\n");
    strcpy(cstr, temp2);
    //free
    free(temp2);
    //print 3
    printf(cstr);
}
int somefunction(){
    int array_len = 10;
    char* cmd = (char*)malloc(array_len*sizeof(char));
    strcpy(cmd, "apple");
    db_cstr(cmd, array_len);
    // final print
    printf(cmd);
    return 1;
}

My values(always) for // print 1 == "" and // print 2 == "apple" and // print 3 == "apple". However, when I do final print, printf prints nothing. I assumed this had to do with free(temp2); so when I commented it out the final print was "apple". I believe this is because the orignal cmd pointer in somefunction is still pointing to the freed array at temp2. How do you make the cmd pointer point to what the new cstr is pointing to in db_cstr. (I don't want db_cstr to return anything).

Upvotes: 2

Views: 140

Answers (6)

Lenar
Lenar

Reputation: 398

The only way to change an address that cmd points is to pass a pointer to cmd to db_cstr.

So you need to rewrite code like this:

void db_cstr(char** cstr, int len) {
    char* temp2 = *cstr;
    *cstr = (char*)malloc(len*2*sizeof(char));
    strcpy(*cstr, temp2);
    free(temp2);
}
int somefunction(){
    int array_len = 10;
    char* cmd = (char*)malloc(array_len*sizeof(char));
    strcpy(cmd, "apple");
    db_cstr(&cmd, array_len);
    // final print
    printf(cmd);
    return 1;
}

Look at db_cstr(&cmd, array_len); you are passing a pointer to pointer here.

Upvotes: 0

NPE
NPE

Reputation: 500663

The following free()s the caller's cmd:

char* temp2 = cstr;
free(temp2);

Therefore the final printf() is trying to print memory that's already been freed, which is undefined behaviour.

The easiest way to make db_cstr() return the new pointer is like so:

char* void db_cstr(char* cstr, int len) {
    ...
    printf(cstr);
    return cstr;
}

int somefunction(){
    ...
    cmd = db_cstr(cmd, array_len);
    ...
}

A similar effect can be achieved by making the first argument to db_cstr() into a pointer-to-pointer (char**) and changing the code appropriately.

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 613252

Probably the simplest is to return the new memory from the function rather than trying to return it via the pointer.

char* db_cstr(char* cstr, int len) {
    char* result = malloc(len);
    strcpy(result, cstr);
    free(cstr);
    return result;
}

Then you would call it like this:

cmd = db_cstr(cmd, array_len);

Upvotes: 0

Don't use printf but puts in your case. If you pass "a%sx%d" to your printf without additional argument, your program has undefined behavior and would crash.

Learn to use a debugger, and print the addresses of the strings involved. For example, you might put in several places a statement similar to

fprintf(stderr, "at %s:%d cstr=%p\n", __FILE__, __LINE__, cstr);

of course replacing cstr twice by a possibly more appropriate variable name.

Upvotes: 0

unwind
unwind

Reputation: 399949

You should return the new pointer. But you state, without motivation, that you don't "want" to return anything. So, I guess you need to make the argument a pointer to a pointer, so you can change the caller's pointer from with-in the function:

void db_cstr(char **cstr, size_t len);

Upvotes: 1

asaelr
asaelr

Reputation: 5456

If you want a function to change a var, you should pass a pointer to it. This is true even when the var is a pointer.

so: void db_cstr(char** cstr, int len), db_cstr(&cmd, array_len);, and so on...

by the way, don't use printf directly on a var. use printf("%s",cmd) instead.

Upvotes: 2

Related Questions