Stack implementation using Array (ways to improve code) - c++

Need to implement stack using array only, methods: push, pop, print.
The task itself:
Implement stack using only array. The only time compiler should allocate memory is through set_size function.
The current code version works good enough, but I'm looking for ways to improve it's exec-time / complexity / readability etc. Any ideas?
Thank you in advance.
#include <vector>
#include <string>
template <class T>
class Stack
{
int size = 0;
T* Array;
int top = 0;
public:
Stack(size_t Size);
~Stack()
{
delete[] Array;
}
void push(T element);
void pop();
void print();
};
template <class T>
Stack<T>::Stack(size_t Size)
{
size = Size;
top = -1;
Array = new T[size];
}
template <class T>
void Stack<T>::push(T element)
{
if (top >= (size - 1))
{
std::cout << "overflow" << std::endl;
}
else
{
Array[++top] = element;
}
}
template <class T>
void Stack<T>::pop()
{
if (top < 0)
{
std::cout << "underflow" << std::endl;
}
else
{
std::cout << Array[top--] << std::endl;
}
}
template <class T>
void Stack<T>::print()
{
if (top == -1)
{
std::cout << "empty" << std::endl;
}
int i = top;
while (i > -1)
{
std::cout << Array[i--] << " ";
}
std::cout << std::endl;
}
template <class T>
Stack<T> set_size(int Size)
{
return Stack<T>(Size);
}
int main()
{
auto stack = set_size<std::string>(5);
stack.push("hello");
stack.push("hi");
stack.push("hey");
stack.push("greetings");
stack.push("welcome");
stack.print();
stack.pop();
stack.pop();
stack.print();
return 0;
}```

Your main problem comes from the type conversion between your stack pointer top to your stack size size.
top is an int, which is a signed type.
size_t is an unsigned integral type.
When testing (top >= (size - 1)), top is converted to an unsigned int and then considered as UINT_MAX instead of -1, which is always >= to any other unsigned int.
You can either use a size_t as your stack pointer, which means that you cannot use negative value, or convert (size - 1) to a signed value before comparing to top (but this last solution means that you must ensure that the size you specify as a size_t is not too big to be converted to a signed int).
Your print function has also two issues:
in your first test, you assign -1 to top instead of comparing the values
you change your top stack pointer, so that you stack is in an inconsistant state after a call to print()

Your branch predictions are possibly not optimal. You should inspect the resulting assembly to see if the prediction bets on the if rather than on else in your if...else constructs (it will probably predict the if and in this case you should put the common case in the if).
You should pass the arguments by reference and not by value. It doesn't matter in case of simple integers but if your T becomes something more complex, it will result in redundant copy upon push.

Related

Iterate over struct pointer

Given a struct pointer to the function. How can I iterate over the elements and do not get a segfault? I am now getting a segfault after printing 2 of my elements. Thanks in advance
#include <stdio.h>
#include <string>
#include <iostream>
using namespace std;
struct something{
int a;
string b;
};
void printSomething(something* xd){
while(xd){
cout<<xd->a<<" "<<xd->b<<endl;
xd++;
}
}
int main()
{
something m[2];
m[0].a = 3;
m[0].b = "xdxd";
m[1].a = 5;
m[1].b = "abcc";
printSomething(m);
return 0;
}
You'll have to pass the length of the array of struct
void printSomething(something* xd, size_t n){
//^^^^^^^^ new argument printSomething(m, 2);
size_t i = 0;
while(i < n){ // while(xd) cannot check the validity of the xd pointer
cout<<xd->a<<" "<<xd->b<<endl;
xd++;
i++;
}
}
You should better use std::vector<something> in C++
The problem is that you are assuming there is a nullptr value at the end of the array but this is not the case.
You define a something m[2], then
you take the address of the first element, pointing to m[0]
you increase it once and you obtain address to m[1], which is valid
you increase it again, adding sizeof(something) to the pointer and now you point somewhere outside the array, which leads to undefined behavior
The easiest solution is to use a data structure already ready for this, eg std::vector<something>:
std::vector<something> m;
m.emplace_back(3, "xdxd");
m.emplace_back(5, "foo");
for (const auto& element : m)
...
When you pass a pointer to the function, the function doesn't know where the array stops. After the array has decayed into a pointer to the first element in the array, the size information is lost. xd++; will eventually run out of bounds and reading out of bounds makes your program have undefined behavior.
You could take the array by reference instead:
template <size_t N>
void printSomething(const something (&xd)[N]) {
for (auto& s : xd) {
std::cout << s.a << " " << s.b << '\n';
}
}
Now xd is not a something* but a const reference to m in main and N is deduced to be 2.
If you only want to accept arrays of a certain size, you can make it like that too:
constexpr size_t number_of_somethings = 2;
void printSomething(const something (&xd)[number_of_somethings]) {
for (auto& s : xd) {
std::cout << s.a << " " << s.b << '\n';
}
}
int main() {
something m[number_of_somethings];
// ...
printSomething(m);
}
Another alternative is to pass the size information to the function:
void printSomething(const something* xd, size_t elems) {
for(size_t i = 0; i < elems; ++i) {
std::cout << xd[i].a << " " << xd[i].b << '\n';
}
}
and call it like this instead:
printSomething(m, std::size(m));
Note: I made all versions const something since you are not supposed to change the element in the `printSomething´ function.

