String appending function doesn't work - c

I tried to create a function that easily appends two strings. The source string has to be previously allocated dynamically. I used my knowledge but this resulted in ub and any sort of leaks.
void strapp (char *source_offset, int position, char *appendage)
{
size_t appendage_size = strlen(appendage);
source_offset = realloc(source_offset, strlen(source_offset) + appendage_size);
sprintf( &source_offset[position], "%s%s", appendage, source_offset + (position + appendage_size) );
}
What am I doing wrong?

source_offset + (position + appendage_size) is somehow strange. It seems that you tried to catenate the second string with a substring of the first copying the result in the first string.
source_offset + (position + appendage_size) is the suffix of the source string starting at offset position+appendage_size which is a non-sense as it is past the end of the source string...
May be you wanted something like this?
If you want to catenate the two string then the following is correct:
size_t appendage_size = strlen(appendage);
source_offset = realloc(source_offset, position + appendage_size + 1);
sprintf( &source_offset[position], "%s", appendage );
Which appends appendage to source_offset starting at position.
Now if you want to insert appendage in the middle this can be a little more tricky:
size_t appendage_size = strlen(appendage);
char *result = malloc(strlen(source_offset) + appendage_size + 1);
char cut = source_offset[position];
source_offset[position] = '\0';
sprintf( result, "%s%s%c%s", source_offset,appendage,cut,source_offset+position+1);
// do hat you want with result
Beware that realloc may change the base address of the initial memory, so you can't do things like this as the parameter value source_offset will be changed only locally.

size_t appendage_size = strlen(appendage) + 1; /* To hold a \0 character */
The new array allocated should be able to hold both the null characters in each string
strlen(string) + 1

actually your coes does not append string B to string A, it does append string B at position n of string A along with string A, with some out of memory offset of String A
char *
strapp(char *dest, const char *src)
{
size_t i,j;
for (i = 0; dest[i] != '\0'; i++)
;
for (j = 0; src[j] != '\0'; j++)
dest[i+j] = src[j];
dest[i+j] = '\0';
return dest;
}
the above code appends string B to String A and returns pointer to string a A;
char* strapp (char *source_offset, const char *appendage, int position)
{
char* temp = NULL:
size_t appendage_size = strlen(appendage);
if (( temp = realloc(source_offset, strlen(source_offset) + appendage_size + 1)) == NULL )
return NULL;
else
source_offset = temp;
sprintf( &source_offset[position], "%s", appendage);
return source_offset;
}

If you want to fix your function try this:
void strapp (char **source_offset, int position, char *appendage)
{
size_t appendage_size = strlen(appendage);
*source_offset = (char*)realloc(*source_offset, strlen(*source_offset) + appendage_size);
sprintf( *source_offset + position, "%s%s", appendage, *source_offset + (position + appendage_size) );
}
And call this like strapp(&str, 10, "INSERTION"); // &str - pointer to pointer.
The problem was in the following: when you sent pointer to function you are able to change data pointed by this pointer, but cannot change the pointer, so memory allocation made inside function do mot change address of initial string.

Since I was asked to provide an example of function usage and respectively the output. I converted this code to something compilable to mingw and it works like expected.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void strapp (char *source_offset, int position, char *appendage)
{
size_t appendage_size = strlen(appendage) + 1;
source_offset = realloc(source_offset, strlen(source_offset) + appendage_size);
sprintf( &source_offset[position], "%s%s", appendage, source_offset + (position + appendage_size) );
}
int main(void)
{
char *str1 = malloc(11 + 1);
sprintf(str1, "Hello World");
strapp(str1, 4, " Horrible ");
printf(str1);
free(str1);
return 0;
}
Output: Hell Horrible

