I got some C code:
typedef struct {
size_t len;
size_t alloclen;
char *buf;
} str;
void strnappnd(str **s, const char *buf, size_t n) {
if ((*s)->len + n >= (*s)->alloclen) {
size_t nalloclen = (*s)->len + n + 1;
void *tmp = realloc((*s)->buf, nalloclen);
if (!tmp) {
printf("failure");
exit(-1);
}
(*s)->buf = tmp;
(*s)->alloclen = nalloclen;
}
memccpy((*s)->buf + (*s)->len, buf, '\0', n);
(*s)->len += n;
(*s)->buf[(*s)->len] = '\0';
}
void strfree(str **s) {
free((*s)->buf);
free(*s);
*s = NULL;
}
Apparently, the strnappnd leaks at the realloc line. Why?
Consider:
void f() {
str *s = (str *)malloc(sizeof(str));
s->len = 5;
s->alloclen = 5;
s->buf = strdup("Hello");
strnappend(&s, " World!", 7);
free(s); /* courtesy of Eric */
}
If you had something like that, the memory allocated by realloc() would leak as f() is left.
If you wrote
(*s)->buf = realloc((*s)->buf, nalloclen)
that would be a memory leak, because if realloc fails and returns NULL, you lose the (*s)->buf pointer, which is still pointing to allocated memory.
Since you exit on failure it's not a problem, but maybe your static analyser ignores the exit?
Like this mtrace said "No memory leaks"
char *strnappnd(str **s, const char *buf, size_t n) {
...
return (*s)->buf;
}
void strfree(str *s){
free(s->buf);
free(s);
}
using the sample code given by Frerich
void f() {
str *s = (str *)malloc(sizeof(str));
s->len = 5;
s->alloclen = 5;
s->buf = strdup("Hello");
s->buf = strnappend(&s, " World!", 7);
strfree(s);
}
You create strfree() function and it is not used inside the code.
The memory need to be free always, if it is not used.
if (!tmp) {
printf("failure");
if (!(*s) && !((*s)->buf))
strfree(&(*s));
exit(-1);
}
Looking strfree(), looks as you reserved memory for *s too in somewhere.
Do the same before your code finish.
if (!(*s) && !((*s)->buf))
strfree(&(*s));
Related
char *ft_substr(char const *s, unsigned int start, size_t len)
{
size_t i;
size_t d;
char *subs;
i = 0;
d = 0;
subs = (char *)malloc(sizeof(char) * (len + 1));
if (!subs || !s)
{
return NULL;
}
while (s[i])
{
if (i >= start && d < len)
{
subs[d] = s[i];
d++;
}
i++;
}
subs[d] = '\0';
return subs;
}
The code that appears above is the substr function I wrote, when I checked the memory leak using Memd, I noticed that there was an error on the 14th line.
subs = (char *)malloc(sizeof(char) * (len + 1));
Unfortunately, I do not have the authority to install Valgrind on the device I use, so I was able to test with memd. I'm fairly new to C, can you explain why I'm getting a memory leak error and how can I fix it?
The key to solving this would be to add a guard to the beginning of your function, before you allocate for subs which may never be returned and thus be unable to be freed.
if (!s) return NULL;
You can still check later when subs is allocated and return NULL if that allocation fails.
subs = (char *)malloc(sizeof(char) * (len + 1));
if (!subs || !s)
{
return (NULL);
}
I the sub-statement of the if statement will get the control when s is equal to NULL when there will be a memory leak.
Pay attention to that you may remove the check whether s is a null pointer as all C standard string functions do. If the user will call the function passing a null pointer then there will be undefined behavior.
And the call of malloc can allocate more memory then it is required.
Also the types of the second and third parameters are inconsistent.
char *ft_substr(char const *s, unsigned int start, size_t len)
The both should have the type size_t.
If you want to make it safe you need not only to check if s is not NULL but also if the start is not past the length of the string.
Try to have only one return point from the function and rather use positive checks (ie if something is valid, instead of checking if it is invalid).
char *ft_substr(char const * restrict s, const size_t start, const size_t length)
{
char *subs = NULL;
if(s)
{
size_t len = strlen(s);
if(len >= start)
{
subs = malloc(length + 1);
if(subs)
{
char *wrk = subs;
size_t tocopy = length;
s += start;
while(tocopy-- && (*wrk++ = *s++));
*wrk = 0;
}
}
}
return subs;
}
I'm actually learning C programming and my school actually doesn't allow us to use calloc / realloc without reprogramming them. That's why I'm asking for help.
Here is my problem :
I want to use void * to make my code reusable but I encounter the problem "dereferencing void * pointer" when I try to run through my array. I'm unable to pick up the type of the final pointer.
Here is my functions :
#include <stdlib.h>
void *my_calloc(size_t size, size_t n) //n = number of bytes your type : sizeof(<whatever>)
{
void *ptr = NULL;
if (size < 1 || n < 1)
return (NULL);
ptr = malloc(n * (size + 1));
if (ptr == NULL)
return (NULL);
for (int i = 0; i != (n * (size + 1)); i++) {
*ptr = NULL; //Here is my problem
ptr++;
}
return (ptr);
}
void *my_realloc(void *src, size_t size, size_t n)
{
void *dst = NULL;
int dst_len = 0;
if (src == NULL || size < 0 || n < 1)
return (NULL);
dst_len = my_strlen(src) + size;
if (dst_len == my_strlen(src))
return (src);
dst = my_calloc(dst_len, n);
if (dst == NULL)
return (NULL);
for (int i = 0; src[i] != NULL;i++)
dst[i] = src[i]; //Here is the same problem...
free(src);
return (dst);
}
I just find a problem while I was writing my post, my my_strlen function can only take a char *... so I would need a function my_strlen looking like :
int my_strlen(void *str)
{
int len = 0;
while (str[len] != NULL) { //same problem again...
len++;
}
return (len);
}
A typical function where i call calloc / malloc would be :
int main(void)
{
char *foo = NULL;
int size = 0;
int size_to_add = 0;
size = <any size>;
//free(foo); //only if foo has been malloc before
foo = my_calloc(size, typeof(*foo));
//something
size_to_add = <any size>;
foo = my_realloc(foo, size_to_add, sizeof(*foo))
//something
free(foo);
return (0);
}
Thank you for trying to help me.
my_calloc() has various troubles:
Attemptted pointer math on a void *
This is undefined behavior (UB).
Instead make ptr a character pointer.
// void *ptr = NULL;
unsigned char *ptr = NULL;
...
ptr++;
Attempt to de-reference a void *
This is also UB.
Instead make ptr a character pointer.
// void *ptr = NULL;
unsigned char *ptr = NULL;
...
// *ptr = NULL;
*ptr = '\0';
my_calloc() allocates more memory than calloc()
To do the same as calloc(), do not add one.
// ptr = malloc(n * (size + 1));
ptr = malloc(n * size);
No overflow protection
my_calloc() does not detect overflow with n * (size + 1). A test is
// Note: it is known n > 0 at this point
if (SIZE_MAX/n > size+1) return NULL;
// or if OP drop the + 1 idea,
if (SIZE_MAX/n > size) return NULL;
my_realloc() has various troubles:
Different signature
I'd expect the goal of "school actually doesn't allow us to use calloc / realloc without reprogramming them" was meant to create a realloc() substitute of which my_realloc() is not. If a different function is desired, consider a new name
void *my_realloc(void *src, size_t size, size_t n)
// does not match
void *realloc(void *ptr, size_t size);
Failure to handle a shrinking allocation
The copying of data does not take into account that the new allocation may be smaller than the prior one. This leads to UB.
Unneeded code
size < 0 is always false
Memory leak
The below code does not free src before returning. Further, it does not allocate anything when n>0. This differs from calloc(pit, 0) and calloc(NULL, 42).
// missing free, no allocation
if (src == NULL || size < 0 || n < 1) {
return (NULL);
}
Assumed string
my_strlen(src) assume src points to a valid string. calloc() does not assume that.
void is an incomplete type, so you can't dereference a void *. What you can do however is cast it to a char * or unsigned char * to access individual bytes.
So my_calloc can do this:
((char *)ptr)[i] = 0;
And my_realloc can do this:
((char *)dst)[i] = ((char *)src)[i];
I try to code my own concatenation function in C without library, but I have issue and I don't know where it comes from.
To do my function I use pointers of char.
This is my Code :
#include <stdio.h>
#include <stdlib.h>
int longueur(char *str)
{
int i =0;
while(str[i] != '\0')
{
i++;
}
return i;
}
void concat(char* source, char* dest)
{
int longStr1 = (longueur(source));
int longStr2 = (longueur(dest));
int i=0, j=0;
char* temp = dest;
free(dest);
dest = (char*) realloc(dest, ((longStr1 + longStr2)* sizeof(char)));
/*dest[0] = temp[0]; <------ If I do this it will generate issue, so the bellow code too*/
while(temp[i] != '\0')
{
dest[i] = temp[i];
i++;
}
while(source[j] != '\0')
{
dest[i] = source[j];
i++;
j++;
}
dest[i] = '\0';
}
int main()
{
char *str1 = "World";
char *str2 = "Hello";
concat(str1, str2);
printf("-------------\n%s", str2);
return 0;
}
EDIT
I read all your answer, so I changed my concat function to :
void concat(char* source, char* dest)
{
int longStr1 = (longueur(source));
int longStr2 = (longueur(dest));
int i=0, j=0;
dest = (char*) malloc((longStr1 + longStr2)* sizeof(char) + sizeof(char));
while(dest[i] != '\0')
{
dest[i] = dest[i];
i++;
}
while(source[j] != '\0')
{
dest[i] = source[j];
i++;
j++;
}
dest[i] = '\0';
}
Now I don't have issue but my code only display "Hello"
In addition to all the good comments and solutions: realloc can give you a different pointer and you must return that pointer. So your function signature should be:
void concat(char* source, char** dest)
{
int longStr1 = (longueur(source));
int longStr2 = (longueur(dest));
int i=0, j=0;
char* temp = *dest, *temp2;
if ((temp2 = realloc(dest, ((longStr1 + longStr2)+1))==NULL) return;
*dest= temp2;
while(temp[i] != '\0')
{
*dest[i] = temp[i];
i++;
}
while(source[j] != '\0')
{
*dest[i] = source[j];
i++;
j++;
}
*dest[i] = '\0';
}
..and this assumes the function will only be called with a dest that was allocated with malloc. And sizeof(char) is always 1. (This resulting function is not optimal.)
--EDIT--
Below the correct, optimized version:
void concat(char* source, char** dest)
{
int longSrc = longueur(source);
int longDst = longueur(dest);
char *pDst, *pSrc;
if ((pDst = realloc(*dest, longSrc + longDst + 1))==NULL) return;
if (pDst != *dest) *dest= pDst;
pDst += longSrc;
pSrc= source;
while(pSrc)
*pDst++ = *pSrc++;
*pDst = '\0';
}
In your code
free(dest);
and
dest = (char*) realloc(dest, ((longStr1 + longStr2)* sizeof(char)));
invokes undefined behavior as none of them use a pointer previously allocated by malloc() or family.
Mostly aligned with your approach, you need to make use of another pointer, allocate dynamic memory and return that pointer. Do not try to alter the pointers received as parameters as you've passed string literals.
That said, you need to have some basic concepts clear first.
You need not free() a memory unless it is allocated through malloc() family.
You need to have a char extra allocated to hold the terminating null.
Please see this discussion on why not to cast the return value of malloc() and family in C..
If your concatenation function allocates memory, then, the caller needs to take care of free()-ing the memory, otherwise it will result in memory leak.
After you have freed dest here:
free(dest);
You cannot use this pointer in following call to realloc:
dest = (char*) realloc(dest, ((longStr1 + longStr2)* sizeof(char)));
/*dest[0] = temp[0]; <------ If I do this it will generate issue, so the bellow code too*/
man realloc
void *realloc(void *ptr, size_t size);
The realloc() function changes the size of the memory block
pointed to by ptr to size bytes. (...)
But this pointer is invalid now and you cannot use it anymore. When you call free(dest), the memory dest points to is being freed, but the value of dest stays untouched, making the dest a dangling pointer. Accessing the memory that has already been freed produces undefined behavior.
NOTE:
Even if free(dest) is technically valid when called on pointer to memory allocated by malloc (it is not an error in your function to call free(dest) then), it is incorrect to use this on pointer to literal string as you do in your example (because str2 points to string literal it is an error to pass this pointer to function calling free on it).
Given your original use, perhaps you would find a variant like this useful
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
size_t longueur ( const char * str ) { /* correct type for string lengths */
size_t len = 0;
while (*str++ != '\0') ++len;
return len;
}
char * concat ( const char * first, const char * second ) {
const char * s1 = first ? first : ""; /* allow NULL input(s) to be */
const char * s2 = second ? second : ""; /* treated as empty strings */
size_t ls1 = longueur(s1);
size_t ls2 = longueur(s2);
char * result = malloc( ls1 + ls2 + 1 ); /* +1 for NUL at the end */
char * dst = result;
if (dst != NULL) {
while ((*dst = *s1++) != '\0') ++dst; /* copy s1\0 */
while ((*dst = *s2++) != '\0') ++dst; /* copy s2\0 starting on s1's \0 */
}
return result;
}
int main ( void ) {
const char *str1 = "Hello";
const char *str2 = " World";
char * greeting = concat(str1, str2);
printf("-------------\n%s\n-------------\n", greeting);
free(greeting);
return 0;
}
In this variant, the two inputs are concatenated and the result of the concatenation is returned. The two inputs are left untouched.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char *string;
int end;
size_t length;
} str;
str* new_string(size_t len)
{
str *ptr;
ptr = (str*) malloc(sizeof(str));
ptr->string = (char *)malloc(sizeof(char) * len);
ptr->length = len;
ptr->string[len] = '\0';
ptr->end = 0;
return ptr;
}
void resize_string(str *ptr, size_t new_size)
{
ptr->string = (char *)realloc(ptr->string, sizeof(char) * new_size);
if (ptr->end == ptr->length) {
ptr->string[ptr->length] = ' ';
ptr->string[new_size] = '\0';
}
ptr->length = new_size;
}
void append_string(str *ptr, char ch)
{
if (ptr->end == ptr->length) {
size_t new_length = ptr->length + 5;
resize_string(ptr, new_length);
}
ptr->string[ptr->end] = ch;
ptr->end++;
ptr->string[ptr->end] = '\0';
}
char *raw_string(str *ptr)
{
return ptr->string;
}
void delete_string(str *ptr)
{
if (ptr != NULL) {
if (ptr->string != NULL) {
free(ptr->string); //deugger here is showing the break statement and HEAP CORRUPTION DETECTED.
}
free(ptr);
}
}
int main() {
char ch;
str *string = new_string(5);
while (1) {
ch = getchar();
if(ch=='\n'){
break;
}
append_string(string, ch);
}
printf("%s",raw_string(string));
delete_string(string);
return 0;
}
The above code runs fine in ubuntu , without any seg. fault,but in visual studio 2013 the code blows at runtime. Debugger is showing break statement when i try to free(ptr->string) and HEAP CORRUPTION DETECTED ,but i don't understand why?
Sounds like you should enable checked allocs in your gcc-based environment, you will see you're corrupting the heap regardless of underlying library.
That said, you're reallocating with 1 less than expected, e.g. indexing 'new_size' (to set your NUL byte after) is out of range.
Below is some psudo, but I'm trying to accomplish this. The problem is as written, it returns a blank pointer.
int testFunction(char *t) {
int size = 100;
t = malloc(100 + 1);
t = <do a bunch of stuff to assign a value>;
return size;
}
int runIt() {
char *str = 0;
int str_size = 0;
str_size = testFunction(str);
<at this point, str is blank and unmodified, what's wrong?>
free(str);
return 0;
}
This works fine if I have a predefined size, such as char str[100] = "" and I don't try to malloc or free memory afterwords. I need to be able to make the size dynamic though.
I've also tried this, but seem to run into a corrupt pointer somehow.
int testFunction(char **t) {
int size = 100;
t = malloc(100 + 1);
t = <do a bunch of stuff to assign a value>;
return size;
}
int runIt() {
char *str = 0;
int str_size = 0;
str_size = testFunction(&str);
<at this point, str is blank and unmodified, what's wrong?>
free(str);
return 0;
}
Thanks!
Your test function is just a bit backward. Size should be an input. The allocated pointer should be the output:
char* testFunction(int size) {
char* p = malloc(size);
<do a bunch of stuff to assign a value>;
return p;
}
int runIt() {
char *str = 0;
int str_size = 100;
str = testFunction(str_size);
<do something>
free(str);
return 0;
}
edit
Per comment, making size an output too.
char* testFunction(int *size) {
*size = <compute size>;
char* p = malloc(size);
<do a bunch of stuff to assign a value>;
return p;
}
int runIt() {
char *str = 0;
int str_size;
str = testFunction(&str_size);
<do something>
free(str);
return 0;
}
You're nearly there with the second example, but change
int testFunction(char **t) {
...
t = malloc(100 + 1);
To
int testFunction(char **t) {
...
*t = malloc(100 + 1);
The point being that you're passing in a char**, a pointer to a pointer, so you want to assign the malloc to what that points at (a pointer).
I am also studying c++. I had a the same question. So after speaking to c++ pro at work, he suggest me to do something like this
int method(char* p) {
if (p) {
strcpy(p, "I like c++");
}
return strlen("I like c++");
}
int main()
{
char* par = NULL;
int len = method(par);
if (len > 0) {
par = (char*)malloc(len+1);
memset(par, 0, len + 1);
method(par);
cout << "ret : " << par;
}
free(par);
}