How to stop looping through array when index is empty? - c++

If I have an array of size MAX_SIZE and only have 20 index occupied how do you make it so that it stops printing 0s after itemList[20]? (I am reading in from a text file)
const int MAX_SIZE = 1000;
item itemList[MAX_SIZE];
for(int i= 0; i<MAX_SIZE;i++)
{
itemList[i].Print(); //prints members in item
if(i==19) // I used this just to see what I was printing properly
{ //I know it is bad practice so I would like an alternative.
break; //Also, it is only possible if you have access to the text file.
}
}

You can perform a basic check:
if(itemList[i].Function() == 0) break;

As an alternative to break, you can use a while loop:
int i = 0;
while (itemList[i] != 0 && i < MAX_SIZE)
{
itemList[i].Print();
i++;
}
Replace itemList[i] != 0 with whatever expression you're using to determine whether the element is occupied or not.
Alternatively, keep track of how many elements there are as you build up the array from the file, and only loop that many times.
Better still, remember that you're using C++, not C. Add elements from the file to a container such as std::vector instead of a raw array, then just loop through the whole thing. This also fixes a serious bug in your code; namely, that you will have undefined behaviour when there are more than 1000 entries in the file.

const int MAX_SIZE = 1000;
item itemList[MAX_SIZE];
for (int i = 0; i < sizeof(aitemList / sizeof(*itemList)); i++)
{
if (itemList[i] != 0)
itemList[i].Print();
}
or use a vector
std::vector<int> itemList;
for (int i = 0; i < itemList.size(); i++)
{
if (itemList[i] != 0)
{
// do stuff
}
}

A short lesson in:
correctly iterating through a null-terminated pointer array
cleaning up arrays of pointers in an exception safe way
solving your problem correctly
.
#include <iostream>
#include <algorithm>
#include <memory>
using namespace std;
struct Oper {
void exec() const { cout << "Hello\n"; }
~Oper() { cout << "deleted\n"; }
};
int main()
{
Oper* myArray[1000];
fill(begin(myArray), end(myArray), nullptr);
// make a sentinel to ensure that the array is cleaned up if there is an exception
std::shared_ptr<void> sentinel(&myArray, [&](void*) {
// clean up array
for (auto p = begin(myArray) ; p != end(myArray) ; ++p) {
delete *p;
}
});
myArray[0] = new Oper;
myArray[1] = new Oper;
myArray[2] = new Oper;
myArray[4] = new Oper; // note: missed 3
for(auto p = begin(myArray) ; p != end(myArray) && *p ; ++p) {
(*p)->exec();
}
return 0;
}
Output:
Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1
Executing the program....
$demo
Hello
Hello
Hello
deleted
deleted
deleted
deleted

for(int i= 0; i<MAX_SIZE && i<20;i++)
it looks better because using of break is a bad pattern.

Related

Heap corruption detected in C++ after removing strings

