I'm trying to create a function that can insert a char array of length len into a given index in a string type. However, it acts ways that it shouldn't.
When trying to insert at the end of the initial array, the contents of buf are inserted at the beginning of the string, in reverse order.
i.e. inserting world into Hello becomes dlrowHello
When trying to insert in the middle of the initial case, the output is worldHello
str->bytes.data is the array being modified.
str->bytes.usage is the length of that array.
bool string_insert(string_t * const str, size_t index,
const char * const buf, size_t len)
{
bool success = false;
uint8_t orig_size = str->bytes.usage;
uint8_t * temp;
if (str->len < index){
return false;
}
if(str->bytes.usage+len>=str->bytes.usage){
temp = malloc(sizeof(char) * (str->bytes.usage + len));
}
else {
temp = malloc(sizeof(char) * (str->bytes.usage));
}
if (temp == NULL){
success = false;
}
else{
if (index == 0){ //inserts at beginning
for (int k = 0; k < len; k++){
temp[k] = buf[k];
}
for (int j = len; j < str->bytes.usage+len; j++){
temp[j] = str->bytes.data[j - len];
}
}
else if (index == str->bytes.usage){ //inserts at end
for (int h = 0; h < str->bytes.usage; h++){
temp[h] = str->bytes.data[h];
}
for (int g = 0; g < len; g++){
temp[g+str->bytes.usage] = buf[g];
}
}
else{ //inserts in the middle
for (int i = 0; i < index; i++){
temp[i] = str->bytes.data[i];
}
for(int i = index; i < index + len; i++){
temp[i] = buf[i-index];
}
for(int i = index + len; i < str->bytes.usage+len; i++){
temp[i] = str->bytes.data[i-len];
}
}
string_free(str);
str->bytes.data = temp;
str->bytes.dim = 2*str->bytes.usage;
str->bytes.usage = orig_size+len;
success = true;
}
return success;
}
Basically, I just need to know if I'm blind and missing something obvious.
When index is 0, it is not in reverse order?
if (index == 0){ //inserts at beginning of array
for (int k = 0; k < len; k++){
temp[k] = buf[k];
changed to
if (index == 0){ //inserts at beginning of array
for (int k = 0; k < len; k++){
temp[k] = buf[len - k - 1];
// as i understand it:
// str->bytes.data = allocated memory
// str->bytes.dim = size of the allocated memory
// str->bytes.usage= size of the used memory
//
#define BLOCK_SIZE 32
bool string_insert(string_t * const str, size_t index,
const char * buf, size_t len)
{
int max_size;
int i;
char *start;
char *end;
char *tmp=str->bytes.data;
max_size=str->bytes.usage+len;
if(max_size>=str->bytes.dim){
// realloc by BLOCK_SIZE steps
max_size=((max_size/BLOCK_SIZE)+1)*BLOCK_SIZE;
tmp=realloc(tmp,max_size);
if(!tmp) return 0; //false
//update fields with new values
str->bytes.data=tmp;
str->bytes.dim=max_size;
}
start=tmp+index;
end=tmp+(str->bytes.usage);
//shift content after the insertion point
while(end>start){
end--;
*(end+len)=*end;
}
// no comments
strncpy (start,buf,len);
str->bytes.usage+=len;
return 1;
}
Rather than the posted convolutions, you could try something similar to this
Assuming:
Not normal C strings
the string_t instances do not contain enough unused memory to
hold both the strings
string 2 is to be inserted at the offset into string 1
the string_t is defined as:
typedef struct
{
char * pStr;
int strLen;
} string_t;
caveat: not compiled, nor tested
string_t *string_insert(
const string_t * originalStr,
const string_t * strToInsert,
size_t index )
{
string_t *newString = NULL;
if( NULL != (newString = malloc( sizeof( string_t )) ) )
{ // then malloc successful
newString->pStr = NULL;
newString->strLen = 0;
// allocate enough room.
if(NULL != (newString->pStr =
malloc( originalStr->strlen + strToInsert->strlen )
{// then mallooc successful
newString->strLen =
origianlStr->strlen + strToInsert->strLen;
//copy first part of originalStr to newString
for( int i = 0; i < index; i++ )
{
newString->pStr[i] = originalStr->pStr[i];
}
// copy string to insert
for( int j=0; j<strToInsert->strlen; j++ )
{
newStr->pStr[i+j] = strToInsert-pStr[j];
}
// copy last part of originalStr to newStr
for ( int k = 0; k<newString->strLen; k++ )
{
newStr->pStr[i+j+k] = origianalStr->pStr[index+k];
}
}
else
{
free( newString );
newString = NULL;
}
}
return newString
} // end function: string_insert
You have
if (str->len < index){
, but you fail to assign anything to str->len.
Related
char* putNum(Node* head)
{
char* str;
int size = 0;
int index = 0;
int lastNum, count = 0;
Node* temp = head->left;
lastNum = temp->info;
while (lastNum != 0)
{
lastNum = lastNum / 10;
count++;
}
if (head->info < 0)
{
size = abs(head->info) - 1;
size *= 5 + count;
str = (char*)malloc(sizeof(char) * size);
str[0] = '-';
index++;
}
else
{
size = head->info - 1;
size *= 5 + count;
str = (char*)malloc(sizeof(char) * size);
}
for (int i = 0; i < abs(head->info); i++)
{
char chars[5];
itoa(temp->info, chars, 10);
for (int j = 0; j < strlen(chars); j++)
{
str[index++] = chars[j];
}
temp = temp->left;
}
return str;
}
hi im learing c in visual studio and im tring to make a function that take a doubly linked list with a long number and put the number in a string. the error pops in the last '}' of the function.
I am making a "simple" print out string, append string and remove section from a string. The append and new string works sometimes, sometimes it outputs nothing.
When i do:
char * temp = malloc(newSize);
It just stops outputting anything.
I have commented everything out in sections, trying to find the problem. Can't seem to find the problem, but google keeps coming up with "Heap Corruption".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char * data;
int length;
} String;
int str_getLength(const char * characters)
{
int index = 0;
while (1)
{
if (characters[index] == '\0') break;
index++;
}
return index;
}
String str_new(const char * characters)
{
String result;
result.length = str_getLength(characters);
result.data = malloc(result.length);
memcpy(result.data, characters, result.length);
return result;
}
void str_append(String * str, const char * characters)
{
int charsLength = str_getLength(characters);
str->data = realloc(str->data, charsLength);
for (int i = 0; i < charsLength; i++) {
str->data[i + str->length] = characters[i];
}
str->length = str->length + charsLength;
}
void str_remove(String * str, int startIndex, int endIndex)
{
if (startIndex < 0 || endIndex > str->length || endIndex < startIndex) {
return;
}
int chunkSize = endIndex - startIndex;
int newSize = str->length - chunkSize;
char * temp = malloc(newSize);
// for (int i = 0; i < str->length; i++)
// {
// if (i < startIndex || i > endIndex) {
// temp[i] = str->data[i];
// }
// }
// free(str->data);
// str->length = newSize;
// str->data = temp;
}
}
int main()
{
String str = str_new("Hello, ");
printf("%s\n", str.data);
str_append(&str, "this is my first C application.");
printf("%s\n", str.data);
str_remove(&str, 0, 3);
printf("%s\n", str.data);
free(str.data);
return 0;
}
I expected it to output a modified string, it doesn't and sometimes it outputs nothing.
I am a beginner, sorry if it's a quick fix.
Apart from the blaze answer.
There are few more problems.
// for (int i = 0; i < str->length; i++)
// {
// if (i < startIndex || i > endIndex) {
// temp[i] = str->data[i];
// }
// }
You will access out of bound for temp.
You need to maintain separate index for temp.
char * temp = malloc(newSize+1);
int k=0;
for (int i = 0; i < str->length; i++)
{
if (i < startIndex || i > endIndex) {
temp[k++] = str->data[i];
}
}
temp[k] = '\0';
free(str->data);
str->length = newSize;
str->data = temp;
And
You are not null terminating the string after append.
str->data = realloc(str->data, str->length + charsLength +1); //current length + new length + \0
for (int i = 0; i < charsLength; i++) {
str->data[i + str->length] = characters[i];
}
str->data[i + str->length] = '\0'; //null terminate the new string
str->length = str->length + charsLength;
There are two problems with your reallocation. First of all, you're not assigning the result of the realloc to str->data, so if the memory was reallocated to a different place, tr->data points to invalid memory afterwards. Second, you're not adding the sizes of the string and the appended part, you're just taking the size of the part that you're appending.
This here
realloc(str->data, charsLength);
Should be:
str->data = realloc(str->data, charsLength + str->length + 1);
Is it possible to concatenate and add an array of pointers into one index of another array of pointers. I'm trying to take the strings inside my *token pointer and make it one string inside the first index of my commands pointer array, so on and so forth
cmd = strtok(str, " ");
while(n < 5 && (act_token = strtok(NULL, " ")))
{
token[n] = act_token;
n++;
}
token[n] = NULL;
/* Below is where I'm trying to add all the elements of the token array into one index of the comands array */
while( z < len ){
comands[b] = token[z];
z++;
}
b++;
}
To avoid O(n*n) complexity caused by looping a concatenation, in #zzxyz otherwise good answer, consider copying to the end of the accumulated destination.
char *concat_alloc(const char *token[], size_t n) {
size_t sum = 1;
for (size_t i = 0; i < n; i++) {
size_t len = strlen(token[i]);
sum += len;
if (sum < len) {
return NULL; // Too long
}
}
char *dest = malloc(sum);
if (dest) {
char *p = dest;
for (size_t i = 0; i < n; i++) {
size_t len = strlen(token[i]);
memcpy(p, token[i], len);
p += len; // advance to the end
}
*p = '\0';
}
return dest;
}
I feel your pain. String handling is very bad in C, and almost as bad in C++.
However, once you write the function, all you have to do is call it...
char *GetStringFromStringArray(const char**sourceStrings, size_t nCount)
{
char *destString = NULL;
size_t destLength = 1; //start with room for null-terminator
if (nCount == 0)
return destString;
for (size_t i = 0; i < nCount; i++)
destLength += strlen(sourceStrings[i]);
destString = (char*)malloc(destLength);
strcpy(destString, sourceStrings[0]);
for (size_t i = 1; i < nCount; i++)
strcat(destString, sourceStrings[i]);
return destString;
}
int main()
{
char *tokens[10] = { "bob", "jim", "hank" };
char *destStrings[2];
destStrings[0] = GetStringFromStringArray((const char**)tokens, 2);
destStrings[1] = GetStringFromStringArray((const char**)&tokens[1], 2);
free(destStrings[0]);
free(destStrings[1]);
}
The way I initialized tokens is not ok, by the way. Purely for easy example.
I'm trying to program a split that takes in a char-array with multiple words and separates each word into their own smaller char-array. All the pointers of the smaller char-arrays are kept in a pointer array so I can return a double pointer.
Can you take a look at my code and see if you see any errors. When I try to run my program my computer gets gradually slower, after 3-4 seconds I can't move my mouse or alt+f4 my editor. So something has to be seriously wrong!
Also I'm completely new to C-programming so I will most definitely have a silly mistake in there.
char **split(char *s) {
char **result;
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
//Counts the number of words to allocate memory for the pointer-array.
for(int i = 0; i < strlen(s); i++) {
if(s[i] == ' ') {
wrd_cnt++;
}
}
result = malloc(wrd_cnt * sizeof(char*));
//Counts letters in each word to allocate memory for every single small char-array with malloc.
for(int i = 0; i < strlen(s); i++) {
for(int j = 0; j < (wrd_cnt); j++) {
int char_cnt = 0;
for(int k = 0; s[i] != ' ' || s[i] != '\0'; k++, i++) {
char_cnt++;
result[j] = malloc(char_cnt * sizeof(char));
}
}
}
//Puts each word into their own place in the pointer array.
for(int i = 0; i < strlen(s); i++) {
for(int j = 0; j < (wrd_cnt); j++) {
for(int k = 0; s[i] != ' ' || s[i] != '\0'; k++, i++) {
result[j][k] = s[i];
}
}
}
result[wrd_cnt-1] = NULL;
return result;
}
In this situation the loops using j and k can be removed and instead increment and reset i, j and char_cnt based on the i loop as the s array is processed, similar to what you had done for wrd_cnt in the first loop
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char **split(char *s);
int main ( void) {
char **output = NULL;
int each = 0;
char line[99] = " string to parse for words ";
output = split ( line);
each = 0;
while ( output[each]) {
printf ( "%s\n", output[each]);
each++;
}
each = 0;
while ( output[each]) {
free ( output[each]);
each++;
}
free ( output);
exit ( 0);
}
char **split(char *s) {
char **result;
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
int char_cnt = 0;
int i = 0;
int j = 0;
int k = 0;
//Counts the number of words to allocate memory for the pointer-array.
for(i = 0; i < strlen(s); i++) {
if(s[i] == ' ') {
wrd_cnt++;
}
}
if ( ( result = malloc(wrd_cnt * sizeof(char*))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
//Counts letters in each word to allocate memory for every single small char-array with malloc.
char_cnt = 1;
j = 0;
for( i = 0; i < strlen(s); i++) {
if ( s[i] == ' ') {
if ( ( result[j] = malloc(char_cnt * sizeof(char))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
j++;
char_cnt = 1;
continue;
}
char_cnt++;
}
if ( j == wrd_cnt - 2) {
//allocate for last word
if ( ( result[j] = malloc(char_cnt * sizeof(char))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
j++;
result[j] = NULL;
}
result[wrd_cnt - 1] = NULL;//just to make sure the last pointer is null
//Puts each word into their own place in the pointer array.
j = 0;
k = 0;
for( i = 0; i < strlen(s); i++) {
if ( s[i] == ' ') {
result[j][k] = '\0';//for space only so [j][0] is '\0'
k = 0;
j++;
continue;
}
result[j][k] = s[i];
k++;
result[j][k] = '\0';//for last word if there is no final space in s[]
}
return result;
}
Aside from the comments above your code scares me because of all the malloc() calls you do, one for each word. This means you must also free each word. This leaves programs open to memory leaks.
Given that this is C, which allows lots of casting, you can use a single malloc to hold both the (char *) pointer array AND the actual words.
char **split(char const *s) {
char **result; //
char *target; // where in result chars stored
size_t s_strlen = strlen(s); // length of s
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
{
char const *sx;
for ( sx = s; sx = strpbrk( sx, " \t\n\r" ); sx++ )
{
wrd_cnt++;
}
}
result = malloc( (wrd_cnt * sizeof(char *)) + s_strlen + 2 );
/* allow for \0 and possible ' ' */
target = (char *)(result + wrd_cnt); /* where to save words */
strcpy( target, s ); /* copy to target known to be big enough */
if ( s_strlen > 0 && target[s_strlen-1] != ' ' )
strcat( target + s_strlen, " " ); /* assure ends in space */
{
char *tx, *tnext;
int n;
n = 0;
for ( tx = target; tnext = strpbrk( tx, " \t\n\r" ); tx = tnext + 1 )
{
result[n++] = tx; /* remember pointer */
*tnext = '\0'; /* terminate word */
}
result[n] = NULL; /* null termination */
}
return result;
}
I am trying to convert a vector into a string using the following function.
char* my_vect2str(char** input)
{
int i;
char* ret = (char*)xmalloc(sizeof(char*));
for(i=0; input[i] != NULL; i++)
{
if(*input[i] == '\0')
ret[i] = ' ';
else
ret[i] = *input[i];
}
ret[i] = '\0';
return ret;
}
This appears to be getting just the first character of each string in the vector. How do I alter my for loop to get this working properly? Thanks!
Your malloc should be the size of the pointer contents, not the pointer itself. You also don't need to cast the malloc void *. You need an inner loop counter in order to iterate through both dimensions of you pointer. This should work:
char* my_vect2str(char** input)
{
int i;
int count = 0;
char* ret = (char*)malloc(sizeof(char*)); // should be a larger size
for(i=0; input[i] != NULL; i++)
{
int j = 0;
while(1){
if(input[i][j] == '\0'){
ret[count++] = ' ';
break;
}else{
ret[count++] = input[i][j];
}
j++;
}
}
ret[count] = '\0';
return ret;
}
The first loop calculates the total size of the strings in input. Then, the space is allocated and the strings are concatenated to ret.
char* my_vect2str(char** input)
{
int i, j, k = 0;
char* ret;
int size = 0;
int len;
char* inp = input[k++];
while (inp != NULL) {
size += strlen(inp);
inp = input[k++];
}
ret = malloc((size * sizeof(char)) + 1);
memset(ret, 0, size + 1);
i = 0;
j = 0;
while (i < size) {
if (input[j] != NULL) {
len = strlen(input[j]);
memcpy(&ret[i], input[j], len);
i += len;
}
++j;
}
return ret;
}