¿Es este un método correcto usando free () en C?

If I have this function foo() and I'm calling it from another function foo2(); must I free the memory in the calling function like this?

char *foo(char *str1){
    char *str2;
    str2 = malloc((sizeof(char) * strlen(str1)) + 1);
    memcpy(str2, str1, strlen(str1) + 1)
    return str2;
}

void foo2(void){
    char *str1 = "Hello world!"
    char *str2;
    str2 = foo(str1);
    ...some stuff
    free(str2); //Should I do this here?
}

preguntado el 09 de marzo de 12 a las 16:03

C and C++ are not the same thing. I removed the C++ tag, since you mention C, and the code posted is clearly not C++. -

¿Por qué no puedes usar memcpy() directamente en foo2? And wouldn't it depend on what you're using str1 y str2 ¿por? -

La multiplicación por sizeof(char) is a little odd. Logically, it should be sizeof(char) * (strlen(str1) + 1), since the extra 1 is also a char. OTOH, since sizeof(char) == 1 by definition, it is not necessary. Were you messing with arrays of structures, and you needed the +1, it would be crucial to multiply it all by the size of the structure. -

@ToddMurray Wrong. C and C++ have a common subset. But that subset no es C. This code here even happens to be proof of that. Try compiling it in a C++ compiler and you'll see why. char* str = malloc((sizeof(char) * strlen(str1)) + 1); is not valid C++. ideone.com/Pcx5P -

@ToddMurray But that's no longer the same code. The code without the cast is valid C. You can't say that "C code is C++ code" when you mean "C code if you change some things is C++ code". -

5 Respuestas

Yes, except you don't test that your malloc is successful

respondido 09 mar '12, 16:03

You can free the allocated memory anywhere given the fact that you did the allocation, that the allocation was successful, and that the memory was not already freed.

respondido 09 mar '12, 16:03

I'd add to that for the OP's benefit that my general rule of thumb though is to attempt to free the memory in the same "unit" where I allocated it. My definition of unit is fairly loose here, and corresponds more to my internal conception of how the program is put together than any particular syntactic construction. - deong

@deong: This basically makes any sort of object oriented programming in C impossible, since the standard idiom for that is to have a function that "creates an object" of some type via malloc and filling in initial values, then returns a pointer, which the caller would later pass to a function for "destroying" that type of object. - R .. GitHub DEJA DE AYUDAR A ICE

@R: That's sort of what I meant by having a very loose definition of "unit". I'm not sure of a better way to describe the point, maybe "symmetry" captures some of what I'm going for. Allocate at the beginning of a block, deallocate at the end is symmetrical. Allocate in a create method, deallocate in a free method is symmetrical. Allocate in a create function, deallocate in the middle of some logic somewhere else isn't and feels wrong. That's also what I was getting at by saying it's more of a conceptual level than a syntactic one. - deong

Yeah that would work, however you have a bug:

char *foo(const char *str1)
{
    char *str2 = (char *)malloc((sizeof(char) * strlen(str1)) + 1);
    if (str2 != NULL)
        memcpy(str2, str1, strlen(str1) + 1);
    return str2;
}
void foo2
{
    char *str1 = "Hello world!"
    char *str2;
    str2 = foo(str1);
    ...some stuff
    free(str2);
}

Conventionally you would document the fact that the caller is responsible for freeing the returned pointer using free().

También tu foo() la función no es más que strdup().

respondido 09 mar '12, 16:03

thanks it's not tested is only a example to explain this better - Luis Rossell

Yes, it is right. foo() allocates some memory and it must be freed by the caller. It's not a very good design but it works. It could be better if foo() accepts two parameters: output buffer and its size.

void foo(char* input, char* output, int* bufferSize);

If output is NULL the required size is written in bufferSize by foo().

respondido 09 mar '12, 16:03

The original is a perfectly valid design. Your alternative is just about valid, though more unwieldy. Frankly, the pointer to size would annoy the living daylights out of me. I'd want to use foo(var_to_copy, buffer, sizeof(buffer)); and not being able to do so would be irksome in the extreme. It would be far better (IMNSHO) for foo() to insist on assert(output != 0 && bufferSize > 0);. And having the function return the pointer it is passed can be useful, too: char *foo(char const *input, char *output, size_t outlen);. - jonathan leffler

I guess we're talking about opinions (more or less) but... I understand the pointer to size is annoying whenever you know the maximum size allowed (but in C you may provide a helper macro to hide it). That function style doesn't come to make code more elegant (and short) but more safe. I really agree with the return type but I would prefer an error code ('cause it may fail) instead of a pointer to the argument. ANYWAY...after all I guess the answer is coherencia with the rest of the application. - Adriano Repetti

It seems the OP agrees with you. Yes, it is subjective, depending on the application and personal or project coding style. Being able to interrogate for the required length can be helpful in other contexts than this one. - jonathan leffler

@JonathanLeffler: Also, int * is a horribly wrong type for a buffer size. At the very least it should be size_t *... - R .. GitHub DEJA DE AYUDAR A ICE

Yes. The memory needs to be freed at some point. As you can see, you now have a tight coupling between the foo function and any of its callers. One common alternative is to pass in a pointer to a char array and the size of it. The function then returns whether it filled out the array properly. This moves the responsibility of both allocating and freeing to the caller.

bool * foo(char * str, size_t size)
{
  if(size < FOO_REQUIRED_SIZE) {
    return FALSE;
  } else {
    ...
    return TRUE;
  }
}

void foo2(void)
{
  char str[FOO_REQUIRED_SIZE];
  foo(str, ARRAY_SIZE(str));
}

Instead of simply returning FALSE if the function failed, you can furthermore document the required size of the array. Or alternatively provide a function which calculates the required size and returns it.

size_t getRequiredBufSizeForFoo()
{
  // Calculate how many bytes required
  return ...;
}

void foo2(void)
{
  size_t len = getRequiredBufSizeForFoo();
  char str[len];

  foo(str, len);
}

respondido 09 mar '12, 16:03

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.