When running this code I get an error as shown in the image below.
I've tried running it on GCC compiler and it worked fine. But when running it on Visual Studio on Windows this error appeared:
Debug Error!
Program: C:\Users\yudab\source\repos\Project2\Debug\Project2.exe
HEAP CORRUPTION DETECTED: after Normal block (#153) at 0x014FD2E0.
CRT detected that the application wrote to memory after end of heap buffer.
After some testing it seems as the error only appears after trying to delete the second word.
#include <cstring>
#include <string>
#pragma warning(disable : 4996)
#include <iostream>
using namespace std;
void delStr(char**& lexicon, int& lexSize, char word[]);
void printAll(char** lexicon, int lexSize);
void retract2dArr(char**& arr, int& size);
int main() {
char** lexicon = new char* [3];
lexicon[0] = new char[6]{ "hello" };
lexicon[1] = new char[5]{ "test" };
lexicon[2] = new char[6]{ "world" };
int size = 3;
char removeTest[5] = { "test" }; //The first word I want to remove from the list
char removeWorld[6] = { "world" }; //The second word I want to remove from the list
printAll(lexicon, size); //First prints the entire list
delStr(lexicon, size, removeTest); //Removes the first word
delStr(lexicon, size, removeWorld); //Removes the second word
printAll(lexicon, size); //Prints the list after deleting the words
return 0;
}
void delStr(char**& lexicon, int& lexSize, char word[]) {
bool toDelete = false;
for (int i = 0; i < lexSize; i++) {
if (strcmp(lexicon[i], word) == 0) {
toDelete = true;
for (; i < lexSize - 1; i++) {
strcpy(lexicon[i], lexicon[i + 1]);
}
}
}
if (toDelete == true) {
delete[] lexicon[lexSize - 1];
retract2dArr(lexicon, lexSize);
}
return;
}
void printAll(char** lexicon, int lexSize) {
for (int i = 0; i < lexSize; i++) {
cout << lexicon[i];
if (i != lexSize - 1) {
cout << " ";
}
}
cout << endl;
return;
}
void retract2dArr(char**& arr, int& size) {
size--;
char** newArr = new char* [size];
for (int i = 0; i < size; i++) {
*(newArr + i) = *(arr + i);
}
printAll(newArr, size);
delete[] arr;
arr = newArr;
return;
}
You can't strcpy one string to another
if (strcmp(lexicon[i], word) == 0) {
toDelete = true;
for (; i < lexSize - 1; i++) {
strcpy(lexicon[i], lexicon[i + 1]);
}
}
As length will be different for each strings.
Example:
lexicon[0] = new char[6]{ "hello" };
lexicon[1] = new char[5]{ "test" }; // length is 4
lexicon[2] = new char[6]{ "world" }; // length is 5
3rd string won't fit in 2nd string, it causes out of bound access.
As kiran Biradar pointed out, the strcpy is to blame here. Although instead of copying each word in the lexicon to the memory allocated for the previous word, it would probably be better to simply move the pointers back withing the lexicon array.
Try something like this for your delStr function:
void delStr(char**& lexicon, int& lexSize, char word[]) {
for (int i = 0; i < lexSize; i++) {
if (strcmp(lexicon[i], word) == 0) {
delete[] lexicon[i];
for (; i < lexSize - 1; i++) {
lexicon[i] = lexicon[i + 1];
}
retract2dArr(lexicon, lexSize);
}
}
}
P.S. You didnt need to use a toDelete flag, you could call teh retract2dArr function within the first if.

Returning Array in C++ returns unaccessable elements

I am working on a project where I parse a string in to an array and then return it back to the main function. It parses fine but when I return it to the main function I can't get access to the array elements.
//This is from the Main function. It calls commaSeparatedToArray which returns the array.
for (int i = 0; i < numberOfStudents; i++) {
string * parsedToArray = mainRoster->commaSeparatedToArray(studentData[i]);
Degree degreeType = SOFTWARE;
for (int i = 0; i < 3; i++) {
if (degreeTypeStrings[i] == parsedToArray[8])
degreeType = static_cast<Degree>(i);
}
mainRoster->add(parsedToArray[0], parsedToArray[1], parsedToArray[2], parsedToArray[3], stoi(parsedToArray[4]), stoi(parsedToArray[5]), stoi(parsedToArray[6]), stoi(parsedToArray[7]), degreeType);
}
//Here is the commaSeparatedToArray function
string * roster::commaSeparatedToArray(string rowToParse) {
int currentArraySize = 0;
const int expectedArraySize = 9;
string valueArray[expectedArraySize];
int commaIndex = 0;
string remainingString = rowToParse;
while (remainingString.find(",") != string::npos) {
currentArraySize++;
if (currentArraySize <= expectedArraySize) {
commaIndex = static_cast<int>(remainingString.find(","));
valueArray[currentArraySize - 1] = remainingString.substr(0, commaIndex);
remainingString = remainingString.substr(commaIndex + 1, remainingString.length());
}
else {
cerr << "INVALID RECORD. Record has more values then is allowed.\n";
exit(-1);
}
}
if (currentArraySize <= expectedArraySize) {
currentArraySize++;
commaIndex = static_cast<int>(remainingString.find(","));
valueArray[currentArraySize - 1] = remainingString.substr(0, commaIndex);
remainingString = remainingString.substr(commaIndex + 1, remainingString.length());
}
if (currentArraySize < valueArray->size()) {
cerr << "INVALID RECORD. Record has fewer values then is allowed.\n";
exit(-1);
}
return valueArray;
}
1) You can't return arrays in C++. Your code (as I'm sure you know) returns a pointer to an array. That's an important difference.
2) The array is declared locally in the function and therefore no longer exists after the function has exitted.
3) Therefore once you have returned from the function you have a pointer to something which no longer exists. Bad news.
4) You must always consider the lifetime of objects when you program C++. One solution to this problem is to dynamically allocate the array (using new[]). This means that the array will still exist when you exit the function. But it has the signifcant disavantage that you must remember to delete[] the array at a suitable later time.
5) The best solution (in general) is to use a std::vector. Unlike an array a std::vector can be returned from a function. So this option leads to the simplest, most natural code.
vector<string> roster::commaSeparatedToArray(string rowToParse) {
...
vector<string> valueArray(expectedArraySize);
...
return valueArray;
}
Since your array/vector is constant size, you could also use a std::array
array<string, expectedArraySize> valueArray;
To complete the answer that John has already given, I made some example code to show you, how such function could look like.
Parsing, or tokenizing can be easily done with the std::sregex_token_iterator. That is one of the purposes for this iterator. You can see the simplicity of the usage below.
In the function we define a vector af string and use its range constructor to do the whole tokenizing.
Then we make a sanity check and return the data.
Please see:
#include <string>
#include <regex>
#include <iterator>
#include <vector>
#include <algorithm>
#include <iostream>
const std::regex separator(",");
constexpr size_t ExpectedColumnSize = 9;
std::vector<std::string> commaSeparatedToArray(std::string rowToParse)
{
// Parse row into substrings
std::vector<std::string> columns{
std::sregex_token_iterator(rowToParse.begin(),rowToParse.end(),separator ,-1),
std::sregex_token_iterator() };
// Check number of columns
if (columns.size() != ExpectedColumnSize) {
std::cerr << "Error. Unexpected number of columns in record\n";
}
return columns;
}
// test code
int main()
{
// Define test data
std::string testInputData{ "1,2,3,4,5,6,7,8,9" };
// Get the result from the parser
std::vector<std::string> parsedElements{ commaSeparatedToArray(testInputData) };
// show the result on the console
std::copy(parsedElements.begin(), parsedElements.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}

Using shared_ptr to access addresses in vector to creat a linked list

Here, I am trying to make an array of nodes, which will also be linked as a linked list. I am purposely making the vector and linked list together, so as to make them contiguous, and when I pop from the vector, I can still refer to the memory.
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
struct node {
int data;
std::shared_ptr<node> next;
};
int main(){
vector<node> myArray;
for (int i = 0; i < 10; ++i) {
myArray.push_back(node());
}
for (int i = 0; i < 10; ++i) {
myArray[i].data = i;
myArray[i].next.reset(&myArray[i+1]);
}
std::shared_ptr<node> nodeList(make_shared<node>());
auto here = nodeList;
for (int i = 0; i < 10; ++i) {
here->next.reset(&myArray[i]); // the error occurs in this line
here = here->next;
}
here = nodeList;
here = here->next;
while (here != nullptr) {
cout << here->data << " with address of : " << &(*here) << endl;
}
return 0;
}
When I run this code, I get an error stating error for object 0x7fb8cbc033d0: pointer being freed was not allocated. Why is this happening? Thank you for your help in advance.
1) The pointer you assigned to myArray[i].next is not allocated by anyone, so it will barf when shared_ptr try to delete it.
2) myArray[i].next.reset(&myArray[i+1]); is dangerous, because the vector could reallocate the memory it uses, and this pointer will be invalid.
3) A linked list node doesn't have to store a pointer, you could store a next_index filed and do myArray[i].next_index = i + 1 or something like that.
4) You could also store shared_ptr in the vector if that's what you want.

