C/C++ Remove Item From Struct Array - c++

struct Student
{
char* name;
int balls;
};
void inputdata(Student **s, int *n)
{
int nn;
printf("%s\n", "Input amount of students");
scanf("%i", &nn);
Student* a = new Student[nn];
for (int i = 0; i < nn; ++i)
{
a[i].name = new char[4096];
scanf("%4095s", a[i].name);
scanf("%i", &a[i].balls);
}
*n = nn;
*s = a;
}
void print(Student *s, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%s %i\n", s[i].name, s[i].balls);
}
}
void fixdata(Student *s, int *n)
{
int nn = *n;
for (int i = 0; i < nn; ++i)
{
if (s[i].balls > 100)
s[i].balls = 100;
else if (s[i].balls < 20)
{
for(int j = i; j < nn; ++j)
s[j] = s[j+1];
nn-=1;
}
}
*n = nn;
}
int main(int argc, char const *argv[])
{
Student* s;
int n;
inputdata(&s, &n);
print(s, n);
fixdata(s, &n);
print(s, n);
return 0;
}
I am trying to delete items where balls is less than 20. If so, I am supposed to shift items to the right, but at the same time remove items that are less than 20. I try, but it drastically shifts left 2 of the same records and is not doing the job properly.
UPDATE: Ok, I cleaned up the code a bit, now n decreases, but the problem when it stays at 1 record, that is supposed to be deleted too. I wonder why it does not get removed. Thankfully most of the trouble is fixed. Still, what is the problem deleting one item left when it is less than 20?

The function has a bug because after moving elements of a sub-array to the left in the next iteration of the for loop you will deal with another element instead of the required. Also the function should return the number of actual elements after removing some elements.
Also the function can produce memory leaks then one element of the array is assigned to other element of the array.
The function can look the following way
int fixdata( Student *s, int n )
{
int i = 0;
for ( int j = 0; j < n; ++j )
{
if ( not ( s[j].balls < 20 ) )
{
if ( i != j )
{
delete [] s[i].name;
s[i] = s[j];
s[j].name = nullptr;
}
if ( s[i].balls > 100 ) s[i].balls = 100;
++i;
}
}
return i;
}
And in main you can write
int m = fixdata(s, n);
print(s, m);
Also you should delete the allocated memory when the array is not needed any more.
for ( int i = 0; i < n; i++ )
{
delete [] s[i].name;
}
delete [] s;

In fixdata(), when "removing" an item, you are leaking that item's name, but more importantly your inner loop goes out of bounds of the array if the "last" item is removed.
Try this instead:
void fixdata(Student *s, int *n)
{
int nn = *n;
for (int i = 0; i < nn; ++i)
{
if (s[i].balls > 100)
{
s[i].balls = 100;
}
else if (s[i].balls < 20)
{
for(int j = i + 1; j < nn; ++j)
{
delete[] s[j-1].name;
s[j-1] = s[j];
s[j] = Student{};
}
--nn;
}
}
*n = nn;
}
That being said, your code is more C than C++. The C++ approach would be to use things like std::cin, std::string, and std::vector instead, eg:
#include <iostream>
#include <vector>
#include <string>
struct Student
{
std::string name;
int balls;
};
std::istream& operator>>(std::istream &in, Student &stud)
{
in >> s.name >> s.balls;
return in;
}
std::ostream& operator<<(std::ostream &out, const Student &stud)
{
out << stud.name << " " << stud.balls;
return out;
}
void inputdata(std:vector<Student> &s)
{
int nn;
std::cout << "Input amount of students\n";
std::cin >> nn;
std::vector<Student> a(nn);
for (auto &stud : s) {
std::cin >> stud;
}
s = std:move(a);
}
void print(const std::vector<Student> &s)
{
for (const auto &stud : s) {
std::cout << stud << "\n";
}
}
void fixdata(std::vector<Student> &s)
{
for (size_t i = 0; i < s.size();)
{
auto &stud = s[i];
if (stud.balls < 20) {
s.erase(s.begin()+i);
}
else {
if (stud.balls > 100) {
stud.balls = 100;
}
++i;
}
}
/* alternatively:
s.erase(
std::remove_if(s.begin(), s.end(),
[](const auto &stud){ return (stud.balls < 20); }
),
s.end()
);
std::for_each(s.begin(), s.end(),
[](auto &stud){ stud.balls = std::min(stud.balls, 100); }
);
*/
}
int main()
{
std::vector<Student> s;
inputdata(s);
print(s);
fixdata(s);
print(s);
return 0;
}