Partial fix of the problem I found while investigating.
If I add a substring to a location of the source string like that:
sprintf(&source[4], "new");
the source string will be terminated by the "new" and the rest of the string will not be shown.
But if I declare the substring "new" and use
memcpy(&str1[4], appendage, 3); it does the job.
There, this is the working function:
void strapp (char *source_offset, int position, char *appendage)
{
size_t appendage_size = strlen(appendage) + 1;
char copy[strlen(source_offset) + 1];
strcpy(copy, source_offset);
source_offset = realloc(source_offset, strlen(source_offset) + appendage_size);
memcpy(&source_offset[position], appendage, strlen(appendage));
sprintf(&source_offset[position + strlen(appendage)], &copy[position]);
}

Related

Changing size of dynamic *char

Is there way how to make my string to even size?
EDIT:
I need a few string in struct that have even length. So something like:
struct msg { char * first; char * second; char * third; };
so it is in the end something like first string "hi\0\0" second string "hello\0" third string "byebye\0\0" and i need to change them anytime+they are dynamic allocated.
Create a strdup_even().
Allocate memory as needed to copy the string, plus maybe 1 more to make "even".
char *strdup_even(const char *str) {
size_t len = strlen(str) + 1; // Size needed for the _string_
size_t len2 = len + len % 2; // Even allocation size
char *copy = malloc(len2);
if (copy) {
memcpy(copy, str, len);
if (len2 > len) {
copy[len] = '\0';
}
}
return copy;
}
Sample usage
struct msg m;
m.first = strdup_even("hi");
m.second = strdup_even("hello");
Use malloc() and realloc():
char *string = malloc(STRING_SIZE);
strcpy(string, "hi");
string = realloc(string, STRING_SIZE+1);
string[STRING_SIZE] = '\0';

How to append a char at a defined position

