I am creating a function to split a char * into an array of other char * based on a target (which can be " ", "a", etc). In this example, I am using a white space (" ") to split my char * into an array of char *.
However, I am having some difficulties dealing with the dynamically allocated memory. I allocated memory for both the array of char * that I will be returning as the split function return value, and another one to copy each separated const * that I am reading from the main char * parameter.
#define MAX_SIZE 65535
#define MAX_SPLIT_SIZE 1023
char** a_split(char * x, const char * target, int sn){
char ** sa = nullptr;
size_t t_len = strnlen(target, MAX_SIZE);
if(t_len < 1 || t_len > MAX_SIZE){ return sa; }
int split_num = 0;
if(sn > 1 && sn < MAX_SPLIT_SIZE){
sa = new char * [sn + 1]();
split_num = sn;
}else if(sn == -1){
sa = new char * [MAX_SPLIT_SIZE + 1];
split_num = MAX_SPLIT_SIZE;
}else {
return sa;
}
char * ptr = x;
char * mi; // Match index.
int i = 0; // Index of 'sa' array.
while((mi = std::strstr(ptr, target)) && split_num--){
size_t dif = mi - ptr;
char * n_cstring = new char[dif + 1]();
memcpy(n_cstring, ptr, dif); // Copying content to new string.
sa[i++] = n_cstring; // Append new string to 'sa' array of split strings.
ptr += dif;
ptr += t_len;
delete [] n_cstring; // <------- This is causing some weird errors.
}
if(mi == nullptr){
sa[i] = ptr;
}
return sa;
}
int main(int argc, char * argv[]){
char c[] = "I love Thanos";
char ** x = a_split(c, " ", -1);
for(int i = 0; x[i]; i++){
puts(x[i]);
}
return 0;
}
I found out that when using 'delete [] n_cstring', instead of outputting the char *'s separately (like in "I" "love" "Thanos"), it is outputting "love" "love" "Thanos". It is making this kind of repetition for every example. Why is 'delete' doing this?
Also, as I am returning a dynamically allocated array ('sa'), where would you recommend me to delete it? - the main function does not recognize 'sa'.
One problem is that you are deleting n_cstring too soon: since you are storing it in sa[] array, i.e.
sa[i++] = n_cstring;
deleting it right after that in the loop leaves sa[i] hanging. The end result is that your main would have undefined behavior, in addition to having memory leaks due to never deleting sa.
I am returning a dynamically allocated array (sa), where would you recommend me to delete it?
There is only one place where this could be done - it's main. You put sa into x, so you need to invoke delete[] x once you are done printing.
Note that since delete[] n_cstring must be removed from a_split function, main must also delete the individual elements of x before deleting x itself.
The best approach by far is to change your code to use std::vector<std::string>. This would free you from allocating and deleting character arrays, fixing potential crashes automatically.
Related
I have code that is supposed to remove all characters in one C-string from another.
The problem arises when I try to use the function strstr: both an array and a char* get converted to bool. Obviously it doesn't work because strstr needs to receive 2 char* arguments.
#include <iostream>
#include <ctype.h>
#include <string.h>
#include <malloc.h>
using namespace std;
char* temp_char(char* orig,char* del)
{ char *str_temp, *temp, *p, *c;
char symbol[2];
int i=0;
int len = strlen(orig);
temp=(char *)calloc(len,1);
str_temp = (char *)calloc(len,1);
strcpy(str_temp,orig);
for(i=0;i<strlen(del);i++){
symbol[0]=del[i];
symbol[1]=0;
temp[0]=0;
while( p=strstr(str_temp,del)!=NULL){
strncat(temp,str_temp,p-str_temp);
p++;
str_temp=p;
}
strcat(temp,str_temp);
strcpy(str_temp,temp);
}
cout<<temp;
return temp;
}
int main () {
char a[]="stuccsess",b[]="test";
temp_char(a,b);}
Any help would be appreciated.
You have two errors in one line of your code. The first is not addressing the issue that the != operator has higher priority than =. Thus, in the following line:
while( p=strstr(str_temp,del)!=NULL) {
the comparison is actually:
while( p = ( strstr(str_temp,del)!=NULL ) ){
So, you are attempting to assign the result of the != test, which will be a bool value, to a char* variable (p).
You can fix this, easily, by putting the assignment expression in parentheses.
However, although that will fix the compiler error, it will not address the second error. Just a few lines above that while statement, you assign one of the characters of the del string to the first of the symbol string (and, properly, add a nul terminator to that) … but you never then actually use that symbol string. Instead, you pass the entire del string as the second argument in the call to strstr. You should be passing your created symbol as that second argument.
So, changing that while line to the following will make the code work:
while ((p = strstr(str_temp, symbol)) != nullptr) { // Use nullptr in C++
But that leaves other errors in your function. How will you ever be able to free the memory allocated in the str_temp = (char*)calloc(len, 1); call? Once you have subsequently modified that str_temp pointer (in the str_temp = p; line), you no longer have the original pointer value, which must be used when calling free(). So, you need to save that value, just after you have made the allocation. (The temp memory pointer is returned, so that can be freed in the main function.)
There are other issues in your code that could be improved, like using new[] and delete[] in C++, rather than the old, C-language calloc, and that your index and length variables should really be of size_t type, rather than int. Here's a version with the corrections and suggestions discussed applied:
#include <iostream>
#include <cstring>
using std::cout;
char* temp_char(char* orig, char* del)
{
size_t len = strlen(orig);
char* temp = new char[len + 1];
char* str_temp = new char[len + 1];
char* save_temp = temp; // Save it so we can call delete...
strcpy(str_temp, orig);
for (size_t i = 0; i < strlen(del); i++) {
char symbol[2];
symbol[0] = del[i];
symbol[1] = 0;
temp[0] = 0;
char* p;
while ((p = strstr(str_temp, symbol)) != nullptr) {
strncat(temp, str_temp, static_cast<size_t>(p - str_temp));
p++;
str_temp = p;
}
strcat(temp, str_temp);
strcpy(str_temp, temp);
}
cout << temp;
delete[] save_temp; // ... don't forget to free this.
return temp;
}
int main()
{
char a[] = "stuccsess", b[] = "test";
char* answer = temp_char(a, b);
delete[] answer; // We should free the memory allocated!
return 0;
}
But your approach is far more complicated than it need be. You can simply loop through the original string and check each character to see if it is in the 'delete' string (using the strchr function, for example); if it is not (i.e. that strchr returns nullptr), then append the character to the accumulated temp and increase the running length:
char* temp_char(char* orig, char* del)
{
size_t len = strlen(orig);
char* temp = new char[len + 1];
int newlen = 0;
for (char* cp = orig; *cp != '\0'; ++cp) {
if (strchr(del, *cp) == nullptr) temp[newlen++] = *cp;
}
temp[newlen] = '\0'; // Add null terminator
std::cout << temp;
return temp;
}
I'm doing an exercise in which I have to copy a c-style string into memory allocated on free store. I am required to do it without using subscripting and relying solely on pointer arithmetic. I wrote the following function-
char* str_dup(const char* s)
{
// count no. of elements
int i = 0;
const char* q = s;
while (*q) { ++i; ++q; }
//create an array +1 for terminating 0
char* scpy = new char[i + 1];
//copy elements to new array
while (*s)
{
*scpy = *s;
++s;
++scpy;
}
*scpy = 0;
return scpy;
}
The function is returning random characters. But if I change it into this-
char* str_dup(const char* s)
{
// count no. of elements
int i = 0;
const char* q = s;
while (*q) { ++i; ++q; }
//create an array +1 for terminating 0
char* scpyx = new char[i + 1];
char* scpy = scpyx;
//copy elements to new array
while (*s)
{
*scpy = *s;
++s;
++scpy;
}
*scpy = 0;
return scpyx;
}
it works. Can someone explain me why first code is not working and second is working?
The first code is not working since you return the final value of scpy, which at that point points at the terminating NUL character, and not the start of the string.
One solution is to do as you did, and save a copy of the original pointer to have something to return.
You should really use strlen() and memcpy(), they make this easier but perhaps they're off-limits to you.
I am coding this in C++. My current issue at hand is that I have to trim the whitespace from the beginning of the character array. I am not allowed to use any string functions. My idea is to count the number of whitespaces at the beginning, allocate memory based on how much less memory I would need in a character array if I didn't have those whitespaces, do so, and then copy over the new string and deallocate the original string.
My issue is that I can't seem to deallocate that string without Visual Studio hitting a break point for me. I can get it working with the code I have below, (not deallocating the roginal strig at all) d=but wouldn't that cause a memory leak?
Thanks for your help in advance.
#include <iostream>
using namespace std;
class SmartString{
private:
char* str;
public:
SmartString ( )
{
str = NULL;
}
SmartString (char *str){
int length = 0;
int copy_index = 0;
while(str[length] != '\0')
{
length++;
}
length++;
char * copy;
copy = (char*)malloc(sizeof(char) * length);
copy = new char[length];
while(copy_index < length)
{
copy[copy_index] = str[copy_index];
cout << str[copy_index];
copy_index++;
}
this -> str = copy;
}
~ SmartString()
{
if(str != NULL)
{
delete str;
free(str);
}
}
void ShowString()
{
cout << "[" << str << "]";
}
int Size()
{
if(str == NULL)
return 0;
else
{
int i = 0;
while(str[i] != '\0')
{
i++;
}
i++;
return i;
}
}
**void Trim()
{
int counter = 0;
while (str[counter] == ' ' && counter < Size())
{
counter++;
}
int new_length = Size() - (counter + 1);
char * temp;
temp = (char*) malloc(sizeof(char) * new_length);
temp = new char[new_length];
int counter_2 = 0;
while(counter_2 < Size())
{
temp[counter_2] = str[counter_2 + counter];
counter_2++;
}
str = temp;
}**
};
int main()
{
char *str;
str = " Hello";
SmartString * s = new SmartString(str);
str = "Change";
(*s).Trim();
(*s).ShowString();
system("Pause");
}
You have not used 'delete' in your main function to deallocate your 's' pointer variable, So that the destructor method of your 'SmartString' class never called. In your second constrcutor method, you I've allocated the 'copy' variable twice where it's not need And also you have some mistake in your 'Trim' method.
In your destructor method, You should remove the free(str); statement cause the delete str; statement will deallocate the 'str'. So there is no need to deallocate twice.
malloc - Allocates the requested memory and returns a pointer to it.
new X; - Do the same thing but also calls constructor method if X is a class or struct after allocating.
new X[] - Allocates dynamic array with the requested memory and returns a pointer to it.
free - Deallocates the memory previously allocated.
delete - Do the same thing but also calls destructor method if X is a class or struct after deallocating.
delete[] - Deallocates the memory previously allocated dynamic array.
new and delete is the standard memory allocation and deallocation implement of C++ language where malloc and free is the standard memory allocation and deallocation function of C language.
Here I've rewritten your 'Trim' method:
void Trim()
{
int counter = 0;
while (str[counter] == ' ' && counter < Size())
{
counter++;
}
int new_length = Size() - (counter + 1);
char * temp;
// There is no need to allocate twice
//temp = (char*) malloc(sizeof(char) * new_length);
temp = new char[new_length+1];
int counter_2 = 0;
while(counter_2 < //Size() ( Here is your big mistake. You should not use 'Size()' here )
new_length
)
{
temp[counter_2] = str[counter_2 + counter];
counter_2++;
}
temp[counter_2] = 0;
str = temp;
}
And for deallocating, you have to use the 'delete' like this:
int main()
{
char *str;
str = " Hello";
SmartString * s = new SmartString(str);
str = "Change";
(*s).Trim();
(*s).ShowString();
// use delete to deallocate a pointer
delete s;
system("pause");
}
I see three reasonable approaches to this.
One would be to modify the existing string in-place. Find the position of the first non-space character, then copy from there to the end of the string to positions starting from the first element of the string. This can't be applied to a string literal (or you'll get undefined behavior).
The second would be to allocate a new buffer and copy the data you want to keep into that buffer. In this case, you probably do not want to try to modify the original (and, especially, you don't want to try to free its data).
The third would be to (basically) re-implement a class about like std::string, that always allocates a buffer in a specific way, so it "knows" how to manipulate that buffer safely. In this case, you could/would have a constructor to create an object from a string literal, so by the time your function was invoked, it would only (even attempt to) manipulate such objects and could never accidentally try to manipulate/modify something like a string literal.
Right off the bat, I'm required to use dynamically allocated character arrays for my assignment, so do NOT suggest I just use strings. I need to create a method that accepts a character array as an argument, and inserts that character into a char* using strcpy. How do I do this without first initializing the char*?
Here is my code so far:
char* char_array;
char test_array[] = {'t','e','s','t','\0'};
strcpy(char_array, test_array);
Your char_array is just an unitialized pointer. You need to dynamically allocate memory for it and then carry out strcpy.
char* char_array = new char[6];
char test_array[] = {'t','e','s','t','\0'};
strcpy(char_array, test_array);
Or as suggested by Joachim you can use strdup() to duplicate a string - it will allocate the memory and copy the string into it.
In both cases, don't forget to free() (or delete[]) the resulting memory once you're done with it.
You can't do that unless you actually allocate a chunk of memory for char_array through malloc or new.
int length = 6;
char* char_array = (char*) malloc(sizeof(char) * length);
or
char* char_array = new char[6];
char * char_array = NULL;
void yourFunc(char your_array[]) {
if (NULL != char_array) {
free(char_array);
char_array = NULL;
}
char_array = (char *)malloc(sizeof(char) * strlen(your_array));
strcpy(char_array, your_array);
}
you stated you need a method/function that accepts a char[]/char *
you have stated your constraints ...
this does seem to be low level for instructional purpose
I assuming null terminated character array and valid source character array
//function accepts character array
char * charseqduplicate(char * s)
{
//low level c
const int len = strlen(s) + 1;;
char * copy = new char[len];
strcpy(copy, s);
//remember to delete or use something like unique_ptr
return copy;
}
void letsdothis()
{
//low level c
char test[] = {'y','e','s',char(0)};
char * dup = charseqduplicate(test);
cout << dup;
delete [] dup;
}
I've tried so may ways on the Internet to append a character to a char* but none of them seems to work. Here is one of my incomplete solution:
char* appendCharToCharArray(char * array, char a)
{
char* ret = "";
if (array!="")
{
char * ret = new char[strlen(array) + 1 + 1]; // + 1 char + 1 for null;
strcpy(ret,array);
}
else
{
ret = new char[2];
strcpy(ret,array);
}
ret[strlen(array)] = a; // (1)
ret[strlen(array)+1] = '\0';
return ret;
}
This only works when the passed array is "" (blank inside). Otherwise it doesn't help (and got an error at (1)). Could you guys please help me with this ? Thanks so much in advanced !
Remove those char * ret declarations inside if blocks which hide outer ret. Therefor you have memory leak and on the other hand un-allocated memory for ret.
To compare a c-style string you should use strcmp(array,"") not array!="". Your final code should looks like below:
char* appendCharToCharArray(char* array, char a)
{
size_t len = strlen(array);
char* ret = new char[len+2];
strcpy(ret, array);
ret[len] = a;
ret[len+1] = '\0';
return ret;
}
Note that, you must handle the allocated memory of returned ret somewhere by delete[] it.
Why you don't use std::string? it has .append method to append a character at the end of a string:
std::string str;
str.append('x');
// or
str += x;
The function name does not reflect the semantic of the function. In fact you do not append a character. You create a new character array that contains the original array plus the given character. So if you indeed need a function that appends a character to a character array I would write it the following way
bool AppendCharToCharArray( char *array, size_t n, char c )
{
size_t sz = std::strlen( array );
if ( sz + 1 < n )
{
array[sz] = c;
array[sz + 1] = '\0';
}
return ( sz + 1 < n );
}
If you need a function that will contain a copy of the original array plus the given character then it could look the following way
char * CharArrayPlusChar( const char *array, char c )
{
size_t sz = std::strlen( array );
char *s = new char[sz + 2];
std::strcpy( s, array );
s[sz] = c;
s[sz + 1] = '\0';
return ( s );
}
The specific problem is that you're declaring a new variable instead of assigning to an existing one:
char * ret = new char[strlen(array) + 1 + 1];
^^^^^^ Remove this
and trying to compare string values by comparing pointers:
if (array!="") // Wrong - compares pointer with address of string literal
if (array[0] == 0) // Better - checks for empty string
although there's no need to make that comparison at all; the first branch will do the right thing whether or not the string is empty.
The more general problem is that you're messing around with nasty, error-prone C-style string manipulation in C++. Use std::string and it will manage all the memory allocation for you:
std::string appendCharToString(std::string const & s, char a) {
return s + a;
}
char ch = 't';
char chArray[2];
sprintf(chArray, "%c", ch);
char chOutput[10]="tes";
strcat(chOutput, chArray);
cout<<chOutput;
OUTPUT:
test