Related

can't figure out where the heap overwriting is

#include<iostream>
using namespace std;
class Text{
public:
~Text(){
delete data;
}
char* data{};
int mSize{};
void fill(char* stringInput) {
mSize = strlen(stringInput);
data = new char [mSize];
for (int i = 0; i < mSize; i++){
data[i] = stringInput[i];
}
}
};
class myString{
public:
explicit myString(int size){ // constructor
strAmount = size;
strings = new Text [size];
}
~myString(){ // destructor
delete[] strings;
}
void addString(char* input){
strings[filledAmount].fill(input);
filledAmount++;
}
void delString(int pos){
for ( int i = pos; i < filledAmount; i++){
swap(strings[i], strings[i+1]);
}
strings[filledAmount].data = nullptr;
strings[filledAmount].mSize = 0;
filledAmount--;
}
void eraseEverything(){
for ( int i = 0; i < filledAmount; i++){
strings[i].data = {};
strings[i].mSize = 0;
}
filledAmount = 0;
}
int maxString() const {
int index{};
for ( int i = 0 ; i < filledAmount; i++){
if (strings[i].mSize > strings[index].mSize){
index = i;
}
}
return index;
}
int charAmount(){
int counter{};
for(int i = 0 ; i < filledAmount; i++){
counter+=strings[i].mSize;
}
return counter;
}
double digitPercentage(){
int digitsAmount{};
for(int i = 0; i < filledAmount; i++){
for ( int j = 0; j < strings[i].mSize; j++){
if (isdigit(strings[i].data[j])){
digitsAmount++;
}
}
}
double digitPercent = (digitsAmount/(double)charAmount())*100;
return digitPercent;
}
int filledAmount{};
int strAmount{};
Text* strings;
};
void render_text(myString& obj) {
for (int k = 0; k < obj.filledAmount; k++) {
for (int i = 0; i < obj.strings[k].mSize; i++)
cout << obj.strings[k].data[i];
cout << endl;
}
cout << endl;
}
int main(){
myString a(5);
a.addString((char *) "zxc 1v1 forever shadow fiend");
a.addString((char *) "This is a string");
a.addString((char *) "12345");
a.addString((char *) "Hello");
a.addString((char *) "A1oha Dance");
render_text(a);
a.delString(1);
render_text(a);
int maxInd = a.maxString();
cout << "Max string :\n";
for (int i = 0; i < a.strings[maxInd].mSize; i++) {
cout << a.strings[maxInd].data[i];
}
cout << "\n\n";
}
Please help me find the crash point. I suppose it crashes in the destructor pole, but I still can't figure it out.
This is something like a self-written string class, the problem is that I can't find the place where the problems start.
I also have a thought that the destructor tries to delete too much memory from the heap so the compiler prevents it from doing that. Can I somehow change the size of the strings array?
I have fixed all your memory errors, with the help of address sanitizers
The main change here is:
Add copy constructors and copy assignment operators
Manually delete the last element in the function delString
Though that the code now works, it's not a true modern c++ style code. I highly recommend that your using std::string to replace your Text class. Use std::vector to replace the dynamic array. Then you will stay away from the pain of memory errors. The delString should be replaced with vector::erase,which is much neat than your hand write algorithm.
https://en.cppreference.com/w/cpp/string/basic_string
https://en.cppreference.com/w/cpp/container/vector
I strongly recommend rewriting the code with std::string and std::vector
#include <cstring>
#include <iostream>
using namespace std;
class Text {
public:
~Text() { delete[] data; }
char* data{};
int mSize{};
Text() = default;
Text(const Text& oth) {
mSize = oth.mSize;
data = new char[mSize];
std::copy(oth.data, oth.data + oth.mSize, data);
}
Text& operator=(const Text& oth) {
delete[] data;
mSize = oth.mSize;
data = new char[mSize];
std::copy(oth.data, oth.data + oth.mSize, data);
return *this;
}
void fill(char* stringInput) {
mSize = strlen(stringInput) + 1;
data = new char[mSize];
for (int i = 0; i < mSize; i++) {
data[i] = stringInput[i];
}
}
};
class myString {
public:
explicit myString(int size) { // constructor
strAmount = size;
strings = new Text[size];
}
myString(const myString& oth) {
strAmount = oth.strAmount;
strings = new Text[oth.strAmount];
for (size_t i = 0; i < strAmount; ++i) {
strings[i] = oth.strings[i];
}
}
myString& operator=(const myString& oth) {
delete[] strings;
strAmount = oth.strAmount;
strings = new Text[oth.strAmount];
for (size_t i = 0; i < strAmount; ++i) {
strings[i] = oth.strings[i];
}
return *this;
}
~myString() { // destructor
delete[] strings;
}
void addString(char* input) {
strings[filledAmount].fill(input);
filledAmount++;
}
void delString(int pos) {
for (int i = pos; i < filledAmount; i++) {
swap(strings[i], strings[i + 1]);
}
delete[] strings[filledAmount].data;
strings[filledAmount].data = nullptr;
strings[filledAmount].mSize = 0;
filledAmount--;
}
void eraseEverything() {
for (int i = 0; i < filledAmount; i++) {
strings[i].data = {};
strings[i].mSize = 0;
}
filledAmount = 0;
}
int maxString() const {
int index{};
for (int i = 0; i < filledAmount; i++) {
if (strings[i].mSize > strings[index].mSize) {
index = i;
}
}
return index;
}
int charAmount() {
int counter{};
for (int i = 0; i < filledAmount; i++) {
counter += strings[i].mSize;
}
return counter;
}
double digitPercentage() {
int digitsAmount{};
for (int i = 0; i < filledAmount; i++) {
for (int j = 0; j < strings[i].mSize; j++) {
if (isdigit(strings[i].data[j])) {
digitsAmount++;
}
}
}
double digitPercent = (digitsAmount / (double)charAmount()) * 100;
return digitPercent;
}
int filledAmount{};
int strAmount{};
Text* strings = nullptr;
};
void render_text(myString& obj) {
for (int k = 0; k < obj.filledAmount; k++) {
for (int i = 0; i < obj.strings[k].mSize; i++)
cout << obj.strings[k].data[i];
cout << endl;
}
cout << endl;
}
int main() {
myString a(6);
a.addString((char*)"zxc 1v1 forever shadow fiend");
a.addString((char*)"This is a string");
a.addString((char*)"12345");
a.addString((char*)"Hello");
a.addString((char*)"A1oha Dance");
render_text(a);
a.delString(1);
render_text(a);
int maxInd = a.maxString();
cout << "Max string :\n";
for (int i = 0; i < a.strings[maxInd].mSize; i++) {
cout << a.strings[maxInd].data[i];
}
cout << "\n\n";
return 0;
}

