for Loops in C++ with Arrays and Pointers - c++

My teacher introduced us to looping through an array in the following format:
int size;
cin >> size;
int *garbage = new int[size];
for (int* p = garbage; p < (garbage + size); p++) {
if (*p > *max) {
max = p;
}
}
I understand what's going on, but it's just odd to me as a new concept. Are there better ways of going through an array like this?
Second Question: do you have to turn every pointer to a nullptr once you are done with it? For example, does it cause memory leaks to have that for loop like that with the pointer p or does it self destruct once it leaves the for loop scope?

There certainly are better (as in - more readable) ways of iterating through an array. For instance:
for (int index = 0; index < size; index++) {
if (p[index] > *max) {
max = &p[index];
}
}
However, as you see in the max = ... assignment, it's easier to have a pointer to an element of an array if you want to pass it on to another pointer variable.
Regarding your second question - there is no inherent value in setting it to nullptr. However, to prevent memory leaks, you should deallocate (free) the memory you reserved with operator new by using the operator delete once you're done with the array, like so:
delete [] p;
EDIT: Please note that you won't be able to safely dereference the pointer max (meaning you can't read that int it's pointing to with *max expression) after you deallocate the array without causing Undefined Behavior and possibly crashing your program, because your program will return the reserved memory to the OS and won't have access to it.
Thanks to #user4581301 for pointing it out.

There's another way of solving the problem. There's a C++ standard library algorithm for finding the maximum element in a range: std::max_element. No hand-written loop is needed.
int max = *std::max_element(garbage, garbage + size);
Note that it returns an iterator (a pointer in this case) so I used * to get the value from the iterator.
Your teacher might not like this solution.

It's a bit cleaner to do this:
int size = 10, max = 0;
int *mem = new int[size];
int *end = mem + size;
for (int *p = mem; p < end; p++)
if (*p > max)
max = *p;
But if you start using C++11 you can do this:
int max = 0;
std::vector<int> mem;
for (auto &i : mem)
if (i > max)
max = i;
There are probably even easier ways using other std namespace tools.
do you have to turn every pointer to a nullptr once you are done with it?
No, but it helps a lot when debugging. If you use a freed pointer that wasn't set to nullptr you'll be a lot more confused than if it is nullptr. Setting the pointer to nullptr doesn't actually automatically free the memory. You still have to call the appropriate 'free' function before setting to nullptr.

in C++11 you can do this:
int max = 0;
std::vector<int> mem;
for (auto &i : mem)
if (i > max)
max = i;

Related

Dynamic resizing array code not working in C++?

I am trying to create an array, which doubles every time it is completely filled.
#include <iostream>
using namespace std;
int* array_doubler(int* array, int size){
int *new_array = new int[size*2];
for(int i = 0; i < size; i++){
new_array[i] = array[i];
}
delete[] array;
return new_array;
}
int main()
{
int N = 10; // Size of array to be created
int *array = new int[0];
for(int i = 0; i < N; i++)
{
if(array[i] == '\0')
array = array_doubler(array, i);
array[i] = i*2;
}
//Printing array elemensts
for(int i = 0; i < N; i++)
cout << array[i] << '\t';
cout << '\n';
return 0;
}
Problem is when I create dynamic memory with new, all the spots have the null character \0 value in them (not just the last spot). i.e. If i write:
int* p = new int[5];
then all the 5 blocks in memory p[0],p[1],p[2],p[3],p[4],p[5] have \0 in them, not just the p[5]. So the if(array[i] == '\0') in my main() calls array_doubler for every single iteration of for loop. I want it to fill the available spots in the array first and when it reaches the last element, then call array_doubler.
Problem is when I create dynamic memory with new, all the spots have the null character \0 value in them (not just the last spot).
Actually they have undefined values in them. 0 is a valid value for them to have, but tomorrow the compiler might suddenly decide that they should all have 1 instead of 0.
If you want to detect the end of an array, then you have to remember how big the array is. C++ doesn't do it for you. Actually, it does do it for you if you use std::vector, but I suppose that's not the point of this exercise.
I'm not sure why you'd want to do this, as std::vector offer this kind of feature, and are more idiomatic of c++ (see isocpp faq on why C-style array are evil).
One of the issue of C-style array is the fact that they donĀ“t know their own size, and that they don't have default value, thus stay uninitialized.
If for some reason you need to not use std::vector, the next best solution would be to wrap the array with it's size in a structure or a class (which is kinda what std::vector is doing), or to initialize your array using std::memset (which is the C function you would use if you were in C).
Do keep in mind that this is not considered as good practices, and that the STL offer plenty of solution when you need containers.