I'm trying to add a character at a defined position. I've created a new function, allocate a memory for one more char, save characters after the position then added my character at the defined position, and now I don't know how to erase characters after that position to concatenate the saved string. Any solution?
Here is the beginning of my function:
void appendCharact(char *source, char carac, int position) {
source = realloc(source, strlen(source) * sizeof(char) + 1); //Get enough memory
char *temp = source.substr(position); //Save characters after my position
source[position] = carac; //Add the character
}
EDIT :
I'm trying to implement another "barbarous" solution, in debug mode I can see that I've approximately my new string but it look like I can't erase the older pointer...
void appendCharact(char *source, char carac, int position) {
char *temp = (char *)malloc((strlen(source) + 2) * sizeof(char));
int i;
for(i = 0; i < position; i++) {
temp[i] = source[i];
}
temp[position] = carac;
for (i = position; i < strlen(source); i++) {
temp[i + 1] = source[i];
}
temp[strlen(temp) + 1] = '\0';
free(source);
source = temp;
}
I mentioned that I could see five problems with the code as shown (copied here for reference)
void appendCharact(char * source, char carac , int position)
{
source = realloc(source, strlen(source) * sizeof(char) + 1); //Get enough memory
char * temp = source.substr(position); //Save characters after my position
source[position] = carac; //Add the charactere
}
The problems are (in no specific order):
strlen(source) * sizeof(char) + 1 is equal to (strlen(source) * sizeof(char)) + 1. It should have been (strlen(source) + 1) * sizeof(char). However, this works fine since sizeof(char) is defined in the C++ specification to always be equal to 1.
Related to the above: Simple char strings are really called null-terminated byte strings. As such they must be terminated by a "null" character ('\0'). This null character of course needs space in the allocated string, and is not counted by strlen. Therefore to add a character you need allocate strlen(source) + 2 characters.
Never assign back to the pointer you pass to realloc. If realloc fails, it will return a null pointer, making you lose the original memory, and that is a memory leak.
The realloc function return type is void*. In C++ you need to cast it to the correct pointer type for assignment.
You pass source by value, meaning inside the function you have a local copy of the pointer. When you assign to source you only assign to the local copy, the original pointer used in the call will not be modified.
Here are some other problems with the code, or its possible use:
Regarding the null-terminator, once you allocate enough memory for it you also need to add it to the string.
If the function is called with source being a literal string or an array or anything that wasn't returned by a previous call to malloc, calloc or realloc, then you can't pass that pointer to realloc.
You use source.substr(position) which is not possible since source isn't an object and therefore doesn't have member functions.
Your new solution is much closer to a working function but it still has some problems:
you do not check for malloc() failure.
you should avoid computing the length of the source string multiple times.
temp[strlen(temp) + 1] = '\0'; is incorrect as temp is not yet a proper C string and strlen(temp) + 1 would point beyond the allocated block anyway, you should just write temp[i + 1] = '\0';
the newly allocated string should be returned to the caller, either as the return value or via a char ** argument.
Here is a corrected version:
char *insertCharact(char *source, char carac, size_t position) {
size_t i, len;
char *temp;
len = source ? strlen(source) : 0;
temp = (char *)malloc(len + 2);
if (temp != NULL) {
/* sanitize position */
if (position > len)
position = len;
/* copy initial portion */
for (i = 0; i < position; i++) {
temp[i] = source[i];
}
/* insert new character */
temp[i] = carac;
/* copy remainder of the source string if any */
for (; i < len; i++) {
temp[i + 1] = source[i];
}
/* set the null terminator */
temp[i + 1] = '\0';
free(source);
}
return temp;
}
int pos = 1;
char toInsert = '-';
std::string text = "hallo";
std::stringstream buffer;
buffer << text.substr(0,pos);
buffer << toInsert;
buffer << text.substr(pos);
text = buffer.str();
Try using something like:
#include <string>
void appendCharAt(std::string& src, char c , int pos)
{
std::string front(src.begin(), src.begin() + pos - 1 ); // use iterators
std::string back(src.begin() + pos, src.end() );
src = front + c + back; // concat together +-operator is overloaded for strings
}
Not 100% sure weather the positions are right. Maybe front hast to be src.begin() + pos and back src.begin() + pos + 1. Just try it out.
The C version of this will have to take care of the situation where realloc fails, in which case the original string is preserved. You should only overwrite the old pointer with the one returned from realloc upon success.
It might look something like this:
bool append_ch (char** str, char ch, size_t pos)
{
size_t prev_size = strlen(*str) + 1;
char* tmp = realloc(*str, prev_size+1);
if(tmp == NULL)
{
return false;
}
memmove(&tmp[pos+1], &tmp[pos], prev_size-pos);
tmp[pos] = ch;
*str = tmp;
return true;
}
Usage:
const char test[] = "hello word";
char* str = malloc(sizeof test);
memcpy(str, test, sizeof test);
puts(str);
bool ok = append_ch(&str, 'l', 9);
if(!ok)
asm ("HCF"); // error handling here
puts(str);
free(str);

Strings and substrings

I tried to work with different versions but all of them do not work unfortunetaly. That is why I am posting here. I need to return an array of two strings. The first is substring from the beginning up to but not including the comma. The second is the substring of s after the comma. String contains only one comma. Also I need to use char * strchr(const char *s, int c). Well, it did not help me. Please, help mw with this, spent many of hours......thanks
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **split_on_comma(const char * s){
//this piece of code does not work
/*int i;
char array[80];
for (i=0; i<strlen(s)-1; i++){
if(substr=(s[i],',')==NULL){
array[i]=s[i];
} else
}*/
return 0;
}
This is quite trivial: just copy both halves of the string.
char **split_on_comma(const char *str)
{
const char *p = strchr(str, ',');
if (p == NULL)
return NULL;
char **subs = malloc(2 * sizeof subs[0]);
ptrdiff_t len1 = p - str;
ptrdiff_t len2 = str + strlen(str) - (p + 1);
// copy and 0-terminate first half
subs[0] = malloc(len1 + 1);
memcpy(subs[0], str, len1);
subs[0][len1] = 0;
// copy and 0-terminate second half
subs[1] = malloc(len2 + 1);
memcpy(subs[1], p + 1, len2);
subs[1][len2] = 0;
return subs;
}
Checks against malloc() returning NULL are omitted for clarity, shall be included in production code though.