delete[] array caused a breakpoint

I am trying to move a part of my code from main function to the additional void function, but I keep getting a problem with deleting allocated memory in the end. By that moment my program did not printed out my array as it should have. So i am looking for a tip how i can fix this.
#include "pch.h"
#include <iostream>
using namespace std;
void push(char* C, int size, istream &in);
void print_str(char* word, int length);
int main()
{
char* C = new char[0];
int size = 0;
cout << "input your text: ";
push(C, size, cin);
print_str(C, size);
delete[] C;
return 0;
};
void print_str(char* word, int length) {
for (int k = 0; k < length; k++)
{
cout << word[k];
}
cout << " ";
};
void push(char* C, int size, istream& in) {
while (1) {
char current = in.get();
if (current == '\n')
break;
else {
char* text1 = new char[size];
for (int i = 0; i < size; i++)
text1[i] = C[i];
delete[] C;
C = new char[size + 1];
for (int i = 0; i < size; i++)
C[i + 1] = text1[i];
delete[] text1;
C[0] = current;
}
size++;
}
}
Breakpoint
push changes the value of C, so it must return the new value so that the other code can see the change. Like this (for instance)
int main() {
...
C = push(C, size, cin);
...
}
char* push(char* C, int size, istream& in) {
while (1) {
char current = in.get();
if (current == '\n')
break;
else {
char* text1 = new char[size];
for (int i = 0; i < size; i++)
text1[i] = C[i];
delete[] C;
C = new char[size + 1];
for (int i = 0; i < size; i++)
C[i + 1] = text1[i];
delete[] text1;
C[0] = current;
}
size++;
}
return C;
}
you need to define c as pointer to pointer , because changing c in push scope won't change value of c in main
here's the changed code
#include "pch.h"
#include <iostream>
using namespace std;
void push(char** C, int size, istream& in);
void print_str(char* word, int length);
int main()
{
char* C = new char[0];
int size = 0;
cout << "input your text: ";
push(&C, size, cin);
print_str(C, size);
delete[] C;
return 0;
};
void print_str(char* word, int length) {
for (int k = 0; k < length; k++)
{
cout << word[k];
}
cout << " ";
};
void push(char** C, int size, istream& in) {
while (1) {
char current = in.get();
if (current == '\n')
break;
else {
char* text1 = new char[size];
for (int i = 0; i < size; i++)
text1[i] = (*C)[i];
delete[] C;
*C = new char[size + 1];
for (int i = 0; i < size; i++)
C[i + 1] = text1[i];
delete[] text1;
(*C)[0] = current;
}
size++;
}
}
also you can make more efficient algorithms to reach your end ,instead of allocating and deallocating memory for each character witch is costy

