char* as an argument to a function in C - c

When passing a char* as an argument to a function, should the called function do a free on that string? Otherwise the data would be "lost" right and the program would leak data. Or are char* handled in a special way by the compiler to avoid everyone from having to do free all the time and automatically deletes it one it goes out of scope? I pass "the string" to the function so not an instance to an already existing char*. Or should one use char[] instead? Just feels so dumb to set a fixed limit to the argument input.

Keep this simple principle in mind: "always free memory at the same level that you allocated it". In other words a function should never try to free memory that it itself has not allocated. A short example to clarify this:
#include "graphics.h"
// The graphics API will get a Canvas object for us. This may be newly allocated
// or one from a pool of pre-allocated objects.
Canvas* canvas = graphics_get_canvas ();
// If draw_image () frees canvas, that violates the above principle.
// The behavior of the program will be unspecified. So, just draw the image
// and return.
draw_image (canvas);
// This is also a violation.
// free (canvas) ;
// The right thing to do is to give back the Canvas object to the graphics API
// so that it is freed at the same 'level' where it was allocated.
graphics_return_canvas (canvas);
Note that the function is not named graphics_free_canvas () or something like that, because the API may choose to free it or reuse it by returning it to a pool. The point is, it is an extremely bad programming practice to assume ownership of a resource that we did not create, unless we are specifically told otherwise.

It sounds like you're asking about this usage:
void foo(char* str);
foo("test string");
This is a special case; "test string" is a constant string stored in the string table within the executable, and doesn't need to be freed. foo should actually take a const char* to illustrate that, and allowing string literals to be stored in non-constant char*s is deprecated in C++

Whether the function should do a free or not depends on who owns the string. This code is perfectly valid and doesn't result in any memory leak:
int main()
{
char* s = malloc(.....);
f(s);
free(s);
}
The free can be performed inside function f as well if it takes the ownership of the string. However note that it is dangerous since you are assuming that string passed to function f is always allocated on heap using malloc or related functions. If a user passes pointer to a string allocated on stack your program will behave unpredicably.
On a general note, compiler doesn't do any special handling for memory management of strings. From compiler's point of view it is just a bunch of characters.

It seems that you are used to OOP style. I don't like the OOP, and for me it would be weird if I'd obtain a copy of an object after assigning. In this case the string is somewhere in memory, and its address is sent as char*, and not the whole string.
Also, be careful that you can free() only the pointers returned by malloc(), and only once.

Sometime a API expects a allocated buffer and its upto to the owner of the function which calls that api to free it.
myFunc()
{
char *error = malloc(<max size of error string>);
foo(error);
//Free the pointer here
free(error);
}
Some API like GLIB api's expect pointer to address of a declared variable
myFunc()
{
GError *error;
glib_api(&error);
if (error)
{
printf("Error %s", error-> message);
// can use glib API to free if error is NON NULL but message is allocated by GLIB API
g_error_free(error);
}
}
So even if you have not allocated memory to a variable you need to do the freeing while using standard libraries.
An allocated piece of memory, if not freed will result in lesser memory in a multiprocess environment, thereby degrading the performance of the system.

With plain char *, I would recommend always writing code with a policy that the caller "owns" the string and is responsible for freeing it if it was obtained by malloc. On the other hand, one could certainly envision "pseudo-pass by value" string objects in C, implemented as a struct, where policy dictates you have to relinquish ownership of a string (or duplicate it first and pass the duplicate) when passing strings as arguments. This could work especially well if the implementation used reference-counted storage for strings where the object passed was just a reference to the storage, so that the "duplicate" operation would merely be a reference-count increment plus trivial wrapper-struct allocation (or even pass-by-value struct).

