I am contacting you because I need to code the functions realloc / strlen (signed and unsigned) / memcpy.
I have recoded these functions but it's doens't working and now I have valgrind errors like Conditional jump or move depends on uninitialised value(s).
Can you help me to fix the problem?
Here are the functions:
void *my_realloc(void *ptr, size_t size)
{
unsigned char *old_ptr = (unsigned char *)ptr;
void *new_ptr = NULL;
size_t old_size = 0;
if (!ptr) {
return malloc(size);
}
if (size == 0) {
free(ptr);
return NULL;
}
old_size = my_strlen_unsigned(old_ptr) + 1;
new_ptr = malloc(size);
if (!new_ptr) {
return NULL;
}
my_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
free(ptr);
return new_ptr;
}
void *my_memcpy(void *restrict dest, const void *restrict src, size_t n)
{
if (!dest || !src) return NULL;
unsigned char *d = dest;
const unsigned char *s = src;
size_t i = 0;
while (i < n && i < my_strlen_unsigned(s)) {
*d = *s;
d++;
s++;
i++;
}
return dest;
}
size_t my_strlen_unsigned(const unsigned char *s)
{
size_t count = 0;
if (s != NULL) {
while (*s != 0) {
count++;
s++;
}
}
return count;
}
size_t my_strlen(const char *s)
{
size_t count = 0;
if (s != NULL) {
while (*s != 0) {
count++;
s++;
}
}
return count;
}
Currently I test these functions via the following functions:
char *my_str_clean(char *str)
{
char *ptr = str;
char *new_str = malloc(1);
size_t i = 0;
if (!new_str)
return (NULL);
while (*ptr) {
if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
new_str = my_realloc(new_str, (sizeof(char) * (i + 2)));
new_str[i] = *ptr;
i++;
}
ptr++;
}
new_str[i] = '\0';
free(str);
return (new_str);
}
int main(int argc, char **argv, char **env)
{
char *test = malloc(15);
test[0] = 'l';
test[1] = 's';
test[2] = ' ';
test[3] = ' ';
test[4] = ' ';
test[5] = ' ';
test[6] = ' ';
test[7] = ' ';
test[8] = ' ';
test[9] = '-';
test[10] = 'l';
test[11] = ' ';
test[12] = '-';
test[13] = 'a';
test[14] = '\0';
char *clean = NULL;
clean = my_str_clean(test);
printf("%s\n", clean);
free(clean);
}
Here is the valgrid report :
==28637== Memcheck, a memory error detector
==28637== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==28637== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==28637== Command: ./mysh
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x109BB2: my_strlen_unsigned (my_strlen_unsigned.c:14)
==28637== by 0x109B2B: my_realloc (my_realloc.c:35)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109660: my_str_clean (my_str_clean.c:13)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x109BB2: my_strlen_unsigned (my_strlen_unsigned.c:14)
==28637== by 0x109ABC: my_memcpy (my_memcpy.c:16)
==28637== by 0x109B73: my_realloc (my_realloc.c:40)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109660: my_str_clean (my_str_clean.c:13)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x1097A5: my_strlen (my_strlen.c:18)
==28637== by 0x10962C: my_put_str (my_put_str.c:21)
==28637== by 0x10955F: main (minishell.c:56)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109B3F: my_realloc (my_realloc.c:36)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
l==28637==
==28637== HEAP SUMMARY:
==28637== in use at exit: 0 bytes in 0 blocks
==28637== total heap usage: 8 allocs, 8 frees, 43 bytes allocated
==28637==
==28637== All heap blocks were freed -- no leaks are possible
==28637==
==28637== For lists of detected and suppressed errors, rerun with: -s
==28637== ERROR SUMMARY: 18 errors from 3 contexts (suppressed: 0 from 0)
Invalid assumption:
void *my_realloc(void *ptr, size_t size)
{
unsigned char *old_ptr = (unsigned char *)ptr;
You're breaking the realloc() specification here. realloc() doesn't assume that the ptr would always be pointing to a string. It is type-agnostic.
And the cast is redundant. There's an implicit conversion from void * to any other pointer type.
Undefined behaviour:
old_size = my_strlen_unsigned(old_ptr) + 1;
The standard, or your my_strlen_unsigned version of it, works with strings, i.e. the pointer is assumed to be pointing to an array of char terminated by a null-byte. An array of ints will not be terminated with a null-byte. Your my_realloc() thus invokes undefined behaviour.
Possible fix:
You can't determine the old size of the block pointed to by ptr and reimplement realloc(), without at least reimplementing your own malloc() and free(), portably. But you can take the old size as the third argument. (But the standard realloc() only takes two. So would this be a conforming implementation?)
Here's glibc's implementation of it: malloc().c
Here's musl C implementation of it: malloc().c
And here's my toy example that attempts to emulate the standard realloc() to some extent:
/**
* #brief The my_realloc() function shall deallocate the old object pointed to
* by ptr and return a pointer to a new object that has the size specified by new_size.
*
* #param ptr - A pointer to a block of memory to resize.
* #param old_size - The size of the block pointed to by ptr.
* #param new_size - The size to resize by.
*
* If ptr is a null pointer, my_realloc() shall be equivalent to
* malloc() for the specified new_size.
*
* If ptr does not match a pointer returned earlier by calloc(),
* malloc(), or realloc() or if the space has previously been
* deallocated by a call to free() or realloc(), the behavior is undefined.
*
* #return Upon successful completion, my_realloc() shall return a pointer to the moved allocated space.
* If size and ptr both evaluate to 0, my_realloc() shall return a
* NULL pointer with errno set to [EINVAL].
* If there is not enough available memory, my_realloc() shall return a
* NULL pointer and set errno to [ENOMEM].
*
* If my_realloc() returns a NULL pointer, the memory referenced by ptr shall not be changed.
*
* #warning my_realloc() may return NULL to indicate an error. For that reason, a different pointer variable
* must be used to hold it's return value. Otherwise, you risk overwriting the original ptr with NULL and
* losing your only reference to the original block of memory.
*/
void *my_realloc (void *ptr, size_t new_size, size_t old size)
{
if (!ptr) {
return malloc (new_size);
}
if (!new_size) {
errno = EINVAL;
return 0;
}
if (new_size <= old_size) {
return ptr;
}
/* As a last resort, allocate a new chunk and copy to it.
*/
void *new = 0;
if (new_size > old_size) {
new = malloc (new_size);
if (!new) {
return 0;
}
memcpy (new, ptr, old_size);
free (ptr);
}
return new;
}
You can also find a sample implementation in chapter 8 of K&R.
Side-notes:
char *new_str = malloc(1);
new_str = my_realloc(..);
You risk losing access to the original memory allocated through malloc() here. If my_realloc() returned NULL, new_str would be assigned it's result and you would cause a memory leak in your program.
Furthermore, the memory returned by malloc() is uninitialized. And your code invokes undefined behaviour by calling my_strlen() underneath my_realloc() on a pointer that is uninitialized. Hence the warnings.
Your are assuming that data in the chunk is a correctly terminated C string, and using my_strlen_unsigned() to calculate the old size. You cannot do this since you cannot know what kind of data is stored in a chunk. The only real solution is to also remember the chunk size somehow, and pass it as parameter to your function.
If you cannot do this, there might be some other ways to get around the problem. For example, the malloc_usable_size() function is a GNU extension available in glibc (GNU libc) that can be used to query the size of an existing chunk, so you can use that instead of your my_strlen_unsigned(). Note however that if you use dynamic linking (the default when compiling) this will make your program non-portable and will only work on systems using glibc. You may wish to link your program statically in this case.
Assuming that the other functions in your code (such as my_memcpy()) are correctly implemented, a correct implementation of my_realloc() would be as follows:
void *my_realloc(void *ptr, size_t size)
{
void *new_ptr;
size_t old_size;
if (!ptr)
return malloc(size);
if (size == 0) {
free(ptr);
return NULL;
}
new_ptr = ptr;
old_size = malloc_usable_size(ptr);
if (size != old_size) {
new_ptr = malloc(size);
if (!new_ptr)
return NULL;
my_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
free(ptr);
}
return new_ptr;
}
Related
This is the leak:
==259134== HEAP SUMMARY:
==259134== in use at exit: 6 bytes in 1 blocks
==259134== total heap usage: 3 allocs, 2 frees, 1,036 bytes allocated
==259134==
==259134== Searching for pointers to 1 not-freed blocks
==259134== Checked 68,872 bytes
==259134==
==259134== 6 bytes in 1 blocks are definitely lost in loss record 1 of 1
==259134== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==259134== by 0x109327: ft_strjoin (in /nfs/homes/jotavare/Downloads/a.out)
==259134== by 0x109474: main (in /nfs/homes/jotavare/Downloads/a.out)
==259134==
==259134== LEAK SUMMARY:
==259134== definitely lost: 6 bytes in 1 blocks
==259134== indirectly lost: 0 bytes in 0 blocks
==259134== possibly lost: 0 bytes in 0 blocks
==259134== still reachable: 0 bytes in 0 blocks
==259134== suppressed: 0 bytes in 0 blocks
This is the code:
#include<stdlib.h>
int ft_strlen(char *str)
{
int i;
i = 0;
while (str[i] != '\0')
i++;
return (i);
}
char ft_strcat(char *dest, char *src)
{
int a;
int b;
char *temp;
a = 0;
b = 0;
temp = dest;
a = ft_strlen(temp);
while (src[b] != '\0')
{
temp[a + b] = src[b];
b++;
}
temp[a + b] = '\0';
return (*dest);
}
int strs_length(int size, char **strs, char *sep)
{
int i;
int len;
i = 0;
len = 0;
while (i < size)
{
len += ft_strlen(strs[i]);
i++;
}
len += ft_strlen(sep) * (size - 1);
return (len + 1);
}
char *ft_strjoin(int size, char **strs, char *sep)
{
char *arr;
char *aux;
int i;
int len;
len = strs_length(size, strs, sep);
i = 0;
arr =malloc(sizeof(char) * (len));
arr[0] = 0;
aux = arr;
if (size == 0)
{
arr[size] = 0;
return (arr);
}
if (!arr)
return (NULL);
while (i < size)
{
ft_strcat(aux, strs[i]);
if (i < size - 1)
ft_strcat(aux, sep);
i++;
}
aux[ft_strlen(aux)] = '\0';
return (aux);
}
#include <stdio.h>
int main()
{
char *text[10];
text[0] = "1";
text[1] = "2";
text[2] = "3";
text[3] = "4";
text[4] = "5";
text[5] = "6";
text[6] = "7";
text[7] = "8";
text[8] = "9";
text[9] = "10";
printf("%s\n", ft_strjoin(3, text, " "));
free(ft_strjoin(3, text, " "));
return(0);
}
This is the exercise:
Autorized functions: malloc
Write a function that will concatenate the set of pointing strings
strs separating them with sep.
If size is 0, we need to return a string that we can free().
It must be prototyped as follows:
char *ft_strjoin(int size, char **strs, char *sep);
You are making this call:
printf("%s\n", ft_strjoin(3, text, " "));
But you don't capture the return value from ft_strjoin and free it, so that's a memory leak.
Your code does this:
Some stuff with the text array
Calls ft_strjoin(3, text, " ") and prints the string whose address it returns
Calls ft_strjoin(3, text, " ") again and frees the string whose address it returns
The first call to ft_strjoin allocates a string and returns the address of it. The string is never freed. Only the string which is allocated by the second call is freed.
To print and free the string which is allocated by the first call, you would have to store its address in a variable, then print it, using the variable, then free it, using the variable, so that the program only calls ft_strjoin once, and then does both things with the same string which is allocated by that call.
This line
free(ft_strjoin(3, text, " "));
calls ft_strjoin again, which takes some more memory, and frees it afterward. In other words, it has no effect except some wasted CPU cycles. Instead of calling ft_strjoin again, you have to capture the output of the first ft_strjoin (the one in printf), save it in a pointer variable and free it after use.
The code is calling ft_strjoin twice and each call allocates a block of memory, but only one of those allocated blocks is being freed.
The first call to ft_strjoin is from an argument of a printf call here:
printf("%s\n", ft_strjoin(3, text, " "));
The allocated memory is not freed by printf and no pointer to the allocated memory has been saved elsewhere, so all references to the allocated memory have been lost. This is a memory leak.
The second call to ft_strjoin is from the call to free here:
free(ft_strjoin(3, text, " "));
The allocated memory from the second call is being freed immediately by the call to free. Nothing useful has been done, but at least it is not a memory leak.
The code should save the result of the first call to ft_strlen so that it can free the memory when it is no longer needed:
char *joined = ft_strjoin(3, text, " ");
printf("%s\n", joined);
free(joined);
The problem should be simple, but I have spent hours on this and cannot see what is wrong in my logic. The output works as it should, but Valgrind prints memory issues that should be fixed. I have added the origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char)); code to the while loop, my question is why doesn't this dynamically adjust the memory? The exact error given by Valgrind is
==9== Invalid write of size 1
==9== at 0x1087E2: mystrcat (mystrcat.c:18)
==9== by 0x10883C: main (mystrcat.c:34)
==9== Address 0x522d046 is 6 bytes inside a block of size 7 free'd
==9== at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9== by 0x1087C2: mystrcat (mystrcat.c:17)
==9== by 0x10883C: main (mystrcat.c:34)
==9== Block was alloc'd at
==9== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9== by 0x108811: main (mystrcat.c:31)
char *mystrcat(char *dest, const char *src)
{
char *origdest = dest;
while(*dest) {
dest++;
}
int i = 1;
while (*src) {
origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char));
*dest++ = *src++; // Copies character and increases/moves pointer
i++;
}
*dest = 0;
return origdest;
}
int main(void)
{
char *str = malloc(7);
strcpy(str, "Mydogs");
str = mystrcat(str, "arecool");
printf("%s\n", str);
free(str);
}
This statement:
Address 0x522d046 is 6 bytes inside a block of size 7 free'd is saying that the realloc() called following these statements results in the old pointer pointing to freed memory.
after this segment:
char *origdest = dest;
while(*dest) {
dest++;
}
EDIT to address comment "what is specifically wrong with the code and what could be changed to make it work?"
The explanation of my first observation above is that once the pointer to allocated memory is moved, as you have done, the memory allocation tables no longer have an accurate location of that memory, making that memory un-freeable.
Your stated goal here is to create a version of strcat(), so using realloc() is a reasonable approach, but to use it safely allocate the new memory into a temporary buffer first, then if allocation fails, the original memory location still exists, and can be freed.
One other small change that makes a big difference is how i is initialized. If 1 is used, it places the beginning of the second string one extra position further in memory, leaving a \0 character just after the first string, effectively making it the end of the resultant string. i.e. you would never see the appended part of the string:
In memory it would look like this:
|M|y|d|o|g|s|\0|a|r|e|c|o|o|l|
Then over-flow your buffer when attempting to place another NULL terminator at the and of the concatenated buffer, resulting in undefined behavior.
The following adaptation of your code illustrates these, along with some other simplifications:
char *mystrcat(char *dest, const char *src)
{
char *temp = NULL;
int i = 0;//changed from i = 1 as first location to
//copy to is len1, not len1 + 1
//Note, starting at len1 + 1 would leave a NULL character
//after "Mydogs", effectively ending the string
//the following are simplifications for use in realloc()
int len1 = strlen(dest);
int len2 = strlen(src);
//call once rather than in a loop. It is more efficient.
temp = realloc(dest, len1+len2+1);//do not cast return of realloc
if(!temp)
{
//handle error
return NULL;
}
dest = temp;
while(*src)
{
dest[len1 + i] = *src;
i++;
src++;
}
dest[len1 + i] = 0;//add null terminator
return dest;
}
int main(void)
{
char *temp = NULL;
char *str = malloc(7);
if(str)//always a good idea to test pointer before using
{
strcpy(str, "Mydogs");
temp = mystrcat(str, "arecool");
if(!temp)
{
free(str);
printf("memory allocation error, leaving early");
return 0;
}
str = temp;
printf("%s\n", str);
free(str);
}
return 0;
}
Why it is not correct to cast the return of c-m-realloc() in C.
Here you move to the end of the original string:
while(*dest)
dest++;
Here you allocate some new memory, but dest still points to the end of the original string. So you are overwriting memory after the end of the original string. Since you are reallocating, the original string may not even exist anymore at the previous location you are writing to, because realloc can move the data to a completely new location.
while (*src)
{
origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char));
*dest++ = *src++; // Copies character and increases/moves pointer
i++;
}
I have the following function to split a string. Most of the time it works fine, but sometimes it randomly causes a segmentation fault.
char** splitString(char* string, char* delim){
int count = 0;
char** split = NULL;
char* temp = strtok(string, delim);
while(temp){
split = realloc(split, sizeof(char*) * ++count);
split[count - 1] = temp;
temp = strtok(NULL, " ");
}
int i = 0;
while(split[i]){
printf("%s\n", split[i]);
i++;
}
split[count - 1][strlen(split[count - 1]) - 1] = '\0';
return split;
}
You have a number of subtle issues, not the least of which your function will segfault if you pass a string literal. You need to make a copy of the string you will be splitting as strtok modifies the string. If you pass a string literal (stored in read-only memory), your compiler has no way of warning unless you have declared string as const char *string;
To avoid these problems, simply make a copy of the string you will tokeninze. That way, regardless how the string you pass to the function was declared, you avoid the problem altogether.
You should also pass a pointer to size_t as a parameter to your function in order to make the number of token available back in the calling function. That way you do not have to leave a sentinel NULL as the final pointer in the pointer to pointer to char you return. Just pass a pointer and update it to reflect the number of tokens parsed in your function.
Putting those pieces together, and cleaning things up a bit, you could use the following to do what you are attempting to do:
char **splitstr (const char *str, char *delim, size_t *n)
{
char *cpy = strdup (str), *p = cpy; /* copy of str & pointer */
char **split = NULL; /* pointer to pointer to char */
*n = 0; /* zero 'n' */
for (p = strtok (p, delim); p; p = strtok (NULL, delim)) {
void *tmp = realloc (split, sizeof *split * (*n + 1));
if (!tmp) { /* validate realloc succeeded */
fprintf (stderr, "splitstr() error: memory exhausted.\n");
break;
}
split = tmp; /* assign tmp to split */
split[(*n)++] = strdup (p); /* allocate/copy to split[n] */
}
free (cpy); /* free cpy */
return split; /* return split */
}
Adding a short example program, you could do the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **splitstr (const char *str, char *delim, size_t *n)
{
char *cpy = strdup (str), *p = cpy; /* copy of str & pointer */
char **split = NULL; /* pointer to pointer to char */
*n = 0; /* zero 'n' */
for (p = strtok (p, delim); p; p = strtok (NULL, delim)) {
void *tmp = realloc (split, sizeof *split * (*n + 1));
if (!tmp) { /* validate realloc succeeded */
fprintf (stderr, "splitstr() error: memory exhausted.\n");
break;
}
split = tmp; /* assign tmp to split */
split[(*n)++] = strdup (p); /* allocate/copy to split[n] */
}
free (cpy); /* free cpy */
return split; /* return split */
}
int main (void) {
size_t n = 0; /* number of strings */
char *s = "My dog has fleas.", /* string to split */
*delim = " .\n", /* delims */
**strings = splitstr (s, delim, &n); /* split s */
for (size_t i = 0; i < n; i++) { /* output results */
printf ("strings[%zu] : %s\n", i, strings[i]);
free (strings[i]); /* free string */
}
free (strings); /* free pointers */
return 0;
}
Example Use/Output
$ ./bin/splitstrtok
strings[0] : My
strings[1] : dog
strings[2] : has
strings[3] : fleas
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/splitstrtok
==14471== Memcheck, a memory error detector
==14471== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14471== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==14471== Command: ./bin/splitstrtok
==14471==
strings[0] : My
strings[1] : dog
strings[2] : has
strings[3] : fleas
==14471==
==14471== HEAP SUMMARY:
==14471== in use at exit: 0 bytes in 0 blocks
==14471== total heap usage: 9 allocs, 9 frees, 115 bytes allocated
==14471==
==14471== All heap blocks were freed -- no leaks are possible
==14471==
==14471== For counts of detected and suppressed errors, rerun with: -v
==14471== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
split[count - 1][strlen(split[count - 1]) - 1] = '\0';
should look like
split[count - 1] = NULL;
You don't have anything allocated there so that you can access it and put '\0'.
After that put that line before while(split[i]) so that the while can stop when it reaches NULL.
The function strtok is not reentrant, use strtok_r() function this is a reentrant version strtok().
i have an array with n words .. i want to attach the strings togther ..
for example if the array have the following strings: "hello" "world" "stack77"
i want the function to return :"helloworldstach7 " any help how i can do something like this without Recursion and with one loop and i can only use from the string library the two functions strcpy and strlen !!
any ideas ! thanks
I NEED TO USE ONE LOOP ONLY !
char *connect(char**words,int n){
int i=0;
while(words){
strcpy(words+i,
i saw many many solutions but they all use other string functions , where i only want to use strcpy and strlen .
If to use only the two mentioned standard string functions then the function can look as it is shown in the demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * connect( char **words, size_t n )
{
size_t length = 0;
for ( size_t i = 0; i < n; i++ ) length += strlen( words[i] );
char *s = malloc( length + 1 );
size_t pos = 0;
for ( size_t i = 0; i < n; i++ )
{
strcpy( s + pos, words[i] );
pos += strlen( words[i] );
}
s[pos] = '\0';
return s;
}
int main( void )
{
char * s[] = { "Hello", " ", "World" };
char *p = connect( s, sizeof( s ) / sizeof( *s ) );
puts( p );
free( p );
}
The program output is
Hello World
If to use only one loop then the function can look the following way
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * connect( char **words, size_t n )
{
char *s = calloc( 1, sizeof( char ) );
if ( s != NULL )
{
size_t pos = 0;
for ( size_t i = 0; s != NULL && i < n; i++ )
{
size_t length = strlen( words[i] );
char *tmp = realloc( s, pos + length + 1 );
if ( tmp != NULL )
{
s = tmp;
strcpy( s + pos, words[i] );
pos += length;
}
else
{
free( s );
s = NULL;
}
}
}
return s;
}
int main( void )
{
char * s[] = { "Hello", " ", "World" };
char *p = connect( s, sizeof( s ) / sizeof( *s ) );
if ( p != NULL ) puts( p );
free( p );
}
In addition to using strcpy, you can also use sprintf. Each of the functions in the printf family returns the number of characters actually output allowing you to compute an offset in your final string without an additional function call. Now, there is nothing wrong with using a strcpy/strlen approach, and in fact, that is probably the preferred approach, but be aware that there are always multiple ways of doing things within the parameters you have given. Also note that the printf family offers a wealth of formatting benefits in the event you would need to include additional information along with the concatenation of strings.
For example, using sprintf to concatenate each string while saving the number of characters in each nc as the offset for writing the next string to the resulting buffer buf, while using a ternary operator to control the addition of a space between the words based on your loop counter, you could do something similar to the following:
char *compress (char **p, int n)
{
char *buf = NULL; /* buffer to hold concatendated string */
size_t total = 0; /* total number of characters required */
int nc = 0; /* number of chars added (counter) */
for (int i = 0; i < n; i++) /* get total required length */
total += strlen (p[i]) + 1; /* including spaces between */
if (!(buf = malloc (total + 1))) /* allocate/validate mem */
return buf; /* return NULL on error */
for (int i = 0; i < n; i++) /* add each word to buf, save nc */
nc += sprintf (buf + nc, i ? " %s" : "%s", p[i]);
*(buf + nc) = 0; /* affirmatively nul-terminate buf */
return buf;
}
note: each memory allocation with malloc, calloc or realloc should be validated to insure it succeeds, and the error handled in the event of failure. (here NULL is returned if allocation fails).
Putting that together in a short example, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *compress (char **p, int n)
{
char *buf = NULL; /* buffer to hold concatendated string */
size_t total = 0; /* total number of characters required */
int nc = 0; /* number of chars added (counter) */
for (int i = 0; i < n; i++) /* get total required length */
total += strlen (p[i]) + 1; /* including spaces between */
if (!(buf = malloc (total + 1))) /* allocate/validate mem */
return buf; /* return NULL on error */
for (int i = 0; i < n; i++) /* add each word to buf, save nc */
nc += sprintf (buf + nc, i ? " %s" : "%s", p[i]);
*(buf + nc) = 0; /* affirmatively nul-terminate buf */
return buf;
}
int main (void) {
char *sa[] = { "My", "dog", "has", "too many", "fleas." },
*result = compress (sa, sizeof sa/sizeof *sa);
if (result) { /* check return */
printf ("result: '%s'\n", result); /* print string */
free (result); /* free memory */
}
return 0;
}
Example Use/Output
$ ./bin/strcat_sprintf
result: 'My dog has too many fleas.'
Memory/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/strcat_sprintf
==27595== Memcheck, a memory error detector
==27595== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==27595== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==27595== Command: ./bin/strcat_sprintf
==27595==
result: 'My dog has too many fleas.'
==27595==
==27595== HEAP SUMMARY:
==27595== in use at exit: 0 bytes in 0 blocks
==27595== total heap usage: 1 allocs, 1 frees, 28 bytes allocated
==27595==
==27595== All heap blocks were freed -- no leaks are possible
==27595==
==27595== For counts of detected and suppressed errors, rerun with: -v
==27595== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have any additional questions.
compress with a Single Loop
As mentioned in the comment to MarianD's answer, you can use a single loop, reallocating your buffer with the addition of each word to the final string, but that is less efficient than getting the total number of characters required and then allocating once. However, there are many occasions where that is exactly what you will be required to do. Basically, you will simply get the length of each word and then allocate memory for that word (and the space between it and the next and for the nul-byte) using realloc instead of malloc (or calloc). realloc acts just like malloc for the first allocation, thereafter it resizes the buffer maintaining its current contents.
note: never realloc the buffer directly (e.g. buf = realloc (buf, newsize);), instead, always use a temporary pointer. Why? IF realloc fails, NULL is returned by realloc which causes you to lose the reference to your original buf (e.g. it will result in buf = NULL;), meaning that the address for your original buf is lost (and you have created a memory leak).
Putting that together, you could do something like the following:
char *compress (char **p, int n)
{
char *buf = NULL; /* buffer to hold concatendated string */
size_t bufsz = 0; /* current allocation size for buffer */
int nc = 0; /* number of chars added (counter) */
for (int i = 0; i < n; i++) { /* add each word to buf */
size_t len = strlen (p[i]) + 1; /* get length of word */
void *tmp = realloc (buf, bufsz + len); /* realloc buf */
if (!tmp) /* validate reallocation */
return buf; /* return current buffer */
buf = tmp; /* assign reallocated block to buffer */
bufsz += len; /* increment bufsz to current size */
nc += sprintf (buf + nc, i ? " %s" : "%s", p[i]);
}
*(buf + nc) = 0; /* affirmatively nul-terminate buf */
return buf;
}
Memory/Error Check
$ valgrind ./bin/strcat_sprintf_realloc
==28175== Memcheck, a memory error detector
==28175== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28175== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28175== Command: ./bin/strcat_sprintf_realloc
==28175==
result: 'My dog has too many fleas.'
==28175==
==28175== HEAP SUMMARY:
==28175== in use at exit: 0 bytes in 0 blocks
==28175== total heap usage: 5 allocs, 5 frees, 68 bytes allocated
==28175==
==28175== All heap blocks were freed -- no leaks are possible
==28175==
==28175== For counts of detected and suppressed errors, rerun with: -v
==28175== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
note: now there are 5 allocations instead of 1.
Let me know if you have any questions.
I'm getting this error from Valgrind when I try to run my program :
==23152== Conditional jump or move depends on uninitialised value(s)
==23152== at 0x4C2D8D0: strcmp (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==23152== by 0x40096C: str_lower_cmp (functions.c:41)
==23152== by 0x400BB8: list_sort (list_sort.c:34)
==23152== by 0x400CC7: get_wdir_content (working_dir.c:27)
==23152== by 0x400C27: main (main.c:18)
==23152== Uninitialised value was created by a heap allocation
==23152== at 0x4C2C27B: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==23152== by 0x400D4C: xmalloc (xfunctions.c:35)
==23152== by 0x400886: lower_string (functions.c:20)
==23152== by 0x400945: str_lower_cmp (functions.c:39)
==23152== by 0x400BB8: list_sort (list_sort.c:34)
==23152== by 0x400CC7: get_wdir_content (working_dir.c:27)
==23152== by 0x400C27: main (main.c:18)
==23152==
==23152== Conditional jump or move depends on uninitialised value(s)
==23152== at 0x400BBB: list_sort (list_sort.c:34)
==23152== by 0x400CC7: get_wdir_content (working_dir.c:27)
==23152== by 0x400C27: main (main.c:18)
==23152== Uninitialised value was created by a heap allocation
==23152== at 0x4C2C27B: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==23152== by 0x400D4C: xmalloc (xfunctions.c:35)
==23152== by 0x400886: lower_string (functions.c:20)
==23152== by 0x400945: str_lower_cmp (functions.c:39)
==23152== by 0x400BB8: list_sort (list_sort.c:34)
==23152== by 0x400CC7: get_wdir_content (working_dir.c:27)
==23152== by 0x400C27: main (main.c:18)
==23152==
Though I can't say what is wrong, I'm guessing it comes from list_sort.c :
t_llist *list_sort(t_llist *list)
{
struct s_node *tmp;
tmp = list->head;
while (tmp != NULL)
{
if (tmp->next != NULL)
{
if (!tmp->name || !tmp->next->name)
printf("Reached.\n");
if (str_lower_cmp(tmp->name, tmp->next->name) > 0)
{
data_swap(tmp, tmp->next);
tmp = list->head;
}
else
tmp = tmp->next;
}
else
return (list);
}
return (list);
}
Does that mean that at some point, the tmp->name or tmp->next->name value is uninitialised ?
EDIT (the functions.c code)
char *lower_string(char *s)
{
char *res;
int i;
i = 0;
res = xmalloc(sizeof(*res) * strlen(s) + 1);
while (s[i])
{
if (s[i] >= 'A' && s[i] <= 'Z')
res[i] = s[i] + 32;
else
res[i] = s[i];
i++;
}
s[i] = '\0';
return (res);
}
int str_lower_cmp(char *s1, char *s2)
{
char *tmp1;
char *tmp2;
int res;
tmp1 = lower_string(s1);
tmp2 = lower_string(s2);
res = strcmp(tmp1, tmp2);
free(tmp1);
free(tmp2);
return (res);
}
Initially valgrind is telling you that you're running strcmp with a memory address allocated with malloc, coming from function lower_string, but that has no initial value assigned.
This would mean an undefined behavior, meaning that, depending on your code, can be very dangerous because can lead to unexpected results.
I would suggest using calloc in lower_string.
Edit: your're setting s[i] to 0 instead of res[i] (the pointer you've allocated and returning). On the other hand, I would suggest using calloc and checking res!=NULL
Your error is here in lower_string You're not terminating the string you're allocating:
char *lower_string(char *s)
{
char *res;
int i;
i = 0;
res = xmalloc(sizeof(*res) * strlen(s) + 1);
while (s[i])
{
if (s[i] >= 'A' && s[i] <= 'Z')
res[i] = s[i] + 32;
else
res[i] = s[i];
i++;
}
s[i] = '\0'; // THIS IS WRONG
return (res);
}
The marked line should be this:
res[i] = '\0'; // THIS IS RIGHT
And note, this would be caught if you properly passed the input string as a const parameter:
char *lower_string(const char *s) // MAKE PARAM CONST
Doing so would have failed to compile because your s[i] = '\0' assignment would have violated the const-condition. General rule, unless you need to modifying something passed as a a by-address parameter, make it const
when the "char *s" passed to lower_string is an empty string you'd have a crashing program too. Calling calloc as jcm said would help fix that problem