Code runs when in main() but gives error when in function [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I am writing a dynamic matrix class that stores each non-zero value as a List of 3 elements [row,column,value]
I made a dynamic array class called "List", and class"Matrix" a List of list pointers.
My code to transpose the Matrix works:
void transpose(Matrix tripleList)
{
for (int i = 0; i < tripleList.getNumOfElem(); i++)
{
List* list = new List;
(*list).copy(*(tripleListMatrix.getAt(i)));
int temp = (*list).getAt(0);
(*list).set(0, (*list).getAt(1));
(*list).set(1, temp);
(*list).displayList();
cout << "\n";
}
}
it works when written directly in main() but gives error when in stand alone function. can anyone explains why and how to fix it?
Full code:
#include <iostream>
using namespace std;
class List //a dynamic int pointer array
{
private:
int capacity;
int numOfElem;
int *arr;
//initialize all values in capacity to 0
void initialize(int from)
{
for (int i = from; i < capacity; i++)
{
arr[i] = 0;
}
}
//double the capaicty, then initialize
void expand()
{
capacity *= 2;
int *tempArr = new int[capacity];
for (int i = 0; i < numOfElem; i++)
tempArr[i] = arr[i];
delete[] arr;
arr = tempArr;
initialize(numOfElem);
}
public:
List()//constructor
{
capacity = 10;
numOfElem = 0;
arr = new int[capacity];
}
~List()//destrcutor
{
delete[] arr;
}
//add int to the end of List
void append(int newElement)
{
if (numOfElem >= capacity)
expand();
arr[numOfElem++] = newElement;
}
//Copy all element of an input list to the end of List
void copy(List list)
{
for (int i = 0; i < list.getNumOfElem(); i++)
{
if (numOfElem >= capacity)
expand();
arr[numOfElem++] = list.getAt(i);
}
}
//get reference of the int at an index in te list
int* getAddress(int index)
{
if (index < 0 || index >= numOfElem)
throw ("Out of bounds exception!!!");
return &arr[index];
}
//change the value of at specific index
void set(int index, int value)
{
arr[index] = value;
}
//get int at an index in te list
int getAt(int index)
{
if (index < 0 || index >= numOfElem)
throw ("Out of bounds exception!!!");
return arr[index];
}
int getNumOfElem()
{
return numOfElem;
}
void displayList()
{
for (int i = 0; i < numOfElem; i++)
{
cout << arr[i] << " ";
}
}
};
class Matrix //a List of list pointers
{
private:
int capacity;
int numOfElem;
List* *arr;
void initialize(int from)
{
for (int i = from; i < capacity; i++)
{
arr[i] = new List;
}
}
void expand()
{
capacity *= 2;
List* *tempArr = new List*[capacity];
for (int i = 0; i < numOfElem; i++)
tempArr[i] = arr[i];
delete[] arr;
arr = tempArr;
initialize(numOfElem);
}
public:
Matrix()
{
capacity = 10;
numOfElem = 0;
arr = new List*[capacity];
}
~Matrix()
{
delete[] arr;
}
void append(List* newElement)
{
if (numOfElem >= capacity)
expand();
arr[numOfElem++] = newElement;
}
void set(int index, List* value)
{
arr[index] = value;
}
List* getAt(int index)
{
if (index < 0 || index >= numOfElem)
throw ("Out of bounds exception!!!");
return arr[index];
}
int getNumOfElem()
{
return numOfElem;
}
};
void transpose(Matrix tripleList)
{
for (int i = 0; i < tripleList.getNumOfElem(); i++)
{
{
List* list = new List;
(*list).copy(*(tripleListMatrix.getAt(i)));
int temp = (*list).getAt(0);
(*list).set(0, (*list).getAt(1));
(*list).set(1, temp);
(*list).displayList();
cout << "\n";
}
}
int main()
{
int m, n, input;
cout << "Please enter the number of rows and columns of the matrix :\n";
cin >> m >> n;
Matrix tripleListMatrix;
int k = 0;
cout << "Please enter the matrix : \n";
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
cin >> input;
if (input != 0)
{
tripleListMatrix.append(new List);
(*(tripleListMatrix.getAt(k))).append(i + 1);
(*(tripleListMatrix.getAt(k))).append(j + 1);
(*(tripleListMatrix.getAt(k))).append(input);
k++;
}
}
}
cout << "The triple list of matrix is:\n";
for (int i = 0; i < tripleListMatrix.getNumOfElem(); i++)
{
(*(tripleListMatrix.getAt(i))).displayList();
cout << "\n";
}
cout << "\n\n";
//transpose(tripleListMatrix);
//the code below is the same as in the function transpose but transpose gives error
for (int i = 0; i < tripleListMatrix.getNumOfElem(); i++)
{
List* list = new List;
(*list).copy(*(tripleListMatrix.getAt(i)));
int temp = (*list).getAt(0);
(*list).set(0, (*list).getAt(1));
(*list).set(1, temp);
(*list).displayList();
//cout << "\t" << list;
cout << "\n";
}
cout << "\n\n";
//checking that tripleListMatrix is unchanged
for (int i = 0; i < tripleListMatrix.getNumOfElem(); i++)
{
(*(tripleListMatrix.getAt(i))).displayList();
cout << "\n";
}
return 0;
}
List* *arr;
When you call transpose(), it makes a copy Matrix because you're not passing by reference. That copy just has a copy of the address for your List, not it's own List object. When the destructor runs on the copy, it clears up the allocated memory, but the original Matrix object in main still points to that same memory. When that object goes away, its destructor tries to free the same memory again and that's bad.
You probably meant:
void transpose(Matrix const & tripleList)
So that no copy is made when calling transpose(), but you should also explicitly delete the copy construtor of Matrix so it cannot be called
Matrix(Matrix const &) = delete;
or make an explicit Matrix copy constructor that makes a deep copy of the memory.

