I have this piece of code:
while(fileStream>>help)
{
MyVector.push_back(help);
}
...and lets say that in file "fileStream" is 1 sentence: Today is a sunny day. Now when i do this:
for(int i=0;i<MyVector.size();i++)
{
printf("%s\n", MyVector[i]);
}
, the resoult is "day day day day day". And, of course, it shouldnt be like that. Variables are declared like this:
char *help;
vector<char*> MyVector;
EDIT:i understand the answers, thank you...but is there any way to store words from file in vector<char*> MyVector(like i wanted in the first place). it would be great for the rest of my program.
When you do fileStream>>help, that's not changing the value of the pointer, it is overwriting the contents of the string that the pointer is pointing at. So you are pushing the same pointer into the vector over and over again. So all of the pointers in the vector point to the same string. And since the last thing written into that string was the word "day", that is what you get when you print out each element of your vector.
Use a vector of std::string instead:
string help;
vector<string> MyVector;
If, as you say, you must stick with vector<char*>, then you will have to dynamically allocate a new string for each element. You cannot safely use a char* with operator>>, because there is no way to tell it how much space you actually have in your string, so I will use std::string for at least the input.
std::string help;
vector<char*> MyVector;
while (fileStream>>help) {
char * p = new char[help.size() + 1];
strcpy(p, help.c_str());
MyVector.push_back(p);
}
Of course, when you are done with the vector, you can't just let it go out of scope. You need to delete every element manually in a loop. However, this is still not completely safe, because your memory allocation could throw an exception, causing whatever strings you have already allocated and put in the vector to leak. So you should really wrap this all up in a try block and be ready to catch std::bad_alloc.
This is all a lot of hassle. If you could explain why you think you need to use vector<char*>, I bet someone could show you why you don't.
Thats because your char * are all pointing at the same object. The help pointer and the pointers stored in the vector all point to the same object. Everytime you read from the stream it modifies what all the pointers are pointing at. Change char* to an std::string.
Related
I need to pull text line by line out of my .txt file and store it into a dynamic array that has new space allocated every time I pull a new line out of the .txt file. My code seems to pull out the first line just fine and store it into the first pointers array, but on the second loop, it seems to reset all the pointers arrays which gives me memory allocation errors when I later try to access it. Why does this happen especially when I don't touch the pointers and their arrays after I store stuff into them?
char** temp = nullptr;
char buffer[256];
int index = 0;
// Open File
fstream myFile;
myFile.open("pantry.txt", ios::in);
if (myFile.is_open())
{
while (!myFile.eof())
{
myFile >> buffer; // Pull line out of txt.file
temp = new char* [index + 1]; // Create new pointer
temp[index] = new char[strlen(buffer)+1]; // Create char array pointed at by new pointer
#pragma warning(suppress : 4996) // Turns off complier warning
strcpy(temp[index], buffer); //Copy buffer into new char array
index++; // Increment our index counter int
}
for (int i = 0; i < index; i++)
{
cout << temp[i] << endl;
}
If allocated and stored correctly I want it to just print out the txt file exactly.
Instead, I get
Exception thrown at 0x7B9A08CC (ucrtbased.dll) in PE 12.4.exe: 0xC0000005: Access violation reading location 0xCDCDCDCD.
pantry.txt
Basil
Flat Leaf Parsely
Thyme
Sage
Cumin
Steak Seasoning
Mace
Garlic Powder
There are multiple bugs in the shown code.
while (!myFile.eof())
This is always a bug that also must be fixed, in addition to the main problem with the shown code:
temp = new char* [index + 1];
To help you understand the problem with this line, it's helpful to remember The Golden Rule Of Computer Programming:
Your computer always does exactly what you tell it to do instead of
what you want it to do.
According to the Golden Rule, the above line tells your computer, exactly: "new something, and assign it to temp".
And this is what your computer will do every time it executes this line. This line is executed once, on each iteration of this loop. The next time this loop runs, the previously newed temp will be replaced by another one, leaking everything that it pointed to before. Why should your computer do anything else, on this line? After all, this is exactly what you told your computer to do. And this is why you observed that this will "reset all the pointers arrays" on every iteration of the loop, resulting in "memory allocation errors".
In any case, this entire chunk of logic needs to be scrapped and rewritten from scratch, this time using the right logic. The simplest thing to do is to actually use the C++ library's std::vector and std::string objects, which will do all the memory allocation for you, correctly. Modern C++ code rarely needs to new anything, and will use C++ library's containers, instead.
It is possible that the goal of your assignment is to demonstrate proper use of low-level memory allocation and deallocation logic. In this case you will need to find some other way of doing this. Since you don't know the number of lines in advance, one approach will be to build a linked list, one line at a time, as each line gets read from the file. The final array, with all the character pointers gets allocated only after the entire file is read (and the number of lines is known), the pointers moved to the array, and the temporary linked list deleted. Or, perhaps implement a std::vector-like algorithm that progressively allocates a new pointer array, when it is full, copies over all the character pointers to a bigger array and then deletes the original one.
Of course, that's a lot of work. But unless the purpose of your assignment or task is to correctly implement low-level memory allocations and deallocation, why go through all the work and pain to do what std::vector and std::string already do, when you can simply use them, in just five or six lines of code, that will replace all of the above?
I'm trying to store this string into a vector of pointers. This is just the starting point of the code. Eventually the vector will store words the user types in and either add it to the vector if the vector doesn't have it or show the word is already in the vector.
I tried to use strcpy() but then it told me to use strcpy_s(). So I did now and now it just crashes every time with no error. Can someone give me some insight into what is going on here.
vector<char*> wordBank;
string str;
cin >> str;
wordBank[0] = new char[str.length() + 1];
strcpy_s(wordBank[0], str.length() + 1 , str.c_str() );
cout << wordBank[0];
delete[] wordBank[0];
I would not consider vector<char*> wordBank; this c++ code, but rather C code that happens to use some C++ features.
The standard library in C++ can make your life easier. You should use std::vector<std::string> instead.
vector<string> wordBank;
wordBank.push_back(str);
It avoid all the pointer stuff, so you need not to do memory management (which is a good reason get rid of the pointers).
For strcpy_s, it's a safer version of strcpy, because you have to explicitly specify the size of the target buffer, which can avoid buffer overflows during copies.
However, strcpy_s is non-standard and MS specific, NEVER use strcpy_s unless your just want your code compiled on MSVS. Use std::copy instead.
The default size of a vector is 0
Hence this line
vector<char*> wordBank;
just defines a 0 sized vector of character pointers
As people have mentioned in comments you can go with either of these 2 options:-
vector<char*> wordBank(1);
OR
wordBank.push_back(...);
You don't need char* elements in the vector. You can use string instead, and append your strings to the vector with push_back(), which will allocate the needed space:
vector<string> wordBank;
string str;
cin >> str;
wordBank.push_back(str);
cout << wordBank[0];
This will free you from the burden of having to use delete every time you want to remove a string from the vector. Basically, you should never have to use delete on anything, and to accomplish that, you should avoid allocating memory with new. In this case, that means avoiding the use of new char[/*...*/], which in turn means you should use string to store strings.
vector<char*> wordBank; constructs an empty vector. As such, wordBank[0], which uses operator[], has undefined behavior since you're accessing out of bounds.
Returns a reference to the element at specified location pos. No bounds checking is performed.
You can use push_back to add a new element, as such:
wordBank.push_back(new char[str.length + 1]);
Of course the most sensible thing is to just use use a vector of strings
vector<string> wordBank;
wordBank.push_back(str);
You're trying to manually manage memory for your strings, while the std::string class was designed to do that for you.
Also, from what you are describing your use case to be, you may wish to check out std::map and/or std::set. Here's a tutorial.
I was tinkering with Boost Property Tree a bit, and I came across this example. I needed to convert the final result to vector, so I just added one more line after
write_json(ss, root);
like this:
std::vector<char> testVec(std::begin(ss.str()), std::end(ss.str()));
I have also tried this instead:
std::string someString = ss.str()
char* someArray = (char*)someString.c_str();
std::vector<char> someVec(someArray, someArray+sizeof(someArray));
In both the cases, I am getting this error:
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Any hints what I am doing wrong? It is not a problem of property tree, I gather. It is a problem of string to vector conversion. I guess this makes sense, because vectors are only supposed to be one dimensional, but I have no clue how to convert that string to vector and get it back again.
This fails:
std::vector<char> testVec(std::begin(ss.str()), std::end(ss.str()));
because the std::begin() and std::end() are not associated with same std::string instance. The ss.str() returns a new std::string instance each time, it does not return a std::string& to some internal member.
This is incorrect:
std::vector<char> someVec(someArray, someArray+sizeof(someArray));
because sizeof(someArray) is sizeof(char*). If there is less than sizeof(char*) elements in someArray an attempt will be made to read memory that should not.
A possible solution:
std::string someString = ss.str();
std::vector<char> someVec(someString.begin(), someString.end());
sizeof() isn't giving you the length of the array, because c strings don't know their length (null-terminated). It's just providing the size of the memory location for the pointer at the beginning of the array, which is why you can reference past the end of the string using operator[] if you're not careful.
Another solution is to overload sizeof to iterate through the string until it hits the null terminator, but that seems like something you could do with built in things without going through the extra work.
I have been trying to return an array of strings for a function for a couple of days to no avail. While I was searching around StackOverflow, I found that it would be a better idea to have a parameter that will be assigned the value of an array. So, here is my code example (not the actual usage, but a mockup of how I am trying to use the function). I am sorry if the code is a bit sloppy. I have been testing things out with it for a while.
void splitOn(string message, string delim, string***toCh) {
string** rString = new string*;
string lArr[numberOf(message, delim)+1];
for(int index=0; index<numberOf(message, delim)+2; index++) {
lArr[index]=message.substr(0, message.find(delim)).c_str();
message = message.substr(message.find(delim)+1, message.length());
rString[index]=&lArr[index];
cout << "IN LOOP "<<*rString[index]<<endl;
}
rString[numberOf(message, string(delim))] = &message;
toCh=&rString;
}
int main(){
string***arr;
splitOn("fox.over.lazy.dog", ".", arr);
cout << **arr[0]<<endl;
Note:
numberOf() takes a string and a delimiter(string) and returns how many times the delimiter is found within the string.
strings are from std::string
lArr (the local array within the loop) and *rString all give correct output.
Although I am trying to assign the array to a parameter, learning how to return an array is more appealing to me.
I could hack this together with a file and getLine(), but I would prefer to learn how to properly do this.
You're trying to return local variables, which will never work. You and your caller need to agree on how to allocate the return value. In C++ as the commenters mention this would normally be done by passing a reference to a vector to handle your allocation for you.
In C you have two options, you can either get the caller to pass in a big enough allocation, or use multiple calls to malloc in the callee (not forgetting the calls to free in the caller!)
For instance, if you pass a writable character array, you can simply overwrite the separator characters with null characters to split it up into individual strings without having to allocate new copies.
I'm writing a C++ program, that stores strings in a string array, when the array is full I resize the array to make space for more items using the code below. But sometimes (not always) it crashes at the "delete[] temp;" line and I don't know why and how to fix it. Please, help.
I have searched a lot but could not find an answer anywhere. When I debug it says "invalid pointer" but how can it be invalid when I stored data there before and did not free it yet?
This is my code:
if(item_cnt >= (arr_size - 1))
{
int oldsize = arr_size;
string * temp;
arr_size *= 2;
temp = arr;
arr = new string [arr_size];
memcpy(arr, temp, oldsize * sizeof(temp));
delete[] temp;
}
Unless you absolutely have to stick with your current approach, I would recommend using a vector to hold your strings. It will manage all the memory for you.
Here's an example:
#include <vector>
#include <string>
int main()
{
std::vector<std::string> arrayOfStrings;
arrayOfStrings.push_back("Hello World!"); // To Add Items
string value = arrayOfString.at(<some index>); // To Retrieve an Item you can also use the [] operator instead of the at method
return 0;
}
The memcpy is at the root of your problem. everyone's said "don't use it", but let me explain exactly why it's a terminally bad idea.
First off, what is a c++ string, and how does it do its magic? It's basically a variable-length array of characters, and it achieves this feat by holding a pointer within each string object that points to the memory allocated to hold those characters. As the string grows or shrinks, that memory gets reallocated. Copy strings properly involves making a 'deep copy' of the contents.
Now, to your code:
arr = new string [arr_size];
This creates an array of empty string objects. Because they're empty, the internal pointers are typically null.
memcpy(arr, temp, oldsize * sizeof(temp));
Here, bad things happen. This isn't actually creating copies of the original strings, it's just overwriting the internal representation. So both the old and the new strings 'point' to the same character data. Now to really screw things up, this happens:
delete[] temp;
We delete thew old strings, but this also frees up the character memory that they were using. So our 'new' copies of these strings are pointing at memory that's actually been freed. We now have a car-crash waiting to happen : The character data could be re-used for anything, and when we try and delete the strings again, the operating system will hopefully spot that you're trying to free memory that hasn't been allocated.
Your array should be really
vector<string>
This is a recommended way of implementing arrays of dynamic size. By using vector you avoid necessity to reallocate/copy stuff manually and avoid problems like the one you have altogether.
Mixing old style and new style memory operations is always a bad idea... here you use memcpy and new/ delete. be aware that delete[] also calls the dtor for each element of the array...
Edit:
ctor --> dtor
hth
Mario