Member variable resetting back to 0

When running through the test the count variable from the class stack1 gets reset back to 0 when using its pop function. Strangely however, during the push loop, the count increases as intended but when pop occurs, the count gets reset back to 0 and subtracts into the negatives from there. Is there something I'm forgetting?
#include <iostream>
using namespace std;
class TheStack
{
public:
TheStack();
void push(int);
int pop();
bool isEmpty();
private:
const int MaxSize = 10;
int arr[10];
int count;
};
TheStack::TheStack()
{
count = 0;
}
void TheStack::push(int userInput)
{
if (count >= MaxSize)
{
cout << "Stack is full." << endl;
}
else
{
arr[count] = userInput;
count+=1;
}
}
int TheStack::pop()
{
if (isEmpty())
{
cout << "Stack is empty." << endl;
}
else
{
int temp = arr[count];
arr[count] = NULL;
count-=1;
return temp;
}
}
bool TheStack::isEmpty()
{
if (count == 0)
{
return true;
}
else
{
return false;
}
}
int main()
{
TheStack stack1;
if (stack1.isEmpty())
{
cout << "isEmpty() works" << endl;
}
stack1.pop();
for (int i = 0; i < 10; i++)
{
stack1.push(i);
}
stack1.push(0);
stack1.pop();
stack1.pop();
stack1.pop();
stack1.pop();
system("pause");
}
When you do push you first save the data into the array and then increment count. This means that in order to properly do pop you need to work in reverse: first decrement count and only then read data from the array.
But in the code you are doing it backwards. When the stack is full, count is at max value (10 in your case), and your arr[count] = NULL; writes beyond the array boundary. This causes undefined behavior and, in particular, destroys your count value. (This is why it suddenly becomes 0.)
Also:
arr[count] = NULL; makes no sense. NULL is supposed to be used in pointer contexts, not in integer contexts. This is not even guaranteed to compile.
What is the point of that anyway? Initially your array contains garbage above the current top of the stack. Why do you suddenly care to clean it up after doing pop?
Not all control paths of pop() return value. This is undefined behavior in itself.
const int MaxSize = 10; in the class definition is a C++11 feature. Since you are already using C++11, you can do the same for count. Just do int count = 0; right inside the class definition and you will not have to write the constructor explicitly.
Although in your implementation MaxSize would make more sense as a static const class member. In that case you'll also be able to declare your array as int arr[MaxSize];.
You must first decrease count and then access arr[count] in int TheStack::pop(). Now you get access above the last pushed element, event out of bound of array if the stack is full.