run failed error while using qsort in c++

I wrote the following code for counting the frequency of names and then printing out the names in lexicographic order with their frequencies.The input here is supposed to be n number of names.So for sorting i wrote qsort function but i guess there is some error in it which i am not able to find bcoz using using other sorting method besides qsort works fine for this code.Please suggest what is the error as i have tried but found no help.below is the code snippet
#include <cstdlib>
#include<stdio.h>
#include<string.h>
struct stu
{
char name[100];
int no;
};
int cmpfunc(const void* p,const void *q){
struct stu *a = *((struct stu**)p);
struct stu *b = *((struct stu**)q);
int str=strcmp(a->name,b->name);
if(str>0) return 1;
if(str<0) return -1;
else return 0;
}
int main(int argc, char** argv) {
int n,k=0;
scanf("%d",&n);
struct stu* data[1000];
for(int i=0 ; i<n ; i++)
{
char nam[100];
scanf("%s",nam);
if(i==0)
{
struct stu* temp=(struct stu*)(malloc(sizeof(struct stu)));
strcpy(temp->name,nam);
temp->no=1;
data[k++]=temp;
}
else
{
int j;
for(j=0 ; j<k ; j++)
{
if(strcmp(data[j]->name,nam)==0)
{
data[j]->no++;
break;
}
}
if(j==k)
{
struct stu* temp=(struct stu*)(malloc(sizeof(struct stu)));
strcpy(temp->name,nam);
temp->no=1;
data[k++]=temp;
}
}
}
qsort(data, k, sizeof(struct stu*),cmpfunc);
for(int i=0 ; i<k ; i++)
{
printf("%s %d\n",data[i]->name,data[i]->no);
}
return 0;
}
if i give input as
5
abcd
abcd
fgh
fgr
fgh
the output is run failed
With the exception of <cstdio>, your code looks much more like C than C++. Perhaps you meant to use <stdio.h>, heed various warnings against casting malloc, check return values and use a C compiler, but this question is tagged C++. In C++ there are far more sensible idioms, for example:
std::string instead of char* / char[].
std::cin/std::cout instead of scanf/printf.
std::vector instead of struct stu* data[1000].
size_t instead of int for array indexes and looping over arrays.
new/delete/unique_ptr instead of malloc/free.
using namespace std;
struct stu {
string name;
int no;
stu() {
}
};
int cmpfunc(const stu *p, const stu *q) {
return p->name < q->name;
}
int main(int argc, char** argv) {
size_t n;
cin >> n;
vector<stu*> data;
for (size_t i = 0; i < n; i++)
{
string nam;
cin >> nam;
if (i == 0)
{
auto* temp = new stu();
temp->name = nam;
temp->no = 1;
data.push_back(temp);
}
else
{
int j;
for (j = 0; j < data.size(); j++)
{
if(data[j]->name == nam)
{
data[j]->no++;
break;
}
}
if (j == data.size())
{
auto* temp= new stu();
temp->name = nam;
temp->no = 1;
data.push_back(temp);
}
}
}
std::sort(data.begin(), data.end(), cmpfunc);
for (int i = 0; i < data.size(); i++)
{
cout << data[i]->name << data[i]->no << endl;
delete data[i];
}
return 0;
}

