I have the following function which gets called multiple times in my code:
char* get_section_name(const char* section, const char* value) {
char *tmp = (char *)malloc(STR_LEN * sizeof(char));
if(strlen(section)>0) {
strcat(tmp, section);
strcat(tmp,".");
}
strcat(tmp, value);
return tmp;
}
and I call it in other functions like this:
section_name = get_section_name(data->model_name,"params_default");
What is the best way to free this memory? Can I just call free(section_name) when I am done?
Yes free, however, you could consider a different name that makes it clear it is allocating memory. Also, you could make use of sprintf for combining 2 strings and strdup for just copying one.
char* create_section_name(const char* section, const char* value) {
const int sectionLen = strlen(section);
if(sectionLen>0) {
//+1 for the period and +1 for the null
char *tmp = (char *)malloc((sectionLen + strlen(value) + 2) * sizeof(char));
//do that often? consider a newString(size) macro. See below
sprintf(tmp, "%s.%s", section, value);
return tmp;
}
else {
return strdup(value);
}
}
This assumes you don't need the full STR_LEN, it uses just enough in both cases.
newString macro I suggested:
#define newString(size) (malloc((size) * sizeof(char)))
Or it can even automatically add one for the null:
#define newString(size) (malloc(((size)+1) * sizeof(char)))
Then malloc is replaced with this:
char *tmp = newString(sectionLen + strlen(value) + 1); //+1 for period
First, you must make sure tmpwas actually allocated (ie mallocdid not fail) :
tmp = (char *)malloc(STR_LEN * sizeof(char));
if (tmp == NULL) {
// quit now !
}
Then, as you strcat it, you must be sure tmp is an empty string, ie its first character is 0
tmp[0] = '\0';
Then, yes, you can free it the way you wrote it.
One last thing : you have to be sure that strlen(section)+strlen(".")+strlen(value) < STR_LEN, or you will overwrite memory you're not supposed to.
free would be great here, but as an alternative, might be the best way to do this would be a change to signature. If you make it like
void get_section_name(const char* section, const char* value, char * result)
then you can pass a pointer to an allocated memory, so the user of this function is perfectly aware of how should he handle the memory after it's used.
Always perform error check when creating memory using malloc(),calloc(), orrealloc()
Yes you can use free here
free(section_name) , Because tmpreturned is stored in section_name which now points to malloced memory.
I am going to make a leap of faith that STR_LEN is going to be of sufficient size. If so then free(section_name); will suffice. Bur use strcpy instead of strcat or initialize a null string.
Related
I'm having some issue with my stack implementation, my push function manipulate the value i send into the function and changes it. I have tried diffrent ways of constructing this but they either don't work or give me corrupted output.
The base idea is the one below here, note: my pop function only walks down one position and doesn't free the memory at the specific position. I can not use strcpy since im working with threads.
Does strdup change the value that it copies, i cant find any information saying that is the case, my understanding is that you are suppose to be able to use the value after it has ben duped.
And how is the correct way to use strdup on a already allocated memory space, i assume that i can't just free it and then use it again.
void stack_push(Stack *s, char *value)
{
if (s->size == s->capacity) {
realloc_stack(s);
}
if(s->data[s->size] == NULL){
// The current position does not contain any data.
s->data[s->size] = strdup(value);
}
else{
free(s->data[s->size]);
s->data[s->size] = strndup(value, strlen(value) + 1);
}
s->size += 1;
}
Edit s->data = char **data
strdup is basically this (no error checking for brevity):
char *strdup(const char *stringtoduplicate)
{
char *newstring = malloc(strlen(stringtoduplicate) + 1);
strcpy(newstring, stringtoduplicate);
return newstring;
}
You use it like this:
char Foo[] = "Bar";
char *newBar = strdup(Foo);
Foo[0] = 'F';
printf("%s %s\n", Foo, newBar); // prints: Far Bar
...
free(newBar); // once you're done with newBar, free it
Now you should be able to answer your own question.
strdup does not in any way modify its argument. If you look at the prototype for strdup you will see that its parameter is declared const, which means that it is not modified.
strdup can be implemented as:
char* strdup(const char* s) {
char* n = malloc(strlen(s) + 1);
if (n) strcpy(n, s);
return n;
}
There is no magic.
You can use strcpy with threads, by the way. But strdup works fine.
Note: I am not 100% confident with memory allocation and how it works, but I understand it for the most part.
When I see realloc() being used, I often see people using a new pointer to store the address space instead of using the same/old one. I am wondering if that is necessary.
Am I able to do the following?
char *string;
string = malloc(5 * sizeof(char));
...
string = realloc(string, 10 * sizeof(char));
More importantly, is this a safe thing to do?
(1) To answer the question of your title: no it is not, because realloc can give you a different pointer.
(2) To answer the question of your code: Neither. If realloc returns 0, meaning it can not honor your request to increase the size, you have lost your value of string but your old string has not been deallocated (memory leak).
Ad. 1: if you could realloc inside a function then you must either pass a double pointer or return the object.
Ad. 2: an example is:
char *string, *tmp;
string = malloc(5 * sizeof(char));
...
if ((tmp = realloc(string, 10 * sizeof(char))) == 0) {
printf("Could not allocate more memory\n);
return;
}
else
string= tmp;
Have a
typedef struct person {
char name[20]
char surname[20]
} person_t;
I need to create a string like XXXXXX:YYYYYY with the function like
char* personToString(person_t *p). I tried to make it:
char* personToString(person_t* p) {
int n1,n2;
n1=strlen(p->name);
n2=strlen(p->surname);
char *p = (char*) malloc((n1+n2+2)*sizeof(char));
strcat(p,puser->name);
strcat(p,":");
strcat(p,puser->surname);
return p;
}
This give me a reasonable output but I have some errors testing with valgrind! I also think that there is a way more classy to write the function!
When you malloc memory for p the memory will hold garbage values. Strcat will append a string after the null character, but in an uninitialized string will hold random values.
Replace the first strcat with strcpy.
You need to
strcpy(p,puser->name);
not
strcat(p,puser->name);
malloc does not initialize the buffer to zero, so strcat is searching for a null byte in p first and probably not finding one, reading past the end of the buffer and thus crashing.
Instead of one strcpy plus two strcat you can also write one call to sprintf:
sprintf(p, "%s:%s", puser->name, puser->surname);
First you should call string copy, then strcat:
strcat(p,puser->name);
should be:
strcpy(p,puser->name);
because memory allocated with malloc function keeps values garbage, by doing strcat for first you are concatenating after garbage -- it also brings Undefined behaviour in your code.
You can use void* calloc (size_t num, size_t size); instead of malloc(), calloc function initialized allocated memory with 0 (then strcat() no problem).
Also dynamically allocated memory you should deallocate memory block using void free (void* ptr);) explicitly.
This looks good to me,
char* personToString( struct person_t *p )
{
int len = strlen(p->name) + strlen(p->surname) + 2; // holds ':' + NULL
char *str = malloc( len ); // Never cast malloc's return value in C
// Check str for NULL
if( str == NULL )
{
// we are out of memory
// handle errors
return NULL;
}
snprintf( str, len, "%s:%s", p->name, p->surname);
return str;
}
NOTE:
Never cast malloc's return value in C.
Use snprintf when multiple strcat is needed, its elegant.
free the return value str here in caller.
Fixed struct and char variables.
I was asked to make 2 functions copyString and concatString i did and Implemented them both but at the output that i got תi have been told that it can be done better but never got explained how.
now it is killing me what could i do better so here is the code and i will be happy to hear any suggestions.
void copyString (char **strDst, const char *strSrc)
{
char *strTmp = NULL;
int length = strlen (src);
if (*strDst== NULL)
{
*strDst= malloc (length);
}
else
{
if (strlen(*strDst) != length)
{
strTmp = *strDst;
}
*strDst= malloc (length);
}
strcpy (*strDst, strSrc);
if (strTmp != NULL)
free (strTmp );
}
void concatString (char **strDst, const char *cat)
{
int cat_length = strlen (cat);
if (cat_length > 0)
{
*strDst= realloc (*strDst, strlen (*strDst) + cat_length);
strcat (*strDst, cat);
}
}
void main(int argc, char *argv[])
{
char *str = NULL;
copyString(&str, "Hello World");
puts(str);
copyString(&str,str+6);
puts(str);
concatString(&str, " Pesron");
}
The ouput should be as following:
1.Hello World
2. World
3. World Person
Thanks.
Errors:
strlen returns the length excluding the nul terminator, so all of your sizes that you allocate are too small.
In the case where if (strlen(*strDst) != length) is false, (that is, the lengths are equal) you leak the old buffer.
realloc and malloc can both fail, you should be able to write code to cope with that.
The correct way to use realloc is:
char *newbuf = realloc(oldbuf, newsize);
if (newbuf == NULL) {
// handle the error somehow, and note that oldbuf is still allocated
} else {
oldbuf = newbuf;
}
"Handle the error somehow" might require deciding what to do, depending on what the documentation of your two functions says they do on failure. If it doesn't say then it should.
(Picky) int is not guaranteed to be a large enough type to hold the length of a string. Use size_t (unless maybe you've been strictly forbidden from using unsigned types, in which case there's ssize_t).
Things you can improve:
There's no need to use strTmp the way you do, you could free the string immediately instead of at the end of the function. [Edit: yes there is a need, there seems to be a requirement that copyString but not concatString should permit overlap of source and destination. Personally I'd still write it slightly differently.]
In if (strTmp != NULL) free (strTmp ); the test is redundant since it is valid to call free with a null pointer, and doing so has no effect.
You do *strDst= malloc (length); in both cases in copyString.
main leaks memory since it never frees str.
main should return int, not void.
Here's how I might write them:
Since you can't change the calling code to make it check for error, you have to either abort() or else write something there that it can call puts on. Since the main function was written on the assumption that the calls cannot fail, abort() is probably the least bad solution.
It would probably be better for the caller if the functions return a value indicating success or failure, but we're constrained by the existing calling code. To be honest that's not a totally unrealistic situation to be programming for...
void concatString (char **strDst, const char *cat) {
size_t dstlen = *strDst ? strlen(*strDst) : 0;
char *buf = realloc(*strDst, dstlen + strlen(cat) + 1);
if (!buf) {
abort();
}
strcpy(buf + dstlen, cat);
*strDst = buf;
}
void copyString (char **strDst, const char *strSrc) {
char *buf = malloc(strlen(strSrc) + 1);
if (!buf) {
abort();
}
strcpy(buf, strSrc);
free(*strDst);
*strDst = buf;
}
Besides of what Steve Jessop mentions in his answer, no errors in your sources but missing:
validation of input parameters
return errors through error value (for example as integer return code of the function, instead of void
I have two functions, one that creates a pointer to a string and another that manipulates it. I somehow am missing something critical, however:
int foo() {
char * mystring; // Create pointer
bar(&mystring); // Pass in address
printf("%s\n", mystring);
return 0; // There's a reason for this
}
int bar(char ** mystring) {
mystring[0] = malloc(strlen(mystring) + 1); // Since malloc will persist even after exiting bar
*mystring = "hahaha"; // Dereference
return 0;
}
Any enlightenment for my addled brain would be greatly appreciated!
C doesn't have strings as first class values; you need to use strcpy() to assign strings.
strcpy(mystring[0], "hahaha");
In addition to the other answers given, note that:
mystring[0]
is the same as
*(mystring + 0)
which is the same as
*mystring
Your malloc allocates the memory and the pointer is written to mystring but it is overwritten by the next line.
The use of malloc is necessary, but this way:
mystring[0] = malloc(strlen(mystring) + 1);
is wrong, since you can't perform strlen on mystring(because it doesn't contain any string yet and because the pointer itself is not initialized). Allocate buffer with the size of your string. for example:
int bar(char ** mystring) {
char* hello = "hahaha";
*mystring = malloc(strlen(hello) + 1);
strcpy(*mystring, hello);
return 0;
}
BTW, you could use the assignment *mystring = "hahaha"; without the malloc since this is a string stored in the data section, and the data will not be lost after returning from the function, but this way it is read-only data and you cannot modify it. The strcpy is there to copy the string to the allocated buffer.