EDIT: I don't want to delete the post because I have learned a lot very quickly from it and it might do someone else good, but there is no need for anyone else to spend time answering or viewing this question. The problems were in my programming fundamentals, and that is something that just can't be fixed in a quick response. To all who posted, thanks for the help, quite humbling!
Hey all, I'm working on building my own string class with very basic functionality. I am having difficulty understand what is going on with the basic class that I have define, and believe there is some sort of error dealing with the scope occurring. When I try to view the objects I created, all the fields are described as (obviously bad pointer). Also, if I make the data fields public or build an accessor method, the program crashes. For some reason the pointer for the object is 0xccccccccc which points to no where.
How can a I fix this? Any help/comments are much appreciated.
//This is a custom string class, so far the only functions are
//constructing and appending
#include<iostream>
using namespace std;
class MyString1
{
public:
MyString1()
{
//no arg constructor
char *string;
string = new char[0];
string[0] ='\0';
std::cout << string;
size = 1;
}
//constructor receives pointer to character array
MyString1(char* chars)
{
int index = 0;
//Determine the length of the array
while (chars[index] != NULL)
index++;
//Allocate dynamic memory on the heap
char *string;
string = new char[index+1];
//Copy the contents of the array pointed by chars into string, the char array of the object
for (int ii = 0; ii < index; ii++)
string[ii] = chars[ii];
string[index+1] = '\0';
size = index+1;
}
MyString1 append(MyString1 s)
{
//determine new size of the appended array and allocate memory
int newsize = s.size + size;
MyString1 MyString2;
char *newstring;
newstring = new char[newsize+1];
int index = 0;
//load the first string into the array
for (int ii = 0; ii < size; ii++)
{
newstring[ii] = string[ii];
index++;
}
for(int jj = 0; jj < s.size; jj++, ii++)
{
newstring[ii] = s.string[jj++];
index++;
}
//null terminate
newstring[newsize+1] = '\0';
delete string;
//generate the object for return
MyString2.string=newstring;
MyString2.size=newsize;
return MyString2;
}
private:
char *string;
int size;
};
int main()
{
MyString1 string1;
MyString1 string2("Hello There");
MyString1 string3("Buddy");
string2.append(string3);
return 0;
}
EDIT:
Thank you everyone so far who has responded and dealing with my massive lack of understanding of this topic. I'll begin to work with all of the answers, but thanks again for the good responses, sorry my question is vague, but there isn't really a specific error, but more of a lack of understanding of arrays and classes.
Here's just the mistakes from the first constructor.
MyString1()
{
//no arg constructor
char *string; //defines local variable that hides the member by that name
string = new char[0]; //sort of meaningless
string[0] ='\0'; //not enough room for that (out-of-bounds)
std::cout << string;
size = 1; //I don't think you should count null as part of the string
}
Similar mistakes elsewhere.
Also you should pass parameters in a more careful way.
MyString1(const char* source); //note const
MyString1 append(const MyString1& what); //note const and reference
If the latter is correct, also depends on what it is supposed to do. Based on std::string the expected result would be:
MyString1 a("Hello "), b("world");
a.append(b);
assert(a == "Hello world");
Some comments on your code:
MyString1()
{
//no arg constructor
Perhaps your instruction requires it, but in general this is the kind of comment that's worse than useless. Comments should tell the reader things that aren't obvious from the first glance at the code.
char *string;
string = new char[0];
string[0] ='\0';
This invokes undefined behavior. Calling new with zero elements is allowed, but you can't dereference what it returns (it may return a null pointer, or it may return a non-null pointer that doesn't refer to any storage). In most cases, you're better off just setting the pointer to NULL.
std::cout << string;
What's the point of writing out an empty string?
size = 1;
The string is empty so by normal figuring, the size is zero.
//constructor receives pointer to character array
Still useless.
MyString1(char* chars)
Since you aren't (or shouldn't be anyway) planning to modify the input data, this parameter should be char const *.
{
int index = 0;
//Determine the length of the array
while (chars[index] != NULL)
index++;
While this works, "NULL" should really be reserved for use as a pointer, at least IMO. I'd write it something like:
while (chars[index] != '\0')
++index;
Unless you're using the previous value, prefer pre-increment to post-increment.
//Allocate dynamic memory on the heap
As opposed to allocating static memory on the heap?
MyString1 MyString2;
Using the same naming convention for types and variables is confusing.
while (string[index] != NULL)
Same comment about NULL as previously applies here.
MyString1 append(MyString1 s)
IMO, the whole idea of this function is just plain wrong -- if you have a string, and ask this to append something to your string, it destroys your original string, and (worse) leaves it in an unusable state -- when you get around to adding a destructor that frees the memory owned by the string, it's going to cause double-deletion of the storage of a string that was the subject (victim?) of having append called on it.
I'd consider writing a private "copy" function, and using that in the implementations of some (most?) of what you've shown here.
As a bit of more general advice, I'd consider a couple more possibilities: first of all, instead of always allocating exactly the amount of space necessary for a string, I'd consider rounding the allocation to (say) a power of two. Second, if you want your string class to work well, you might consider implementing the "short string optimization". This consists of allocating space for a short string (e.g. 20 characters) in the body of the string object itself. Since many strings tend to be relatively short, this can improve speed (and reduce heap fragmentation and such) considerably by avoiding doing a heap allocation if the string is short.
index doesn't start at 0 in the second while loop in your append function. You should probably be using for loops. Oh, and you're using the wrong form of delete. You should be using delete[] string because string is an array.
There are numerous other style issues, and outright errors, but the thing I mentioned first is the basic error you were encountering.
I would write the append function in this way:
void append(MyString1 s)
{
//determine new size of the appended array and allocate memory
int newsize = s.size + size;
char *newstring = new char[newsize+1];
int destindex = 0;
for (int index = 0; index < size; ++index) {
newstring[destindex++] = string[index];
}
for (int index = 0; index < s.size; ++index) {
newstring[destindex++] = s.string[index];
}
newstring[destindex] = '\0';
delete[] string;
string = newstring;
}
Related
I read this sample code in a book. I can't figure out why this part of the following sample code's function declaration is necessary:
while (i <= n)
p[i++] = '\0'; // set rest of string to '\0'
Here is the whole code:
#include <iostream>
const int ArSize = 80;
char * left(const char * str, int n = 1);
int main()
{
using namespace std;
char sample[ArSize];
cout << "Enter a string:\n";
cin.get(sample,ArSize);
char *ps = left(sample, 4);
cout << ps << endl;
delete [] ps; // free old string
ps = left(sample);
cout << ps << endl;
delete [] ps; // free new string
return 0;
}
// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
char * left(const char * str, int n)
{
if(n < 0)
n = 0;
char * p = new char[n+1];
int i;
for (i = 0; i < n && str[i]; i++)
p[i] = str[i]; // copy characters
while (i <= n)
p[i++] = '\0'; // set rest of string to '\0'
return p;
}
I ran the code after I erased it and there was no problem.
The loop is unnecessary. Null-terminated strings end at the first null byte. If more memory was allocated than the actual string needs, it does not matter what’s in those extra bytes. All non-broken C-string handling code stops at the first null terminator. All that’s required is a single
p[i] = '\0';
after the for loop. However, that one null byte is mandatory. C-string functions depend on it and will happily overrun the allocated memory if it’s missing. Essentially they’ll (try to) keep going until they stumble upon the next null byte in memory. If that is past the allocated memory it causes undefined behaviour, resulting in a crash if you’re lucky; or corrupted data if you’re less lucky.
That said: Throw away that book yesterday. The code is a catastrophe from the first to the last line. It barely qualifies as C++. Most of it is plain C. And even as C code it’s highly questionable.
Why to avoid using namespace std. #vol7ron pointed out in the comments that the major complaint is against using namespace std in headers. Here it’s used inside a function in a .cpp file, which lessens the impact significantly. Although in my opinion it is still worth avoiding. If you don’t know the implementation of your standard library in depth, you don’t really have an idea about all the symbols you pull into your scope. If you need it for readability, pulling in specific symbols (e.g. using std::cout;) is a better choice. Also, I’m confident I’m not alone in kind of expecting the std:: prefix. For example, std::string is what I expect to see. string looks slightly off. There’s always a lingering doubt that it might not be the std library string, but a custom string type. So, including the prefix can benefit readability as well.
Why all the C-string pain? We’ve had std::string for a while now …
Copying characters in a loop? Seriously? That’s what std::strcpy() is for.
Raw new and delete everywhere: error prone because you have to keep track of the new/delete pairs manually to avoid memory leaks.
Even worse: asymmetric owning raw pointers. left() allocates and returns a pointer; and it’s the caller’s responsibility to delete it. It doesn’t get more error prone than that.
… And these are only the problems that stick out on first glance.
What that piece of code should look like:
#include <iostream>
#include <string>
std::string left(const std::string& str, std::size_t len = 1);
int main()
{
// getline can fail. If that happens we get an empty string.
std::string sample;
std::getline(std::cin, sample);
auto ps = left(sample, 4);
std::cout << ps << '\n';
ps = left(sample);
std::cout << ps << '\n';
return 0;
}
// `len` may be longer than the string. In that case a copy
// of the complete input string is returned.
std::string left(const std::string& str, std::size_t len)
{
return str.substr(0, len);
}
I was working on a system that split a sentence to a 2D pointer.
I don't wanna use any kind of library or another ways like string, because I want to practice pointers and learn them.
char** sscanf(char* hstring)
{
int count = 0;
char* current = hstring;
while (*current)
{
if (*current == ' ')
{
count++;
}
while (*current == ' ')
{
current++;
}
if (*current)
break;
current++;
}
char** result = new char*[count];
current = hstring;
char* nstr = new char;
int c = 0, i = 0;
while (*current)
{
if (!*current) break;
cout << "t1";
if (*current == ' ')
{
*(++result) = nstr;
nstr = nullptr;
nstr = new char;
}
cout << "t2";
while (*current != '/0' && *current == ' ')
{
current++;
}
cout << "t3";
while (*current != '/0' && *current != ' ')
{
if (!*current) break;
*(++nstr) = *current;
current++;
}
cout << "t4";
*nstr = '/0';
cout << "t5";
}
return result;
}
But it's very strange, sometimes redirects me to
static size_t __CLRCALL_OR_CDECL length(_In_z_ const _Elem * const _First) _NOEXCEPT // strengthened
{ // find length of null-terminated string
return (_CSTD strlen(_First));
}
with error: Acces Violation, other times, choose a random line and call it Acces Breakout(sorry if I spelled wrong)
What I want from you is not to repair my code simply, I want some explanations, because I want to learn this stuff.
First, some advice
I understand that you are making this function as an exercise, but being C++ I'd like to warn you that things like new char*[count] are bad practices and that's why std::vector or std::array were created.
You seem confused about how dynamic allocation works. The statement char* nstr = new char; will create just one byte (char) in heap memory, and nothing is guaranteed to be adjacent to it. This means that ++nstr is a "invalid" operation, I mean, it's making the nstr point to the next byte after the allocated one, which can be some random invalid location.
There is a whole lot of other dangerous operations in your code, like calling new several times (which reserves memory) and not calling delete on them when you no longer use the reserved memory (aka. memory leaks). Having said that, I strongly encourage you to study this subject, for example starting with the ISO C++ FAQ on memory management.
Also, before digging into pointers and dynamic allocation, you should be more confortable with statements and flow control. I say this because I see some clear misunderstandings, like:
while (*current) {
if (!*current) break;
...
}
The check inside the if statement will certainly be false, because the while check is executed just before it and guarantees that the opposite condition is true. This means that this if is never evaluated to true and it's completely useless.
Another remark is: don't name your functions the same as standard libraries ones. sscanf is already taken, choose another (and more meaningful) one. This will save you some headaches in the future; be used to name your own functions properly.
A guided solution
I'm in a good mood, so I'll go through some steps here. Anyway, if someone is looking for an optimized and ready to go solution, see Split a String in C++.
0. Define the steps
Reading your code, I could guess some of your desired steps:
char** split_string(char* sentence)
{
// Count the number of words in the sentence
// Allocate memory for the answer (a 2D buffer)
// Write each word in the output
}
Instead of trying to get them right all at once, why don't you try one by one? (Notice the function's and parameter's names, clearer in my opinion).
1. Count the words
You could start with a simple main(), testing your solution. Here is mine (sorry, I couldn't just adapt yours). For those who are optimization-addicted, this is not an optimized solution, but a simple snippet for the OP.
// I'll be using this header and namespace on the next snippets too.
#include <iostream>
using namespace std;
int main()
{
char sentence[] = " This is my sentence ";
int n_words = 0;
char *p = sentence;
bool was_space = true; // see logic below
// Reading the whole sentence
while (*p) {
// Check if it's a space and advance pointer
bool is_space = (*p++ == ' ');
if (was_space && !is_space)
n_words++; // count as word a 'rising edge'
was_space = is_space;
}
cout << n_words;
}
Test it, make sure you understand why it works. Now, you can move to the next step.
2. Allocate the buffer
Well, you want to allocate one buffer for each word, so we need to know the size of each one of them (I'll not discuss whether or not this is a good approach to the split sentence problem..). This was not calculated on the previous step, so we might do it now.
int main()
{
char sentence[] = " This is my sentence ";
///// Count the number of words in the sentence
int n_words = 0;
char *p = sentence;
bool was_space = true; // see logic below
// Reading the whole sentence
while (*p) {
// Check if it's a space and advance pointer
bool is_space = (*p++ == ' ');
if (was_space && !is_space)
n_words++; // count as word a 'rising edge'
was_space = is_space;
}
///// Allocate memory for the answer (a 2D buffer)
// This is more like C than C++, but you asked for it
char **words = new char*[n_words];
char *ini = sentence; // the initial char of each word
for (int i = 0; i < n_words; ++i) {
while (*ini == ' ') ini++; // search next non-space char
char *end = ini + 1; // pointer to the end of the word
while (*end && *end != ' ') end++; // search for \0 or space
int word_size = end - ini; // find out the word size by address offset
ini = end; // next for-loop iteration starts
// at the next word
words[i] = new char[word_size]; // a whole buffer for one word
cout << i << ": " << word_size << endl; // debugging
}
// Deleting it all, one buffer at a time
for (int i = 0; i < n_words; ++i) {
delete[] words[i]; // delete[] is the syntax to delete an array
}
}
Notice that I'm deleting the allocated buffers inside the main(). When you move this logic to your function, this deallocation will be performed by the caller of the function, since it will probably use the buffers before deleting them.
3. Assigning each word to its buffer
I think you got the idea. Assign the words and move the logic to the separated function. Update your question with a Minimal, Complete, and Verifiable example if you still have troubles.
I know this is a Q&A forum, but I think this is already a healthy answer to the OP and to others that may pass here. Let me know if I should answer differently.
I have been given a task, where I need to create the string_copy function Note that the function body and prototypes have been given by the source and that needs to be maintained. The portions written by me are after the comment write your code here.
#include <iostream>
using namespace std;
int string_length(const char* string_c);
char* string_copy(const char* string_c);
int main()
{
const char* string_c = "This is a string and is a long one so that we can create memory leaks when it is copied and not deleted";
// write your code here
int length = string_length(string_c);
cout << "Copied String: " << string_copy(string_c) << endl;
return 0;
}
int string_length(const char* string) {
int length = 0;
for (const char* ptr = string; *ptr != '\0'; ++ptr) {
++length;
}
return length;
}
char* string_copy(const char* string) {
// we need to add 1 because of ’\0’
char* result = new char[string_length(string) + 1];
// write your code here (remember zero-termination !)
int i;
for (i = 0; string[i] != '\0'; ++i)
{
result[i] = string[i];
}
result[i] = '\0';
return result;
}
Now task tells me
that it is very important that any memory allocated with e=new TYPE is
released later with delete e (and a=new TYPE[size] with delete [] a)
else this will lead to an error.
It is not exactly clear if error means compile/runtime error or error as in my task did not meet the requirement error.
My question is, in this code how do I delete the intermediate dynamically created result array? If I delete result, won't it fail the purpose of the task? Then how am I to respect the quotation above or maybe simulate memory leak as given in the long string constant?
Thanks.
EDIT: Why the negative votes? Please at least explain the reason! I am not asking any solution or something, but mere suggestion if I am missing some point or not!
The caller of string_copy would be responsible for releasing the memory when it's done with it by calling delete[] on it.
This is, by the way, a terrible way to write C++ code. You should be using std::string or std::vector<char> or something like that.
Here's why:
int length = string_length(string_c);
char* copy = string_copy(string_c);
cout << "Copied String: " << copy << endl;
delete[] copy;
return 0;
Yuck.
In fact the ideal solution is to use std::string and not char *. There is no real need of using char * instead of std::string in your example.
With std::string:
You don't need to new anything
You don't need to delete anything
You can do everything with std::string, that you do with char *.
[PLEASE CHECK FINAL EDIT BELOW FOR UPDATE]
My C++ is a bit rusty (to say the least) and I'm having an issue trying to pass a char array into a function to manipulate the values. Example code below:
void myFunction(char* splitStrings,String stringToSetInLoop) {
char substringPtr[stringToSetInLoop.length()];
stringToSetInLoop.toCharArray(substringPtr, stringToSetInLoop.length());
for(int i = 0; i < 10; i++) {
splitStrings[i] = *substringPtr;
}
}
char *mySplitStrings[10];
myFunction(*mySplitStrings,String("Repeat Me"));
Serial.println(mySplitStrings[0]);
The code does not crash, but it outputs a blank line. I suspect that I need to initialize a 2 dimensional array outside the function to pass in so that memory space is allocated. I'm guessing that, although the substring pointer exists inside the function, the memory is destroyed, leaving the char* array mySplitStrings[0] pointing at nothing. Also, I think I need to pass in the reference to the array memory space, not as a pointer.
The ultimate goal here is to be able to pass a char array into a function, assign some values to it, then use those values back in the main code loop. If there's a better way to achieve this, then please let me know.
Thanks in advance. Please free me from my personal pointer/reference hell!
EDIT: Further note, this code is being run on an arduino, so the C++ is limited.
EDIT: When I try to pass in a reference to the char* pointer, I get this error, which I'm not sure how to change the function parameters to fix: error: cannot convert char* ()[10] to char for argument 1 to void myFunction(char*, String). Can anybody please take a stab at showing me a working example?
EDIT:
Thanks to the responses... I now have a working static library function that splits strings passed as a char* array. I know it's not pretty, but it does work. Thanks you to those who contributed. Code below:
void ExplodeString::explode(char* explodeResults[], String str, String delimiter) {
int delimiterPosition;
int explodeResultsCounter=0;
String subString;
do {
delimiterPosition = str.indexOf(delimiter);
if(delimiterPosition != -1) {
subString = str.substring(0,delimiterPosition);
char *subStringPtr[subString.length()+1];
subString.toCharArray(*subStringPtr, subString.length()+1);
explodeResults[explodeResultsCounter++] = strdup(*subStringPtr);
str = str.substring(delimiterPosition+1, str.length());
} else { // here after the last delimiter is found
if(str.length() > 0) {
subString = str;
char *subStringLastPtr[subString.length()+1];
subString.toCharArray(*subStringLastPtr, subString.length()+1);
explodeResults[explodeResultsCounter++] = strdup(*subStringLastPtr);
}
}
} while (delimiterPosition >=0);
}
Usage:
char* explodeResults[10];
ExplodeString::explode(explodeResults, String("cat:dog:chicken"), String(":"));
Serial.println(explodeResults[0]);
Serial.println(explodeResults[1]);
Serial.println(explodeResults[2]);
EDIT: Man, this is sooo much easier when you use the stdlib:
void ExplodeString::explode(std::vector<std::string> &explodeResults, std::string str, char delimiter) {
std::stringstream data(str);
std::string line;
while(std::getline(data,line,delimiter))
{
explodeResults.push_back(line);
}
}
Usage:
std::vector<std::string> commandsResult;
char delimiter[] = ",";
std::string _inputString = "my,string,to,parse";
ExplodeString::explode(commandsResult, _inputString, delimiter[0]);
How to pass an array of char*:
void myFunction(char* splitStrings[10], String stringToSetInLoop) {
// ...
char *mySplitStrings[10];
myFunction(mySplitStrings, String("Repeat Me"));
This will also work:
void myFunction(char* splitStrings[], String stringToSetInLoop) {
and this:
void myFunction(char** splitStrings, String stringToSetInLoop) {
Also, seems there is STL for avr platform - include it, C++ without STL is smth strange.
You are not allocating space for character arrays and just passing pointer of character array.
Instead of using char*splitStrings[10], you can use 2d char array with sufficient space to accomodate max length string. Assuming you max string length is less that 64 you can do something like this.
char splitString[10][64];
void myFunction(char**splitStrings,String stringToSetInLoop)
or
void myFunction(char splitString[][64], String stringToSetInLoop)
{
int len = stringToSetInLoop.length();
for(int i = 0; i<10; i++)
{
for(int j = 0; i<len; j++)
{
splitString[i][j] = stringToSetInLoop.charAt(j);
}
}
}
So I'm making a function that is similar to SubStr. This is an assignment so I cannot use the actual function to do this. So far I have created a function to take a string and then get the desired substring. My problem is returning the substring. In the function when I do Substring[b] = AString[b]; the substring is empty, but if I cout from inside the function I get the desired substring. So what is wrong with my code?
Here is a working demo: http://ideone.com/4f5IpA
#include <iostream>
using namespace std;
void subsec(char AString[], char Substring[], int start, int length);
int main() {
char someString[] = "abcdefg";
char someSubString[] = "";
subsec(someString, someSubString, 1, 3);
cout << someSubString << endl;
return 0;
}
void subsec(char AString[], char Substring[], int start, int length) {
for (int b = start; b <= length; b++) {
Substring[b] = AString[b];
}
}
Maybe this does what you're looking for? It's hard to say as your initial implementation used the length parameter as more of an end position.
#include <iostream>
using namespace std;
void subsec(char AString[], char Substring[], int start, int length)
{
const int end = start + length;
int pos = 0;
for(int b = start; b < end; ++b)
{
Substring[pos++] = AString[b];
}
Substring[pos] = 0;
}
int main()
{
char someString[50] = "abcdefghijklmnopqrstuvwxyz";
char someSubString[50];
subsec(someString, someSubString, 13, 10);
cout << someSubString << endl;
return 0;
}
There are several problems with the code:
1) The char arraysomeSubString has size 1 which cannot hold the substring.
2) The subsec is not correctly implemented, you should copy to the Substring from index 0.
Also remember to add \0 at the end of the substring.
void subsec(char AString[], char *Substring, int start, int length) {
int ii = 0;
for (int jj = start; jj <= length; jj++, ii++) {
Substring[ii] = AString[jj];
}
Substring[ii] = '\0';
}
You need to allocate more than 1 byte for someSubString i.e.
char someSubString[] = "xxxxxxxxxxxxxxxxxx";
or just
char someSubString[100];
if you know the max size you'll ever need.
Either would allocate enough space for the string you're copying to it. Then, you're not doing anything about the terminating 0 either. At the end of a C-style string there needs to be a terminating null to signify end of string. Otherwise cout will print something like;
abcdefgxxxxxxx
if you initialized with x's as I indicated.
There are a few problems with your code as it stands. Firstly, as your compiler is no doubt warning you, in C++ a string literal has type const char[], not just char[].
Secondly, you need to have enough space to store your substring. A good way to do this is for your function to allocate the space it needs, and then pass back a pointer to this memory. This is the way things are typically done in C code. The only thing is that you have to remember to delete the allocated array when you're done with it. (There are other, better ways to do this in C++, with things like smart pointers and wrapper objects, but those come later :-) ).
Thirdly, you'll have a problem if you request a length which is actually longer than the passed-in string -- you'll run off the end and start copying random memory (or just crash), which is definitely not what you want. C strings are terminated with a "nul byte" -- so you need to check whether you've come across this.
Speaking of the nul, you need to make sure that your substring ends with one.
Lastly, it's not really a problem but there's no need for the start parameter, you can just pass a pointer to the middle of the array if you want to.
char* substring(const char* str, int length)
{
// Allocate memory for substring;
char* subs = new char[length+1];
// Copy characters from given string
int i = 0;
while (i < length && str[i] != '\0') {
subs[i] = str[i];
i++;
}
// Append the nul byte
subs[i] = '\0';
return subs;
}
int main()
{
const char someString[] = "foobarbaz"; // Note -- must be const in C++
char* subs = substring(someString + 3, 3);
assert(strcmp(subs, "bar") == 0);
delete subs;
}