Program gives a weird runtime error

#include<iostream>
using namespace std;
class darray
{
private:
int n; // size of the array
int *a; // pointer to the 1st element
public:
darray(int size)
{
n = size;
a = new int[n];
}
~darray(){ delete[] a; }
void get_input();
int get_element(int index);
void set_element(int index, int value);
int count(){ return n; }
void print();
};
void darray::get_input()
{
for (int i = 0; i < n; i++)
{
cin >> *(a + i);
}
}
int darray::get_element(int index)
{
if (index == -1)
index = n - 1;
return a[index];
}
void darray::set_element(int index,int value)
{
a[index] = value;
}
void darray::print()
{
for (int i = 0; i < n; i++)
{
cout << a[i];
if (i < (n - 1))
cout << " ";
}
cout << endl;
}
// perform insertion sort on the array a
void insertion_sort(darray d)
{
int v = d.get_element(-1); // v is the right-most element
int e = d.count() - 1; // pos of the empty cell
// shift values greater than v to the empty cell
for (int i = (d.count() - 2); i >= 0; i--)
{
if (d.get_element(i) > v)
{
d.set_element(e,d.get_element(i));
d.print();
e = i;
}
else
{
d.set_element(e, v);
d.print();
break;
}
}
}
int main()
{
int s;
cin >> s;
darray d(s);
d.get_input();
insertion_sort(d);
system("pause");
return 0;
}
I use the darray class to make a array of size n at runtime. This class gives basic functions to handle this array.
This programs says debugging assertion failed at the end.
It gives this error after ruining the program.Other than that the program works fine. What is the reason for this error ?
You need to declare and define a copy constructor:
darray::darray(const darray& src)
{
n = src.n;
a = new int[n];
for (int i = 0; i < n; i++)
{
*(a + i) = *(src.a + i);
}
}