The pointer to char as a function variable is an address to the same variable except where it's been substituted as a constant string. Your question can't be explained with a simple yes/no guideline; it depends on context. In the code below, a struct allocated on heap and stack individually are passed by reference as well as a string char * and data is inserted into the struct. Notice how the mallocs differ as to when they are used but the function works all the same.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// struct with all data within
typedef struct d
{
int number;
char name[50];
}data;
// struct with dynamic char *
typedef struct d2
{
int number;
char *name;
}dynamic_data;
// generic function placing data into struct
void InsertData ( data * out, int a, char * b )
{
out->number = a;
strcpy(out->name, b);
}
// generic function placing data into second struct
void InsertData2 ( dynamic_data * out, int a, char * b )
{
out->number = a;
strcpy(out->name, b);
}
int main ( void )
{
char * text = "some string\0";
int n = 20;
// allocated struct
data stuff;
dynamic_data stuff2;
dynamic_data * stuff3;
// need to allocate pointer within struct only
stuff2.name = (char *) malloc(50 * sizeof(char));
// heap allocated struct
stuff3 = (dynamic_data * ) malloc(50 * sizeof(dynamic_data));
// heap allocated sub element char *
stuff3->name = (char *) malloc(50 * sizeof(char));
// this is the data
printf ( "Pre insertion data\n" );
printf ( "s=[%s]\n", text );
printf ( "n=%d\n", n );
// this is the function insertting
InsertData ( &stuff, n, text );
printf ( "Post insertion data\n" );
printf ( "stuff.name=[%s]\n", stuff.name );
printf ( "stuff.number=%d\n", stuff.number );
// this is the function inserting
InsertData2 ( &stuff2, n, text );
printf ( "Post insertion data\n" );
printf ( "stuff.name=[%s]\n", stuff2.name );
printf ( "stuff.number=%d\n", stuff2.number );
//
// This is the segfault version - if nothing was allocated for pointers into
// this function scope, it would crash
// this is the function insertting under a heap allocated
InsertData2 ( stuff3, n, text );
printf ( "Post insertion data - dynamic version\n" );
printf ( "stuff3->name=[%s]\n", stuff3->name );
printf ( "stuff3->number=%d\n", stuff3->number );
// free in reverse order
free(stuff3->name);
free(stuff3);
free(stuff2.name);
return 0;
}

Related

Returning allocated buffer, vs buffer passed to a function