How do I solve this memory leak

I am trying to recreate the vector class and I believe there is a memory leak in my code, but I don't know how to solve it. Using the CRT Library in my Visual Studio, it tells me that there is a supposed memory leak that doubles for each time that reserve is called.
I am not quite sure why that is or if there even is a memory leak. The memory leak detection says that it is this line in the reserve function int* temp = new int[n];
This is what I understand to be happening in the reserve function:
Once the contents of arr are copied into temp, it's fine to delete arr. Assigning arr = temp should work because all I'm doing is making arr point to the same place as temp. Because arr was previously deleted, I only have 1 array in the heap and arr and temp both point to the same array so there should be no memory leak. Temp shouldn't matter because it disappears after it exits the scope. On subsequent calls to the reserve function, every thing repeats and there should only be one array in the heap which arr points to.
I do believe that my thinking is probably erroneous in some way.
#include "Vector.h"
namespace Vector {
vector::vector() {
sz = 0;
space = 0;
arr = nullptr;
}
vector::vector(int n) {
sz = n;
space = n;
arr = new int[n];
for(int i = 0; i < n; i++) {
arr[i] = 0;
}
}
void vector::push_back(int x) {
if(sz == 0) {
reserve(1);
} else if (sz == space) {
reserve(2*space);
}
arr[sz] = x;
sz++;
}
void vector::reserve(int n) {
if (n == 1) {
arr = new int[1]; //arr was a nullptr beforehand
}
int* temp = new int[n];
for(int i = 0; i < n; i++) {
temp[i] = arr[i];
}
delete[] arr;
arr = temp;
space = n;
}
Your code assumes in vector::reserve(int n) that arr is null.
Instead maybe spilt up how reserve functions based on whether or not arr is null.
void vector::reserve(int n) {
if (arr) { //handle case when arr is null
space += n;
arr = new int[space];
//no need to copy anything!
} else { //handle case when arr is not null
int* tmp(new int[space + n]);
for(int i = 0; i < space; i++) {
tmp[i] = arr[i];
}
delete[] arr;
arr = tmp;
space += n;
}
}
Also the above code assumes you mean to reserve space+n instead of allowing reserve to shrink the array as you'll lose data if you reserve less then a previous reserve. It's usually better practice to not use assumptions about a pointer's state when working with them because when your code gets more complex the assumptions can end up getting forgotten or more obscure.
I have same issues too. I have created two pointers that points in the same address in heap. When I'm trying too deallocate the memory, and the result is only one pointer that can do that, it's the first pointers that point that address. The second or third pointers that points that address doesn't have an authority to deallocate the memory, but only the first pointers who have that authority.
Example
int *a = new int[5];
int *b = a;
int *c = a;
Pointers b and c doesn't have an authority too dealloacte the memory address that pointers a pointed. Therefore, the memory wasn't deallocated if i'm saying delete[] b nor delete[] c, they didn't have an authority for doing that. Then I tried to write delete [] a and that worked. I don't have an real answers, and I just trying to approaching through my try and errors that I have done. And that's what I got.
Actually this case is violating the rules, but C++ still allowed us to do it, it's called undefined behaviors. We are violating the rules of delete[] operators by, but C++ still allowed you to do, and as the result, you get unexpected output.
Not too much wrong in there.
Bugs
if (n == 1) {
arr = new int[1]; //arr was a nullptr beforehand
}
The comment cannot be guaranteed. Nothing prevents multiple calls of resize including a call of reserve(1), and that will leak whatever memory was pointed at by arr. Instead consider
if (arr == nullptr) {
arr = new int[n]; //arr was a nullptr beforehand
}
now the comment is guaranteed to be true.
The copy loop overshoots the end of arr every time the size of the array is increased.
for(int i = 0; i < n; i++) {
temp[i] = arr[i];
}
arr is only good up to arr[sz-1]. If n is greater than space, and it almost always will be, arr[i] wanders into the great wilds of Undefined Behaviour. Not a good place to go.
for(int i = 0; i < n && i < sz; i++) {
temp[i] = arr[i];
}
Checks both n and sz to prevent overrun on either end and copying of data that has not been set yet. If there is nothing to be copied, all done.
Targets of opportunity
The class needs a destructor to release any memory that it owns (What is ownership of resources or pointers?) when it is destroyed. Without it, you have a leak.
vector::~vector() {
delete[] arr;
}
And if it has a destructor, the Rule of Three requires it to have special support functions to handle (at least) copying of the class or expressly forbid copying.
// exchanges one vector for the other. Generally useful, but also makes moves
// and assignments easy
void vector::swap(vector& a, vector& b)
{
std::swap(a.sz, b.sz);
std::swap(a.space, b.space);
std::swap(a.arr, b.arr);
}
// Copy constructor
vector::vector(const vector& src):
sz(src.sz),
space (src.space),
arr(new int[space])
{
for(int i = 0; i < sz; i++) {
arr[i] = src.arr[i];
}
}
// move constructor
vector::vector(vector&& src): vector()
{
swap(*this, src);
}
// assignment operator
vector::vector& vector::operator=(vector src)
{
swap(*this, src);
return *this;
}
The Copy Constructor uses a Member Initializer List. That's the funny : bit.
The assignment operator makes use of the Copy and Swap Idiom. This isn't the fastest way to implement the assignment operator, but it is probably the easiest. Start with easy and only go to hard if easiest doesn't meet the requirements.

Can I use std::sort on heap allocated raw arrays?

We know that when using a contiguous block of memory we can easily get an iterator (here &arra[0] or arra) and pass the iterators to std::sort.
for instance:
int arra[100];
for (int i = 0; i < 100; i++) {
arra[i] = rand() % 32000;
}
for (int i = 0; i < len; i++)std::cout << arra[i]<<" ";
std::sort(arra,arra+100);
Now if I have a heap allocated array, say, like arr here:
int len;
len = 100;
int* arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = rand() % 32000;
}
I don't know whether I can get an iterator for this array, so can I use std::sort for this array at all?
if not, are there any workarounds to using std::sort on such an array?
Pointers do meet criteria of RandomAccessIterator which is required by std::sort. It doesn't matter if they point to stack memory or heap memory, as long as they point to the same (contiguous) array. So you can simply use:
std::sort(arr, arr + len);
This being said, std::vector is probably a better choice for allocating an array on the heap. It will save you the headache of managing memory on your own.
Yes, you can use std::sort in the same way in both cases, std::sort does not know or care how the memory was allocated.
In the C++ Library, Iterators are basically Fancy Pointers. As such, it is standard-compliant to just increment the pointer to the end of the array to get the "end" pointer:
#include<algorithm>
#include<iostream>
int main() {
int len;
len = 100;
int* arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = rand() % 32000;
}
//Valid, Defined Behavior that works as expected
std::sort(arr, arr + len);
//alternative, to make the code easier to read:
//auto begin = arr;
//auto end = arr + len;
//std::sort(begin, end);
for(int i = 0; i < len; i++)
std::cout << arr[i] << std::endl;
}
However, some Compilers (like Visual Studio's compiler) recognize that this kind of code is inherently unsafe, because you're required to manually supply the length of the array. As a result, they will cause a (suppressible with a Compiler flag, if you need to) Compile-time error if you try to do this, and advise you use a compiler-provided utility instead:
#include<algorithm>
#include<iostream>
int main() {
int len;
len = 100;
int* arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = rand() % 32000;
}
//MSVC Specific Code!
auto begin = stdext::make_checked_array_iterator(arr, len);
auto end = arr + len;
std::sort(begin, end);
for(int i = 0; i < len; i++)
std::cout << arr[i] << std::endl;
}
For more on this particular quick of the Visual Studio compiler, see here: https://learn.microsoft.com/en-us/cpp/standard-library/checked-iterators?view=vs-2019
Can I use std::sort on heap allocated raw arrays?
Yes.
I don't know whether I can get an iterator for this array
You can.
A pointer to element is a random access iterator for arrays. In the case of an automatic array, the array name implicitly decays into a pointer that you can use as an iterator to beginning of the array. In the case of a dynamic array, the result of new[] is already a pointer i.e. an iterator to the beginning of the array. You can get the pointer to the end using pointer arithmetic just like in your example.
The only significant difference between an array variable, and a pointer to a dynamic array regarding the use of std::sort is that you cannot use std::end or std::size with a pointer like you could with an array variable. Instead, you need to separately know the size of the array. In this case you've stored the length in the variable len.