Generic variable to store varying levels of pointers

I want to write a generic centralized null-checker API for pointer type objects. I want to populate a list of my input pointer objects and call the null-checker API to scan through the list and validate if any pointer object is having NULL value. For this, I need to store varying levels of pointers in a single generic variable. How can I do that in C++ ?
template<typename type>
bool check(type& t)
{
return false;
}
template<typename type>
bool check(type* t)
{
return true;
}
int main()
{
char** doubleCharPointer = NULL;
char* charPointer = "charPointer";
doubleCharPointer = &charPointer;
char** temp = doubleCharPointer;
char* temp1 = NULL;
int count = 0;
if(!check(temp))
{
cout << "\nNot a pointer" << endl;
return 0;
}
temp1 = *temp;
count++;
if(!check(temp1))
{
cout << "\nPointer level : " << count << endl;
return 0;
}
count++;
cout << "\nPointer level : " << count << endl;
}
This is my null-checker prototype. It is very static at the moment. But I wanted to extend it to support any level of pointer checking. For that I need "temp" to be able to hold any level of pointer so that I can run an infinite while loop till all the levels of the input pointer is consumed and validated. How can I do that ? Please help.
I don't believe that there is a direct solution to this without knowing the exact numbers of levels. Let's say that you can advance level by level checking every level if it's NULL or not, however the main problem is that you don't know how far you must search, so in the end if you just go from address to address you may end up finding a NULL pointer or never ending your verification loop because you don't have a stop condition.
By doing this you may also access an unallocated memory address which may throw or not an exception, so you can't either use this thrown exception as a stop condition.
I think you want
template<typename T>
bool check_ptr_not_null(const T&)
{
return true; // not a pointer
}
template<typename T>
bool check_ptr_not_null(T* t)
{
return (t != nullptr) && check_ptr_not_null(*t);
}
Live Demo
Disclaimer: Do not up-vote this answer (dirty hack)
Expanding on A-B's answer, this is the best I can get to at the moment:
template<typename T>
bool notnull(T const *t, int level) {
while (level-- > 1) {
if (t == NULL)
return false;
t = (T const *)*t;
}
return t != NULL;
}
Note that this will induce some compiler warnings because of the line t = (T const *)*t so I guess this is unsafe. A cleaner way of doing this is just define some macros instead:
#define NOTNULL_P1(x) (x != NULL)
#define NOTNULL_P2(x) ((x != NULL) && NOTNULL_P1(*x))
#define NOTNULL_P3(x) ((x != NULL) && NOTNULL_P2(*x))
#define NOTNULL_P4(x) ((x != NULL) && NOTNULL_P3(*x))
#define NOTNULL_P5(x) ((x != NULL) && NOTNULL_P4(*x))

C++ code improvement, array out of bounds