C adding extra spaces in trimming

I call below function which is written in C to fetch parent of child-
char *getParent(char *child)
{
int len = strlen(child);
char *parent;
parent = strdup(substring(child, 0, len - 4));
return parent;
}
char *substring(const char* str, int beg, int n)
{
char *ret = malloc(n+1);
strncpy(ret, (str + beg), n);
*(ret+n) = '\n';
return strdup(ret);
}
child is - '11112222'
Now I am expecting output - '1111' but this function also adding extra spaces after 1111 like this '1111---here i am getting space----'.
What's wrong in this function ?
This:
*(ret+n) = '\n';
is wrong, it should be:
*(ret+n) = '\0';
to terminate the string. You're adding a linefeed, not a terminator, thus failing to produce a valid string.
Also, I would recommend prefering indexing since it's a bit cleaner syntactically:
ret[n] = '\0';
And, of course, you should check the return value of malloc() before relying on it.
UPDATE: And gosh, remove that strdup(), it's completely pointless now that you've already malloc()ed your new string.
It should be just:
char * substring(const char *str, size_t beg, size_t n)
{
char *ret = malloc(n + 1);
if(ret != NULL)
{
strncpy(ret, str + beg, n);
ret[n] = '\0';
}
return ret;
}
This still assumes that the offset and length are valid, and that str is non-NULL.

How to Concatenate 2 C strings, without overwriting any terminating Null characters?

I am trying to set up a list of file names for a parameter to SHFileOperation. I want to be able to concatenate a file name onto the char array, but i dont want to get rid of the terminating character. for example, I want this:
C:\...\0E:\...\0F:\...\0\0
when i use strcat(), it overwrites the null, so it looks like
C:\...E:\...F:\...0\
Is there any easy way to do this? or am i going to have to code a new strcat for myself?
The code is pretty straightforward. Use a helper pointer to track where the next string should start. To update the tracker pointer, increment by the length of the string +1:
const char *data[] = { "a", "b", "c" };
size_t data_count = sizeof(data) / sizeof(*data);
size_t d;
size_t buffer_size;
char *buffer;
char *next;
// calculate the size of the buffer
for (d = 0; d < data_count; ++d)
buffer_size += (strlen(data[d] + 1);
buffer_size += 1;
buffer = malloc(buffer_size);
// Next will track where we write the next string
next = buffer;
for (d = 0; d < data_count; ++d)
{
strcpy(next, data[d]);
// Update next to point to the first character after the terminating NUL
next += strlen(data[d]) + 1;
}
*next = '\0';
Use memcpy.
memcpy(dest, src1, strlen(src1)+1);
memcpy(&dest[strlen(src1)+1], src2, strlen(src2)+1);
Using the GNU stpcpy() may be slightly more elegant, if you know beforehand the maximum 'length' of the resulting char array.
char *src[] = {"C:\\foo", "E:\\bar", "F:\\foobar", 0};
char dst[MY_MAX_LEN], *p = dst;
int i;
for (i = 0; src[i]; i++)
p = stpcpy(p, src) + 1;
*p = 0;
assert(p < dst + sizeof dst);
If needed, stpcpy() can be defined as:
char *stpcpy(char * restrict dst, const char * restrict src)
{
return strcpy(dst, src) + strlen(src);
}
just use strcat to append to the original string, but add one to the offset so you're bypassing the previous string's 0 terminator.
// an example
char testString [256];
strcpy (testString, "Test1");
strcat (testString + strlen(testString)+1, "Test2");
strcat (testString + strlen(testString)+1, "Test3");
testString will now contain "Test1\0Test2\0Test3\0"

Resources