How do I properly reference this pointer multiple times?

This is my function to find the union of 2 set arrays located by a void pointer which I have issues running the first part to copy Set A into the union Set before doing comparisons with Set B
Right now the output of this code produces example
Set A = {1,5,7,8}
Union Set = {8,8,8,8} Copies last element of Set A 4 times
as the last loop causes the temp pointer to point at 8.
Do I have to create a new int pointer for each loop or is there a better way of going around this
// Note I cannot use vectors or sorting methods as it isnt in my learning scope yet so i'll have to stick to the primitive comparison way
//Definitions
// VoidPtr is Void*
// aSet is (VoidPtr *a = new VoidPtr[MAX])
// getElementI(aSet[i]) Returns an integer value at that position of the pointer
void findUnion(VoidPtr * aSet,VoidPtr * bSet,VoidPtr * unionSet,int sizea,int sizeb,int &sizec)
{
int* temp;
VoidPtr vp;
int notEqual = 0;
// Copy set a into set c
for(int i =0; i < sizea; i++)
{
*temp = getElementI(aSet[i]);
vp = temp;
unionSet[i] = vp;
}
}
int* temp;
Here temp is an uninitiaised pointer
*temp = getElementI(aSet[i]);
Here temp is being dereferenced. Dereferencing uninitialised pointers results in a program crash (at best) and all sorts of weird behaviour (at worst).
I'm finding it quite hard to understand what you really need to do, but allocating a new int pointer every time round the loop sounds reasonable. Like this
for(int i =0; i < sizea; i++)
{
int *temp = new int (getElementI(aSet[i]));
unionSet[i] = temp;
}
But I am guessing.