How to create and increase the size of an array at runtime in C++

I want to create an array whose size I will only know at runtime, and then further increase that size during execution of the program.
This is from an /r/dailyprogrammer challenge which can be found here https://www.reddit.com/r/dailyprogrammer/comments/3twuwf/20151123_challenge_242_easy_funny_plant/
MSVisual gives me the error std::badd_array_new_length which means that it's having trouble instantiating the array?
I'm so tired with oftentimes copying code letter for letter from websites where it works and I constantly get errors. Is Visual a bad platform for learning C++? Should I try QT?
#include <iostream>
#include <string>
void main(int argc, char* argv[]) {
int currentPlants = std::stoi(argv[2]), targetPeople = std::stoi(argv[1]), currentProduce = 0, week = 0;
int * plants;
plants = new int[currentPlants];
for (int i = 0; i < currentPlants; i++) {
plants[i] = 0;
}
if (plants == nullptr) EXIT_FAILURE;
while (currentProduce < targetPeople) {
currentProduce = 0;
for (int i = 0; i < currentPlants; i++) {
currentProduce += plants[i];
plants[i]++;
}
if (currentProduce >= targetPeople) break;
else {
plants = new int[currentProduce];
for (; currentPlants < currentProduce; currentPlants++) {
plants[currentPlants] = 0;
}
}
week++;
}
std::cout << week;
}
You should use an std::vector.
As a summary :
// Create an array of size 10
std::vector<int> my_vector(10);
// Add '3' to my_vector
my_vector.push_back(3);
// Remove the last element
my_vector.pop_back();
Explanation and example here : www.cplusplus.com/reference/vector/vector/
Edit : you don't need to specify the array size when you construct your object.
// Create an array
std::vector<int> my_vector;
You can't increase the size of an array at runtime. You can create a new bigger array, and copy the contents of the old array to the new array.
The problem with your code is that on the first pass through plants all of your plants[x] are zero. You add all of these together and get zero => currentProduce == 0. You then try to new plants[currentProduce aka 0] which is illegal.
Your second problem is that each time you new you create a new array discarding the old values; new creates a new array, it doesn't know anything about the old one.
I rewrote your code using std::vector, which fixes the crash but produces an endless loop because on the first pass, currentProduce comes out to zero so the array is truncated.
#include <iostream>
#include <string>
#include <vector>
int main(int argc, const char* argv_real[])
{
const char* argv[] = { "programname", "5", "25" };
int currentPlants = std::stoi(argv[2]), targetPeople = std::stoi(argv[1]), currentProduce = 0, week = 0;
std::cout << "targetPeople = " << targetPeople
<< ", currentPlants = " << currentPlants
<< "\n";
std::vector<int> plants;
// Option 1:
// plants.resize(currentPlants);
// Option 2:
for (auto i = 0; i < currentPlants; ++i) {
plants.push_back(0);
}
while (currentProduce < targetPeople) {
std::cout << "cp: " << currentProduce
<< ", tp: " << targetPeople
<< "\n";
currentProduce = 0;
// plantCount is a reference to plants[i] for each i
for (auto& plantCount : plants) {
std::cout << plantCount << ", ";
currentProduce += plantCount;
plantCount++;
}
std::cout << " cp: " << currentProduce << "\n";
if (currentProduce >= targetPeople)
break;
// Option 1:
plants.resize(currentProduce);
// Option 2:
// while (currentPlants < currentProduce) {
// plants.push_back(0);
// }
week++;
}
std::cout << week;
}
Live demo: http://ideone.com/xGpoF6
Outside of using std::vector, you would need to allocate a new array on the heap, copy the contents over, and delete the old one. Then point your int* to the newly allocated array.
This wouldn't technically change the array size, but those accessing the object would see it as though it was changing.
This is dangerous:
int * plants;
plants = new int[currentPlants];
for (int i = 0; i < currentPlants; i++) {
plants[i] = 0;
}
if (plants == nullptr) EXIT_FAILURE;
This is what happens (if you are lucky):
the program attempts to create some memory and returns nullptr if it can't
the program then uses the memory in a loop even if nullptr was returned. (If nullptr was returned this will crash the program, silently corrupt the memory so you get the wrong results or otherwise doing something you don't want)
the program then checks to see if nullptr was returned.
If you are unlucky the compiler does time travel and destroys the entire universe. I am not kidding, have a look at:
https://blogs.msdn.microsoft.com/oldnewthing/20140627-00/?p=633
Undefined behavior causing time travel

Find char* element in array of char* in C++

I'm trying to write function that search for char * element in array of char* and the function start check this element, if the element exist in the array I will have "found", if not it should be "inserted" and the element added to the array.
I wrote this code but I cannot know how to try it, the program always gives me exception, what can I do to check the element in my pointer array?
void checkFunction(char*myArray[], char *element,bool flag)
{
for (int i = 0; i < strlen(*myArray) ; ++i)
{
if (myArray[i] == element)
{
flag = true;
}
}
*myArray = element;
flag = false;
if (flag)
{
cout << "Found" << endl;
}
else
{
cout << "Inserted" << endl;
}
}
C++ Way
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(int argc, const char * argv[]) {
vector<string> myStrings { "One", "Two", "Three" };
// std::find() finds the first element that matches a value
auto it = find(begin(myStrings), end(myStrings), "Twooo");
if (it != end(myStrings)) {
cout << "We found this string; do something..." << endl;
}
}
Few remarks regarding your function:
1.Why do you need the third parameter bool flag, instead of having it as local variable?
2.If you want to expand an array you should copy the old to a newly allocated and then add the new element, you can not just do: *myArray = element;
3.If you want to iterate through the array length/ size, instead of:
for (int i = 0; i < strlen(*myArray) ; ++i)
pass an additional parameter to your function, that indicates the number of elements in the array.
With std::string and std::vector you could do something like:
void check_insert (std::vector<std::string>& v, std::string& c) {
for (auto i = 0; i < v.size(); ++i) {
if (v[i] == c) {
std::cout << "Found!\n";
return;
}
}
v.push_back(c);
std::cout << "Inserted!\n";
}