This is a class template for an Array. I overloaded the [ ] operator in hopes it would fix the "out of bounds" issue. The print outs work well, except if it falls out of range, the compiler enables the range by default and it displays a 6 digit number.
Perhaps looking for a better way to initialize the arrays with the appropriate element number for a better check and if it does fall out of range when looking up the element, display an error.
// implement the class myArray that solves the array index
// "out of bounds" problem.
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
template <class T>
class myArray
{
private:
T* array;
int begin;
int end;
int size;
public:
myArray(int);
myArray(int, int);
~myArray() { };
void printResults();
// attempting to overload the [ ] operator to find correct elements.
int operator[] (int position)
{if (position < 0)
return array[position + abs(begin)];
else
return array[position - begin];
}
};
template <class T>
myArray<T>::myArray(int newSize)
{
size = newSize;
end = newSize-1;
begin = 0;
array = new T[size] {0};
}
template <class T>
myArray<T>::myArray(int newBegin, int newEnd)
{
begin = newBegin;
end = newEnd;
size = ((end - begin)+1);
array = new T[size] {0};
}
// used for checking purposes.
template <class T>
void myArray<T>::printResults()
{
cout << "Your Array is " << size << " elements long" << endl;
cout << "It begins at element " << begin << ", and ends at element " << end << endl;
cout << endl;
}
int main()
{
int begin;
int end;
myArray<int> list(5);
myArray<int> myList(2, 13);
myArray<int> yourList(-5, 9);
list.printResults();
myList.printResults();
yourList.printResults();
cout << list[0] << endl;
cout << myList[2] << endl;
cout << yourList[9] << endl;
return 0;
}
First of all, your operator[] is not correct. It is defined to always return int. You will get compile-time error as soon as you instantiate array of something, that is not implicitly convertible to int.
It should rather be:
T& operator[] (int position)
{
//...
}
and, of course:
const T& operator[] (int position) const
{
//you may want to also access arrays declared as const, don't you?
}
Now:
I overloaded the [ ] operator in hopes it would fix the "out of bounds" issue.
You didn't fix anything. You only allowed clients of your array to define custom boundaries, nothing more. Consider:
myArray<int> yourList(-5, 9);
yourList[88] = 0;
Does your code check for out-of-bounds cases like this one? No.
You should do it:
int operator[] (int position)
{
if((position < begin) || (position > end)) //invalid position
throw std::out_of_range("Invalid position!");
//Ok, now safely return desired element
}
Note, that throwing exception is usually the best solution in such case. Quote from std::out_of_range doc:
It is a standard exception that can be thrown by programs. Some components of the standard library, such as vector, deque, string and bitset also throw exceptions of this type to signal arguments out of range.
An better option to redefining an array class is to use the containers from the std library. Vector and array(supported by c++11). They both have an overloaded operator [] so you can access the data. But adding elements using the push_back(for vector) method and using the at method to access them eliminates the chance or getting out of range errors, because the at method performs a check and push_back resizes the vector if needed.

I can't figure out how to fill an array

I am trying to fill a given array with a passed in value so if I wanted an array to be all 12's
it would simply replace all the elements with 12s. The prototype I have for this function looks like this:
void fill(T *left, T *end, T fill)
The driver for this function looks like this:
static void TestFill1(void)
{
cout << "***** Fill1 *****" << endl;
int i1[10];
int size = 10;
fill(i1, i1 + size, 12);
display(i1, i1 + size);
}
I am having a problem where I am given an array that is uninitialized. Previously in the assignment I was going through the array until the end. In this case I am given an uninitialized array which makes my T *end the same as T *left. I'm not familiar with a way to go through the passed in array.
I was trying something that looked like this:
template <typename T>
void fill(T *left, T *end, T fill)
{
int i = sizeof(*left) / sizeof(*(left + 0));
while(*(left + i) != *end)
{
*(left + i) = fill;
++i;
}
}
I'm not allowed to use subscripts or for loops for this assignment also, #include is off limits same with std::vector.
The variable i, which represents the offset with respect to the first element, should start at zero:
int i = 0;
The loop condition is checking whether the value of the array element is equal to the value of the array element at the end.
while(*(left + i) != *end)
The correct version is the following:
while(left + i != end)
which checks if the pointer (left + i) has reached the end.
Your statement
int i = sizeof(*left) / sizeof(*(left + 0));
might not do, what you think it does.
The sizeof() function doesn't work on plain pointers the same way as for array declarations:
size_t s = sizeof(*left); // Will evaluate to sizeof(T)
while
int i1[10];
size_t s = sizeof(i1); // Will evaluate to sizeof(int) * 10
Your code can be simply fixed as follows:
template <typename T>
void fill(T *left, T *end, T fill) {
T* cur = left;
while(cur < end) {
*cur = fill;
++cur;
}
}
http://www.cplusplus.com/reference/vector/vector/assign/
#include <vector>
int main ()
{
std::vector<int> first;
first.assign (10,12); // 10 ints with a value of 12
return 0;
}
This is how real men do it ™. lol sorry I couldn't resist.