C++ Pointer Dereferenced Assignment

I am trying to learn C++ by firstly going through the low level details before I start using abstractions such as std::copy or memcpy. Currently I am stuck in trying to figure out why this code is never assigning into "sortedArray" variable, when looking with debugger I dereference the value from "data" correctly but it is never assigned to "sortedArray". I see value such as "-842150451" instead of "14" for first value. Can you please help me figure out what I am doing wrong ? And any other issues there may be that I do not see or advice would be greatly appreciated !
void swap(int* bigger, int* smaller){
*bigger += *smaller;
*smaller = *bigger - *smaller;
*bigger = *bigger - *smaller;
}
int* bubbleSort(int *data, int size){
bool swapped = true;
int *sortedArray = (int*)malloc(size*sizeof(int));
for (int i = 0; i < size;i++){
*(sortedArray++) = *(data++);
}
while (swapped){
swapped = false;
for (int i = 1; i <= size - 1; i++){
if (sortedArray[i - 1] > sortedArray[i]){
swap(&sortedArray[i - 1], &sortedArray[i]);
swapped = true;
}
}
size--;
}
return sortedArray;
}
*(sortedArray++) = *(data++); modifies the pointer so it no longer points to the start of the allocated memory. So, later on sortedArray[i] is whatever happens to be in memory past the array, and accessing it is undefined behavior.
If you must use pointers, a quick fix is to use a temporary one, like:
int *sortedArray = (int*)malloc(size*sizeof(int));
int* s = sortedArray;
for (int i = 0; i < size;i++){
*s++ = *data++;
}
Another way would be:
int *sortedArray = (int*)malloc(size*sizeof(int));
for (int i = 0; i < size;i++){
sortedArray[i] = data[i];
}
But, the best way would be to use standard containers and algorithms, like vector and sort.
Here's a demo of the first fix in action.
Change
*(sortedArray++) = *(data++);
to
sortedArray[i] = data[i];
You need to leave intact the pointer to the block of memory you allocated, so you can use it (and free it) later.
Note, there is nothing to be gained by using the *(x+y) syntax instead of x[y], they are equivalent but the latter is easier to read.
In C++ you should not use malloc. Instead use new int[size]. For int there is no difference other than reduced risk of making a typo, however for non-trivial types malloc will not construct them correctly.
That is not C++ at all. You can write generic code that takes a begin iterator and an end iterator in order for it to work with any kind of container that supports such semantic.
template<typename IT>
void bubble_sort(IT begin, IT end) {
while (true) {
bool swapped = false;
for (IT i = begin; i != end-1; i = i+1) {
if (*i > *(i+1)) {
std::iter_swap(i, i+1);
swapped = true;
}
}
if (swapped == false) return;
}
}
Live demo
Where std::iter_swap is like std::swap but works on iterators. You can see iterators as a pair of pointers to the beginning and (past the) end of a container.
You first pointer sortedArray points to some allocated memory.
Then in the first for loop you increment the pointer. not it doesn't point to that memory anymore.
Simply use a temporary pointer for the memory copy.
int* t = sortedArray ;
And now use t in your for loop which copies the data.
Instead of the temporary variable, you can rather count the number of times you called sortedArray++ in your for loop.
If you take a look: for (int i = 0; i < size;i++) you will see that the loop took exactly size number of iterations.
Just subtract size from the pointer after the loop and you point back to your allocated memory.
sortedArray -= size ;