When passing values to my functions, I often consider either returning an allocated buffer from my function, rather than letting the function take a buffer as an argument. I was trying to figure out if there was any significant benefit to passing a buffer to my function (eg:
void f(char **buff) {
/* operations */
strcpy(*buff, value);
}
Versus
char *f() {
char *buff = malloc(BUF_SIZE);
/* operations */
return buff;
}
These are obviously not super advanced examples, but I think the point stands. But yeah, are there any benefits to letting the user pass an allocated buffer, or is it better to return an allocated buffer?
Are there any benefits to using one over the other, or is it just useless?
This is a specific case of the more general question of whether a function should return data to its caller via its return value or via an out parameter. Both approaches work fine, and the pros and cons are mostly stylistic, not technical.
The main technical consideration is that each function has only one return value, but can have any number of out parameters. That can be worked around, but doing so might not be acceptable. For example, if you want to reserve your functions' return values for use as status codes such as many standard library functions produce, then that limits your options for sending back other data.
Some of the stylistic considerations are
using the return value is more aligned with the idiom of a mathematical function;
many people have trouble understanding pointers; and in particular,
non-local modifications effected through pointers sometimes confuse people. On the other hand,
the return value of a function can be used directly in an expression.
With respect to modifications to the question since this answer was initially posted, if the question is about whether to dynamically allocate and populate a new object vs populating an object presented by the caller, then there are these additional considerations:
allocating the object inside the function frees the caller from allocating it themselves, which is a convenience. On the other hand,
allocating the object inside the function prevents the caller from allocating it themselves (maybe automatically or statically), and does not provide for re-initializing an existing object. Also,
returning a pointer to an allocated object can obscure the fact that the caller has an obligation to free it.
Of course, you can have it both ways:
void init_thing(thing *t, char *name) {
t->name = name;
}
thing *create_thing(char *name) {
thing *t = new malloc(sizeof(*t));
if (t) {
init_thing(t);
}
return t;
}
Both options work.
But in general, returning information through the parameters (the second option) is preferable because we usually reserve the return of the function to report an error. And we can return several information trough multiple parameters. Hence, it is easier for the caller to check if the function was OK or not by checking first the returned value. Most of the services from the C library or the Linux system calls work like this.
Concerning your examples, both options work because you are referencing a constant string which is globally allocated at program's loading time. So, in both solutions, you return the address of this string.
But if you do something like the following:
char *func(void) {
char buff[] = "example";
return buff;
}
You actually copy the content of the constant string "example" into the stack area of the function pointed by buff. In the caller the returned address is no longer valid as it refers to a stack location which can be reused by any other function called by the caller.
Let's compile a program using this function:
#include <stdio.h>
char *func(void) {
char buff[] = "example";
return buff;
}
int main(void) {
char *p = func();
printf("%s\n", p);
return 0;
}
If the compilation options of the compiler are smart enough, we get a first red flag with a warning like this:
$ gcc -g bad.c -o bad
bad.c: In function 'func':
bad.c:5:11: warning: function returns address of local variable [-Wreturn-local-addr]
5 | return buff;
| ^~~~
The compiler points out the fact that func() is returning the address of a local space in its stack which is no longer valid when the function returns. This is the compiler option -Wreturn-local-addr which triggers this warning. Let's deactivate this option to remove the warning:
$ gcc -g bad.c -o bad -Wno-return-local-addr
So, now we have a program compiled with 0 warning but this is misleading as the execution fails or may trigger some unpredictible behaviors:
$ ./bad
Segmentation fault (core dumped)
You can't return the address of local memory.
Your first example works because the memory in "example" will not be deallocated. But if you allocated local (aka automatic) memory it automtically be deallocated when the function returns; the returned pointer will be invalid.
char *func() {
char buff[10];
// Copy into local memory
strcpy(buff, "example");
// buff will be deallocated after returning.
// warning: function returns address of local variable
return buff;
}
You either return dynamic memory, using malloc, which the caller must then free.
char *func() {
char *buf = malloc(10);
strcpy(buff, "example");
return buff;
}
int main() {
char *buf = func();
puts(buf);
free(buf);
}
Or you let the caller allocate the memory and pass it in.
void *func(char **buff) {
// Copy a string into local memory
strcpy(buff, "example");
// buff will be deallocated after returning.
// warning: function returns address of local variable
return buff;
}
int main() {
char buf[10];
func(&buf);
puts(buf);
}
The upside is the caller has full control of the memory. They can reused existing memory, and they can use local memory.
The downside is the caller must allocate the correct amount of memory. This might lead to allocating too much memory, and also too little.
An additional downside is the function has no control over the memory which has been passed in. It cannot grow nor shrink nor free the memory.
You can only return one thing from a function.
For example, if you want to convert a string to an integer you could return the integer like atoi does. int atoi( const char *str ).
int num = atoi("42");
But then what happens when the conversion fails? atoi returns 0, but how do you tell the difference between atoi("0") and atoi("purple")?
You can instead pass in an int * for the converted value. int my_atoi( const char *str, int *ret ).
int num;
int err = my_atoi("42", &num);
if(err) {
exit(1);
}
else {
printf("%d\n");
}

Return string from function that will be used to create a DLL

I need to create a DLL file that can be used for MS Access and other applications that will return a string when fed parameters. I am fairly familiar with MS Access but an absolute novice at C.
Following is the code I am experimenting with. I want to be able to issue a call like getstring(32.1, 123.2, "here", 25) and have it return a string of up to 60 characters in length. The actual code works fine and buf contains the string I want when it's finished running but I am having trouble handing it back to the calling function.
UPDATE:
Ok, I've worked out how to create a DLL and run a function from VBA but I am still struggling to understand how to return strings. I think if I can get this to work, I can work out my whole project. By running the following code I can get VBA to return the square of the input number e.g. feed it a parameter of 10 and I get an answer of 100
double _stdcall square(double *x)
{
return *x * *x;
}
However when I run the following code in Excel and feed it a parameter of "test" all I get back is a square box character.
char _stdcall Boxx(char *x)
{
return *x;
}
In this case all I want it to return is what I entered. If I can get it to return that I hope to be able to replace that with the actual result. Any suggestions?
char * Getstring(double lat, double lon, char *name, double zoom)
{
char buf[60] = { '\0' }; // Set the max length of the final link string
int ret = GenShortDroidMapUrl(lat, lon, zoom, name, buf, sizeof(buf) - 1);
return buf;
}
In the posted code, buf[] is an automatic variable whose lifetime ends after the Getstring() function has returned. Since buf[] will no longer exist when control of the program has returned to the caller, a pointer to this variable will be invalid after Getstring() has returned.
One solution is to pass an additional argument into the Getstring() function to accept the string, along with a size argument. Since buf will decay to a pointer in the function call, the sizeof operator can't be used in Getstring() to find the size of the array, but buf_sz holds this value:
char * Getstring(char *buf, size_t buf_sz, double lat, double lon, char *name, double zoom)
{
// buf[] has been zero-initialized in the caller
int ret = GenShortDroidMapUrl(lat, lon, zoom, name, buf, buf_sz - 1);
return buf;
}
Another option that does not require changing the function signature is to dynamically allocate storage for the returned string. Again, buf is a pointer to char in Getstring(), so the sizeof expression in GenShortDroidMapUrl() will need to be replaced; this time the constant BUF_SZ has been used here. Note that the malloced memory will need to be freed by the caller later.
#include <string.h>
#define BUF_SZ 60
/* ... */
char * Getstring(double lat, double lon, char *name, double zoom)
{
char *buf = malloc(sizeof *buf * BUF_SZ);
memset(buf, '\0', BUF_SZ);
/* Or use calloc() and avoid the call to memset() */
// char *buf = calloc(BUF_SZ, sizeof *buf);
int ret = GenShortDroidMapUrl(lat, lon, zoom, name, buf, BUF_SZ - 1);
return buf;
}
If Getstring() is part of a library, you need to ensure that the deallocator function matches the allocation functions. That is, there may be problems if the version of malloc() or calloc() that Getstring() is linked against differs from the version of free() that the calling code is linked against. One solution is to provide a deallocation function with the library. This could be as simple as wrapping free() in another function to be used by the caller to ensure that a matching deallocator is used. Here, the function DLL_Free() is part of the DLL, and malloc(), calloc(), and free() would all be linked against the same library when the DLL is created. The caller that uses Getstring() would use DLL_Free() to deallocate. From the caller, free() may not work as expected to deallocate the memory allocated by Getstring(), but DLL_Free() would since this deallocator uses the version of free() that matches the allocators used in the DLL.
/* Deallocation function included in DLL that matches allocation
* functions used in library
*/
void DLL_Free(void *ptr)
{
free(ptr);
}
There are many ways to return a string, but respect the lifetime of buffers:
Can a local variable's memory be accessed outside its scope?
One is to let the caller supply the buffer. Return how much space would have been needed, and a simple comparison will tell you whether it was enough.
Another is to use a static, optionally thread-local, buffer. Beware the restrictions on concurrency and reentrancy.
And finally, you can allocate it dynamically. Remember that it has to be freed with the same system, which on windows often means you have to manually export the way to free it from your DLL. Better not to reinvent the wheel, look at BSTRs for example.
You could either declare buf static and let the function return const char *. But that would not be reentrant. So another solution is to return strdup(buf), which will return a copy that the caller needs to free after use (otherwise you will have a memory leak).

When should one use dynamic memory allocation function versus direct variable declaration?

Below is an example of direct variable declaration.
double multiplyByTwo (double input) {
double twice = input * 2.0;
return twice;
}
Below is an example of dynamic memory allocation.
double *multiplyByTwo (double *input) {
double *twice = malloc(sizeof(double));
*twice = *input * 2.0;
return twice;
}
If I had a choice, I will use direct variable declaration all the time because the code looks more readable. When are circumstances when dynamic memory allocation is more suitable?
When are circumstances when dynamic memory allocation is more suitable?
When the allocation size is not known at compile time, we need to use dynamic memory allocation.
Other than the above case, there are some other scenarios, like
If we want to have a data-structure which is re-sizeable at runtime, we need to go for dynamic memory allocation.
The lifetime of dynamically allocated memory remains valid unless it is free()d. At times, it comes handy when returning some address of a variable from a function call, which , otherwise, with an auto variable, would have been out of scope.
Usually the stack size would be moderately limited. If you want to create and use an huge array, it is better to use dynamic memory allocation. This will allocate the memory from heap.
Dynamic memory allocation with malloc places the memory on the heap, so it is not destroyed when leaving the function.
At a later point you would need to manually free the memory.
Direct declaration lands on the stack and is deleted on leaving the function. What happens on the return statement is that a copy of the variable is made before it is destroyed.
Consider this example:
On heap
void createPeople():
struct person *p = makePerson();
addToOffice(p);
addToFamily(p);
Vs. on stack
void createPeople():
struct person p = makePerson();
addToOffice(p);
addToFamily(p);
In the first case only one person is created and added to office and family. Now if the person is deleted, it is invalidated in both office and family and moreover, if his data is changed, it is changed in both, too.
In the second case a copy of the person is created for the office and family. Now it can happen that you change data of the copy in office and the copy in family remains the same.
So basically if you want to give several parties access to the same object, it should be on the stack.
"If I had a choice, I will use direct variable declaration all the time"
As well you should. You don't use heap memory unless you need to. Which obviously begs the question: When do I need dynamic memory?
The stack space is limited, if you need more space, you'll have to allocate it yourself (think big arrays, like struct huge_struct array[10000]). To get an idea of how big the stack is see this page. Note that the actual stack size may differ.
C passes arguments, and returns values by value. If you want to return an array, which decays into a pointer, you'll end up returning a pointer to an array that is out of scope (invalid), resulting in UB. Functions like these should allocate memory and return a pointer to it.
When you need to change the size of something (realloc), or you don't know how much memory you'll need to store something. An array that you've declared on the stack is fixed in size, a pointer to a block of memory can be re-allocated (malloc new block >= current block size + memcpy + free original pointer is basically what realloc does)
When a certain piece of memory needs to remain valid over various function calls. In certain cases globals won't do (think threading). Besides: globals are in almost all cases regarded as bad practice.
Shared libs generally use heap memory. This is because their authors can't assume that their code will have tons of stack space readily available. If you want to write a shared library, you'll probably find yourself writing a lot of memory management code
So, some examples to clarify:
//perfectly fine
double sum(double a, double b)
{
return a + b;
}
//call:
double result = sum(double_a, double_b);
//or to reassign:
double_a = (double_a, double_b);
//valid, but silly
double *sum_into(double *target, double b)
{
if (target == NULL)
target = calloc(1, sizeof *target);
*target = b;
return target;
}
//call
sum_into(&double_a, double_b);//pass pointer to stack var
//or allocate new pointer, set to value double_b
double *double_a = sum_into(NULL, double_b);
//or pass double pointer (heap)
sum_into(ptr_a, double_b);
Returning "arrays"
//Illegal
double[] get_double_values(double *vals, double factor, size_t count)
{
double return_val[count];//VLA if C99
for (int i=0;i<count;++i)
return_val[i] = vals[i] * factor;
return return_val;
}
//valid
double *get_double_values(const double *vals, double factor, size_t count)
{
double *return_val = malloc(count * sizeof *return_val);
if (return_val == NULL)
exit( EXIT_FAILURE );
for (int i=0;i<count;++i)
return_val[i] = vals[i] * factor;
return return_val;
}
Having to resize the object:
double * double_vals = get_double_values(
my_array,
2,
sizeof my_array/ sizeof *my_array
);
//store the current size of double_vals here
size_t current_size = sizeof my_array/ sizeof *my_array;
//some code here
//then:
double_vals = realloc(
double_vals,
current_size + 1
);
if (double_vals == NULL)
exit( EXIT_FAILURE );
double_vals[current_size] = 0.0;
++current_size;
Variables that need to stay in scope for longer:
struct callback_params * some_func( void )
{
struct callback_params *foo = malloc(sizeof *foo);//allocate memory
foo->lib_sum = 0;
call_some_lib_func(foo, callback_func);
}
void callback_func(int lib_param, void *opaque)
{
struct callback_params * foo = (struct callback_params *) opaque;
foo->lib_sum += lib_param;
}
In this scenario, our code is calling some library function that processes something asynchronously. We can pass a callback function that handles the results of the library-stuff. The lib also provides us with a means of passing some data to that callback through a void *opaque.
call_some_lib_func will have a signature along the lines of:
void call_some_lib_func(void *, void (*)(int, void *))
Or in a more readable format:
void call_some_lib_func(void *opaque, void (*callback)(int, void *))
So it's a function, called call_some_lib_func, that takes 2 arguments: a void * called opaque, and a function pointer to a function that returns void, and takes an int and a void * as arguments.
All we need to do is cast the void * to the correct type, and we can manipulate it. Also note that the some_func returns a pointer to the opaque pointer, so we can use it wherever we need to:
int main ( void )
{
struct callback_params *params = some_func();
while (params->lib_sum < 100)
printf("Waiting for something: %d%%\r", params->lib_sum);
puts("Done!");
free(params);//free the memory, we're done with it
//do other stuff
return 0;
}
Dynamic memory allocation is needed when you intend to transport data out of a local scope (for example of a function).
Also, when you can not know in advance how much memory you need (for example user input).
And finally, when you do know the amount of memory needed but it overflows the stack.
Otherwise, you should not use dynamic memory allocation because of readability, runtime overhead and safety.

Error trying to change contents of string pointer in C

I'm working on a program in C and one of my key functions is defined as follows:
void changeIndex(char* current_index)
{
char temp_index[41]; // note: same size as current_index
// do stuff with temp_index (inserting characters and such)
current_index = temp_index;
}
However, this function has no effect on current_index. I thought I found a fix and tried changing the last line to
strcpy(current_index, temp_index)
but this gave me yet another error. Can anyone spot what I'm doing wrong here? I basically just want to set the contents of current_index equal to that of temp_index at each call of changeIndex.
If more information is needed, please let me know.
strcpy should work if current_index points to allocated memory of sufficient size. Consider the following example, where changeIndex require additional parameter - size of distination string:
void changeIndex(char* current_index, int max_length)
{
// check the destination memory
if(current_index == NULL)
{
return; // do nothing
}
char temp_index[41];
// do stuff with temp_index (inserting characters and such)
// copy to external memory, that should be allocated
strncpy(current_index, temp_index, max_length-1);
current_index[max_length-1] = '\0';
}
Note: strncpy is better for the case when temp_index is longer then current_index.
Examples of usage:
// example with automatic memory
char str[20];
changeIndex(str, 20);
// example with dinamic memory
char * ptr = (char *) malloc(50);
changeIndex(ptr, 50);
Obviously defining a local char array on the stack and returning a pointer to it is wrong. You should never do that as the memory is not defined after the function ends.
In addition to the previous answers: The strncpy char pointer (which seems unsafe for my opinion), and the malloc which is safer but you need to remember to free it outside of the function (and its inconsistent with the hierarchy of the program) you can do the following:
char* changeIndex()
{
static char temp_index[41]; // note: same size as current_index
// do stuff with temp_index (inserting characters and such)
return temp_index;
}
As the char array is static it will not be undefined at the end of the function and you do not need to remember to free the pointer at the end of the use.
Caveat: If you are using multiple thread you cannot use this option as the static memory could be changed by different threads entering the function at the same time
Your array temp_index is local for function, then *current_index don't take what u want.
U can use also function strdup . Function return begin memory location of copied string , or NULL if error occurred, lets say ( char *strdup(char *) )
char temp[] = "fruit";
char *line = strdup(temp );

Pointers and assignment in a sub-function

I have a small program that creates a semver struct with some variables in it:
typedef struct {
unsigned major;
unsigned minor;
unsigned patch;
char * note;
char * tag;
} semver;
Then, I would like to create a function which creates a semver struct and returns it to the caller. Basically, a Factory.
That factory would call an initialize function to set the default values of the semver struct:
void init_semver(semver * s) {
s->major = 0;
s->minor = 0;
s->patch = 0;
s->note = "alpha";
generate_semver(s->tag, s);
}
And on top of that, I would like a function to generate a string of the complete semver tag.
void generate_semver(char * tag, semver * s) {
sprintf( tag, "v%d.%d.%d-%s",
s->major, s->minor, s->patch, s->note);
}
My problem appears to lie in this function. I have tried returning a string, but have heard that mallocing some space is bad unless you explicitly free it later ;) In order to avoid this problem, I decided to try to pass a string to the function to have it be changed within the function with no return value. I'm trying to loosely follow something like DI practices, even though I'd really like to separate the concerns of these functions and have the generate_semver function return a string that I can use like so:
char * generate_semver(semver * s) {
char * full_semver;
sprintf( full_semver, "v%d.%d.%d-%s",
s->major, s->minor, s->patch, s->note);
return full_semver; // I know this won't work because it is defined in the local stack and not outside.
}
semver->tag = generate_semver(semver);
How can I do this?
My problem appears to lie in this function. I have tried returning a string, but have heard that mallocing some space is bad unless you explicitly free it later.
Explicitly freeing dynamically allocated memory is required to avoid memory leaks. However, it is not necessarily a task that the end users need to perform directly: an API often provides a function to deal with this.
In your case, you should provide a deinit_semver function that does the clean up of memory that init_semver has allocated dynamically. These two functions behave in a way that is similar to constructor and destructor; init_semver is not a factory function, because it expects the semver struct to be allocated, rather than allocating it internally.
Here is one way of doing it:
void init_semver(semver * s, int major, int minor, int pathc, const char * note) {
s->major = major;
s->minor = minor;
s->patch = pathc;
size_t len = strlen(note);
s->note = malloc(len+1);
strcpy(s->note, note);
s->tag = malloc(40 + len);
sprintf(s->tag, "v%d.%d.%d-%s", major, minor, patch, note);
}
void deinit_semver(semver *s) {
free(s->note);
free(s->tag);
}
Note the changes above: rather than using fixed values for the components of struct semver, this code takes the values as parameters. In addition, the code copies the note into a dynamically allocated buffer, rather than pointing to it directly.
The deinit function does the clean-up by free-ing both fields that were allocated dynamically.
A char * on its own is just a pointer to memory. To accomplish what you want you will either need to instead use a fixed size field, i.e. char[33], or you can dynamically allocate the memory as needed.
As it is, your generate_semver function is attempting to print to an unknown address. Let's look at one solution.
typedef struct {
unsigned major;
unsigned minor;
unsigned patch;
char note[32];
char tag[32];
} semver;
Now, in your init_semver function, the line previously s->note = "alpha"; will become a string copy, as arrays are not a valid lvalue.
strncpy(s->note, "alpha", 31);
s->note[31] = '\0';
strncpy will copy a string from the second parameter to the first up to the number of bytes in the third parameter. The second line ensures that a trailing null terminator is in place.
Similarly, in the generate_semver function, it would directly work in the buffer:
void generate_semver(semver * s) {
snprintf( s->tag, 32, "v%d.%d.%d-%s",
s->major, s->minor, s->patch, s->note);
}
This will directly print to the array in the structure, with a maximum character limit. snprintf does append a trailing null terminator (unlike strncpy), so we don't need to worry about adding it ourselves.
You mention having to free allocated memory, and then say: "In order to avoid this problem". Well, it's not so much a problem, but rather a necessity of the C language. It's common to have functions that allocate memory, and require the caller to free it again.
The idiomatic way is to have a pair of "create" and "destroy" functions. So I'd suggest doing it like this:
// Your factory function
semver* create_semver() {
semver* instance = malloc(sizeof(*instance));
init_semver(instance); // will also allocate instance->tag and ->note
return instance;
}
// Your destruction function
void free_semver(semver* s) {
free(semver->tag);
free(semver->note);
free(semver);
}

Resources