views:

157

answers:

5

I am writing my own string copy function. The following works:

char *src, *dest;
src = (char *) malloc(BUFFSIZE);
//Do something to fill the src
dest = (char *) malloc(strlen(src) + 1);
mystringcpy(src, dest);

void mystringcopy(char *src, char *dest) {
   for(; (*dest = *src) != '\0'; ++src, +dest);
}

But this doesn't work:

char *src, *dest;
src = (char *) malloc(BUFFSIZE);
//Do something to fill the src
mystringcpy(src, strlen(src), dest);

void mystringcopy(char *src, size_t length, char *dest) {
   dest = (char *)malloc(length + 1);
   for(; (*dest = *src) != '\0'; ++src, +dest);
}

and I can't understand why... Is allocating memory inside a called function a mistake?

+2  A: 

You haven't really said what "works" means, but I'm assuming you're confused why dest isn't being changed to the new memory back in the calling function.

The reason is that in your mystringcopy function, the parameter dest is a copy of the pointer dest in the calling function.

You then assign that copy to a new buffer, do the copy, and then the copy goes away. The original is unchanged. You need to pass dest as a pointer (to a pointer).

Also, I assume you wrote what you did from memory since it shouldn't compile as is (bad dereference in the calling function). Here's the fixed code:

char *src, *dest;
src = (char *)malloc(BUFFSIZE); // no dereference on src, it's a pointer

//Do something to fill the src
mystringcpy(src, strlen(src), &dest); // pass the address of dest

// take a pointer to a char*
void mystringcopy(char *src, size_t length, char **dest) {
    // now you should dereference dest, to assign to
    // the char* that was passed in
    *dest = (char *)malloc(length + 1);

    // for simplicity, make an auxiliary dest
    char* destAux = *dest;

    // and now the code is the same
    for(; (*destAux = *src) != '\0'; ++src, ++destAux);
}

Another method is to return the dest pointer:

char *src, *dest;
src = (char *)malloc(BUFFSIZE);

//Do something to fill the src
dest = mystringcpy(src, strlen(src)); // assign dest

char* mystringcopy(char *src, size_t length) {
    char* dest = (char *)malloc(length + 1);

    // for simplicity, make an auxiliary dest
    char* destAux = dest;

    for(; (*destAux = *src) != '\0'; ++src, ++destAux);

    return dest; // give it back
}

Keep in mind if length is smaller than the source buffer's real length that you'll overrun your destination buffer. See the comments for a solution, though this is left up to you.

GMan
Great explanation.. Thank you very much... One other question is that if I don't happen to pass the length, I'm assuming I'll have to calculate this inside the called function. Is this right or will strlen still work inside the called function?
Legend
You could just use `strlen` in the function. I would actually leave it out, though. This allows you to make partial copies of a string, for example, or extract sub-strings.
GMan
Oh... understood... Thanks again...
Legend
You are returning a pointer to the \0 byte at the end of "dest" and not the beginning of the allocated buffer!
jmucchiello
This also can suffer an overrun: char*x = mystringcopy("abcde", 1); The loop with gladly copy 6 bytes into the allocated 2 byte buffer.
jmucchiello
I refer to the alternate method for both of those comments.
jmucchiello
Finally the for loop is usually a while loop: while (*dest++ = *src++) {} Although in this case it should be while (length--
jmucchiello
@jmucchiello: I tend to only fix peoples immediate code (The allocation of a buffer), not usability issues. I'll add yours. (Though the return value was obviously incorrect.)
GMan
You may disagree. But I think you have some responsibility to the guy who comes to the site with the same problem and copy/pastes your solution without necessarily understanding it. Either document "there may be other bugs in this code but as to your question...." or make your solution as correct as possible.
jmucchiello
+2  A: 

There is no problem in allocating inside a function.

The problem is that in C arguments are passed by value. So when you assign a value to dest, that's only modifying the dest local to the function.

You have two choices. You can return the dest pointer:

char *alloc_and_copy(const char *src, size_t length)
{
    char *dest = malloc(length + 1);
    ... do your copying
    return dest;
}

or you can pass a pointer to the argument and modify what's being pointed to:

void alloc_and_copy(const char *src, size_t length, char **dest)
{
    char *local_dest = malloc(length + 1);
    ... do your copying using local_dest

    *dest = local_dest;
}

The technique of using a local variable is not required, but I think it makes for more readable code.

R Samuel Klatchko
Assign *dest before you modify local_dest in order to walk the string.
jmucchiello
@jmucchiello - the idea is to do everything in the function with local_dest and only copy it over at the end (otherwise, it adds little value). I've clarified my comment to make that more explicit.
R Samuel Klatchko
+2  A: 

Doing the malloc inside the function is OK, but you're not passing the pointer back out of the function. Either return the pointer:

char * mystringcopy(char *src)

or pass a pointer to the pointer:

void mystringcopy(char *src, char **dest)
Mark Ransom
+1  A: 

In general, when allocating memory there are certain assumptions about what code is responsible for freeing the memory when it's done. I subscribe to the notion that a function should be responsible for one major operation, as if a black box. For both reasons, it is best to allocate your own memory and hand the pointer to the function to populate its buffer.

That aside, you could either return the char * pointer as a return value.

Or, change the char *dest parameter to char **dest. Then, call the function like: mystringcopy(src, strlen(src), *dest). And in the function, it returns the pointer by: *dest = (char *)malloc(length + 1);. Not pretty.

spoulson
+2  A: 

Parameters in C are passed by value, so your function gets a copy of the dest pointer, overwrites it with malloc then discards it. Try this instead:

void mystringcopy(char *src, size_t length, char **dest) {
   *dest = (char *)malloc(length + 1);
   char *p=*dest;
   for(; (*p = *src) != '\0'; ++src, ++p);
}

Now you pass a pointer to the pointer to your string, so you can overwrite it in the main procedure. You'd use it like:

char *src, *dest;
*src = (char *) malloc(BUFFSIZE);
//Do something to fill the src
mystringcpy(src, strlen(src), &dest);
// now in dest you have your copy
Blindy