C++, Passing a 2D array on stack to function by reference - c++

void build() will create a 2D array whose size is determined at run time and pass it to modify() by reference.
void modify() will make some change of the array.
.h file:
void modify(______, int mySize);
void build(int size);
in .cpp file:
void modify(______, int mySize) {
for (int i = 0; i < mySize; i++)
for (int j = 0; j < mySize; j++)
myArray[i][j] = false;
}
void build(int size) {
bool myArray[size][size];
modify(______, size);
}
Can someone tell me what to put in these blanks? I tried many way to cast myArray but still not working. Thank you so much!

First, note that variable length arrays (VLAs) are not standard C++. The fact that this line compiles is due to a GCC compiler extension:
bool myArray[size][size];
It simply isn't valid C++. The dimensions of your array need to be compile time constants, yet here you're using arguments passed to your function build.
Anyway, to pass a VLA to a function, you have to continue using compiler extensions. However, as far as I've tested, these only work when compiling as C. The first option uses an extension that allows parameters to be used in other parameter declarations:
void modify(int mySize, bool array[][mySize]);
The second option, if you want mySize to be the second argument, also uses a compiler extension allowing forward declarations of parameters:
void modify(int mySize; bool array[][mySize], int mySize);
Here int mySize; is a parameter forward declaration.
Nonetheless, you really shouldn't be using variable length arrays. You can dynamically allocate arrays of variable length, like so:
void modify(bool** array, int mySize);
void build(int size) {
bool** myArray = new bool*[size];
for (int i = 0; i < size; i++) {
myArray[i] = new bool[size];
}
modify(myArray, size);
}
However, this is still not a recommended practice in C++. You should avoid dynamic allocation unless you really need it, and when you do need it you should manage it in some way. Here, you would need to remember to delete[] each element of myArray and myArray itself.
What you should really be using is the standard containers. A std::vector<std::vector<bool>> would suit you well here:
void modify(std::vector<std::vector<bool>>& array);
void build(int size) {
std::vector<std::vector<bool>> myArray(size, std::vector<bool>(size));
modify(myArray);
}
Now you don't even have to pass along the size.

Use it this way:
void modify(bool**& myArray, const int mySize)
{
for (int i = 0; i < mySize; i++)
for (int j = 0; j < mySize; j++)
myArray[i][j] = false;
}
void build(const int size)
{
// create the array
bool** myArray = new bool*[size];
for (int i=0; i<size; i++)
myArray[i] = new bool[size];
modify(myArray, size);
// release the array
for (int i=0; i<size; i++)
delete[] myArray[i];
delete[] myArray;
}

Related

Rewrite This Malloc Using New For 2D Array

After some trial and error I found a way to malloc a 2D array so it is contiguous in memory, equivalent to the non-dynamic case.
int numRows =2;
int numCols = 4;
int (*p)[numCols];
p = (int (*)[numCols]) malloc(sizeof(int)*numRows*numCols);
So p is now basically the same as if I had done int p[2][4] - except it's on the heap instead of the stack.
2 Questions:
Do I just need to call free(p) to free the memory? No looping?
How would I convert this to using new, instead of malloc?
I tried
p = new (int (*)[4])[2];
But that gave the error:
error: cannot convert int (**)[4] to int (*)[4] in assignment
Here's a class template that uses one std::vector to hold a contiguous buffer, and size-aware proxy objects to access array elements dimension-by-dimension:
template<typename T>
class TwoDArray {
private:
std::size_t n_rows;
std::size_t n_cols;
std::vector<T> buf;
public:
class OneDArrayProxy {
private:
T *rowptr;
std::size_t colsize;
public:
OneDArrayProxy(const T *rp, std::size_t cs) : rowptr(const_cast<T *>(rp)), colsize(cs) {}
T const &operator[](std::size_t index) const {
return rowptr[index];
}
T &operator[](std::size_t index) {
return rowptr[index];
}
std::size_t size() const { return colsize; }
};
TwoDArray(std::size_t rows, std::size_t cols) : n_rows(rows), n_cols(cols), buf(rows * cols) {}
TwoDArray() : TwoDArray(0, 0) {}
OneDArrayProxy operator[](std::size_t index) const {
return OneDArrayProxy(&buf[index * n_cols], n_cols);
}
std::size_t rows() const { return n_rows; }
std::size_t columns() const { return n_cols; }
};
Usage example:
int main()
{
TwoDArray<int> arr(9, 5);
for (std::size_t i = 0; i < arr.rows(); i++) {
for (std::size_t j = 0; j < arr.columns(); j++) {
arr[i][j] = i * 10 + j;
}
}
for (std::size_t i = 0; i < arr.rows(); i++) {
// you can use the array element's 'size()' function instead of 'columns()'
for (std::size_t j = 0; j < arr[i].size(); j++) {
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
}
You can't do that in C++. Your malloc() code is perfectly valid C, but not valid C++. And it won't work with new.
C++ requires array types to have constant size, C allows arrays types of dynamic size. There is only an exception for 1D arrays, which may be allocated with dynamic size in C++, but that's it. In a 2D array, the second size must be known at compile time.
This is the one point at which C is much more powerful than C++.
It takes a bit to convince g++ to follow the standard in this regard, but compiling this little program
#include <stdlib.h>
int main(int argc, char** args) {
int (*foo)[argc];
}
with g++ -pedantic -std=c++11 foo.cpp dutifully produces the error message:
foo.cpp: In function ‘int main(int, char**)’:
foo.cpp:4:17: warning: ISO C++ forbids variable length array ‘foo’ [-Wvla]
1) Yes, you could just call free()
But attention, you are doing pointer aliasing (2 pointers to different types, int and int[] with the same address), which can cause subtle optimisation bugs. And in C++ it's a very bad practice to use malloc(), and numcols should be const.
2) The way you can do this in C++ would be to use [<array>][2] if the size to be known at compile time:
array<array<int, 4>,2> a;
The more flexible alternative is to use vectors which allow for dynamic size and resizing:
vector <vector <int>> b(2, vector<int>(4));
3) With new you could also do :
p = new (int[2][4]);
The first dimension could also be variable, but the second has to be a constant. But I'd encourage you to use one of the standard containter alternative.

Create Array Using Pointers

i'm working on a array header from base definition of an array to create an array of any type with this header,so i created a array class with functions and constructors.
this is my code so far:
#include <iostream>
#define newline "\n"
class Arr
{
public:
typedef float T;
public:
Arr(int size);
Arr(int size, T fill);
T get(unsigned index) const;
void set(unsigned index, T newvalue);
unsigned Size() const;
unsigned SIZE;
void Print();
private:
};
Arr::Arr(int size,T fill)
{
SIZE = size;
T *pointer;
for (int i = 0; i < size; i++)
{
*pointer = fill;
pointer++;
}
}
void Arr::set(unsigned index, T newvalue)
{
T *pointer;
pointer = 0;
for (unsigned i = 0; i < index; i++)
{
pointer++;
}
*pointer = newvalue;
}
void Arr::Print()
{
T *pointer;
pointer = 0;
for (unsigned i = 0; i < SIZE; i++)
{
std::cout << *pointer << newline;
pointer++;
}
}
i know that my pointer point to nothing,as my question is my pointer should point to what to make this code work correctly?!
any time i point it to 0 after debug it crashes!
thanks...!
Pointers are tricky part of c++.
Here is a good link to get you started
http://www.codeproject.com/Articles/7042/How-to-interpret-complex-C-C-declarations
The reason your code doesn't work is a memory block for the array pointed to by the pointer is not allocated. You have to use the predecessor new in-order to achieve that.
Here an example
int size;
T arr;
T* ptr_2_arr;
ptr_2_arr = new T[size];
To retrieve elements of the array you can loop the array using a for loop
*ptr_2_arr[i];
hope this helps.
Post the problem statement if you need more detail
You must make a data member of the class that will point to the allocated memory for the array. Also you need to define a copy constructor, the copy assignment operator and the destructor.
Also it would be better that type of parameter size of constructors coinsides with the type of data member SIZE, I do not understand why this variable is written in capital letters.
ALso there is no any sense to make the data member SIZE and the function Size() public. if SIZE is public it can be changed by the user at any moment.
Make sure you specify the size of the array in your constructor.
SIZE = size;
pointer = new T[size]; //this is where I see an issue. Specify the size of your array.
for (int i = 0; i < size; i++)
{
*(pointer + i) = fill; //This is fine but you are filling up the array with only one number, fill. Nothing wrong with that if that is you intention. Try (*(pointer + i) = i; for i though size elements.
}

T*[] vs T** function declaration [duplicate]

This question already has answers here:
Pointer vs Array in function definition: what is the difference between void fct1(int *p) and void fct1(int p[])?
(2 answers)
Closed 9 years ago.
I was told there actually is a difference between a T*[] and a T** and that a two dimensional array does not decay into T** as shown here: std::array to pointer access violation error
However.. If they are not the same, then why can't I declare BOTH of these functions?
template<typename T>
void Transpose(T** Data, std::size_t Size)
{
for (int I = 0; I < Size; ++I)
{
for (int J = 0; J < I; ++J)
{
std::swap(Data[I][J], Data[J][I]);
}
}
}
template<typename T>
void Transpose(T* Data[], std::size_t Size)
{
for (int I = 0; I < Size; ++I)
{
for (int J = 0; J < I; ++J)
{
std::swap(Data[I][J], Data[J][I]);
}
}
}
The compiler says that its already declared.. Any ideas?
It's because in function argument lists, T* data[] is just another way to write T** data.
To distinguish between arrays and pointers, you can do the following:
template<typemane T> void Transpose(T** data, ...) { ... }
template<typename T, int N> void Transpose(T* (&data)[N], ...) { ... }
However in your case, you don't not use that distinction anyway, so you can just write the first version and omit the second. The automatic decay of array to pointer when calling the function will make it work on arrays as well.
T*[] and T** are the same when they're used as function parameters.

C++ passing an array pointer as a function argument

I'm trying to use pointers of arrays to use as arguments for a function which generates an array.
void generateArray(int *a[], int *si){
srand(time(0));
for (int j=0;j<*si;j++)
*a[j]=(0+rand()%9);
} //end generateArray;
int main() {
const int size=5;
int a[size];
generateArray(&a, &size);
return 0;
} //end main
But when I compile this this message appears:
cannot convert `int (*)[5]' to `int**' for argument `1' to `void generateArray(int**, int*)'
You're over-complicating it - it just needs to be:
void generateArray(int *a, int si)
{
for (int j = 0; j < si; j++)
a[j] = rand() % 9;
}
int main()
{
const int size=5;
int a[size];
generateArray(a, size);
return 0;
}
When you pass an array as a parameter to a function it decays to a pointer to the first element of the array. So there is normally never a need to pass a pointer to an array.
int *a[], when used as a function parameter (but not in normal declarations), is a pointer to a pointer, not a pointer to an array (in normal declarations, it is an array of pointers). A pointer to an array looks like this:
int (*aptr)[N]
Where N is a particular positive integer (not a variable).
If you make your function a template, you can do it and you don't even need to pass the size of the array (because it is automatically deduced):
template<size_t SZ>
void generateArray(int (*aptr)[SZ])
{
for (size_t i=0; i<SZ; ++i)
(*aptr)[i] = rand() % 9;
}
int main()
{
int a[5];
generateArray(&a);
}
You could also take a reference:
template<size_t SZ>
void generateArray(int (&arr)[SZ])
{
for (size_t i=0; i<SZ; ++i)
arr[i] = rand() % 9;
}
int main()
{
int a[5];
generateArray(a);
}
You do not need to take a pointer to the array in order to pass it to an array-generating function, because arrays already decay to pointers when you pass them to functions. Simply make the parameter int a[], and use it as a regular array inside the function, the changes will be made to the array that you have passed in.
void generateArray(int a[], int si) {
srand(time(0));
for (int j=0;j<*si;j++)
a[j]=(0+rand()%9);
}
int main(){
const int size=5;
int a[size];
generateArray(a, size);
return 0;
}
As a side note, you do not need to pass the size by pointer, because you are not changing it inside the function. Moreover, it is not a good idea to pass a pointer to constant to a parameter that expects a pointer to non-constant.
I'm guessing this will help.
When passed as functions arguments, arrays act the same way as pointers. So you don't need to reference them. Simply type:
int x[]
or
int x[a]
. Both ways will work. I guess its the same thing Konrad Rudolf was saying, figured as much.
This is another method . Passing array as a pointer to the function
void generateArray(int *array, int size) {
srand(time(0));
for (int j=0;j<size;j++)
array[j]=(0+rand()%9);
}
int main(){
const int size=5;
int a[size];
generateArray(a, size);
return 0;
}

C++ Operator Overload Error

I'm trying to create my own version of an array called a safearray, to test my knowledge of operator overloading and creating proper class's and such.
I'm encountering two errors.
SafeArray.h:11:15: error: ‘const int SafeArray::operator’ cannot be overloaded
SafeArray.h:10:10: error: with ‘int& SafeArray::operator’
My code is split between three files.
Main.cpp
#include <cstdlib>
#include <iostream>
#include "SafeArray.h"
using namespace std;
int main(int argc, char** argv) {
SafeArray a(10); // 10 integer elements
for (int i = 0; i < a.length(); i++) {
cout << i << " " << a[i] << "s" << endl; // values initialise to 0
}
cout << endl << a[1]; // Program exits here.
a[3] = 42;
cout << a[3];
a[10] = 10;
cout << a[10];
a[-1] = -1; // out-of-bounds is "safe"?
SafeArray b(20); // another array
b = a; // array assignment
for (int i = 0; i < b.length(); i++) {
cout << b[i] << endl; // values copied from a
}
return 0;
}
SafeArray.h
#ifndef SAFEARRAY_H
#define SAFEARRAY_H
class SafeArray {
public:
SafeArray(int); // int variable will be the array size
int length();
int boundsCheck(int y); // constructor will call this function
// const SafeArray operator= (const SafeArray&);
int& operator[] (int y);
const int operator [] (const int y); // you need this one too.
SafeArray &operator=(SafeArray rhs) {
std::swap(array, rhs.array);
std::swap(length_, rhs.length_);
}
SafeArray(SafeArray const &other);
~SafeArray();
private:
int length_;
int *array;
//int array[];
};
#endif /* SAFEARRAY_H */
SafeArray.cpp
#include "SafeArray.h"
#include <iostream>
SafeArray::SafeArray(int x) {
length_ = x;
array = new int[length];
for (int i = 0; i < length_; i++) {
array[i] = 0;
}
}
int SafeArray::length() {
return this->length_;
}
int SafeArray::boundsCheck(int y) {
}
int& SafeArray::operator[] (int y) {
return array[y];
}
SafeArray::~SafeArray() {
delete [] array;
}
SafeArray::SafeArray(SafeArray const &other) {
int *temp = new int[rhs.size_];
for (int i=0; i<rhs.size_; i++)
temp[i] = rhs.array[i];
std::swap(temp, array);
delete [] temp;
return *this;
}
Your class definition isn't valid. int array[] is an incomplete type, which must not appear as a (non-static) class member. Some compilers accept this as a synonym for int array[0], but zero-sized arrays are not valid in C++, either (only in C99).
In short, you cannot write your code the way you do. You need to learn about dynamic allocation and manage your own memory. Check out how std::vector is implemented.
In C++11, I might recommend a std::unique_ptr<int[]> array as a quick-fix approach, to be initialized as array(new int[x]).
Actually int array[] is valid, and may appear as a class member. The following compiles with strict C++11 conformance:
class foo
{
public:
foo() {}
int length;
int A[];
};
void ralph()
{
foo *bar = (foo *)new int[ 21 ];
bar->length = 20;
bar->A[0] = 1;
}
This is legal, and has its advantages (occasionally). Although it is not commonly used.
However, I suspect that the OP wanted something more along the lines of
class SafeArray {
public:
SafeArray(int); // int variable will be the array size
int length();
int boundsCheck(int y); // constructor will call this function
int& operator[] (int y);
const int operator [] (const int y) // you need this one too.
private:
int length_;
int *array;
};
along with
SafeArray::SafeArray(int x) {
length_ = x;
array = new int[length];
for (int i = 0; i < length_; i++) {
array[i] = 0;
}
}
As #Kerrek already pointed out, your class definition is clearly wrong (shouldn't compile).
To fix it, you want to change the definition to something like:
int *array;
Then in your default ctor you could use something like this:
SafeArray::SafeArray(unsigned size = 0)
: array(new int[size])
{
for (unsigned i=0; i<size; i++)
array[i] = 0;
}
Then, yes, you'll need to write an assignment operator. The usual way is called the copy and swap idiom. You create a copy, then swap the contents of the current one with those of the copy:
SafeArray &operator=(SafeArray rhs) {
std::swap(array, rhs.array);
std::swap(length_, rhs.length_);
}
Along with that, you'll need a copy constructor that makes a copy of the data as well:
SafeArray::SafeArray(SafeArray const &other) {
int *temp = new int[rhs.size_];
for (int i=0; i<rhs.size_; i++)
temp[i] = rhs.array[i];
std::swap(temp, array);
delete [] temp;
return *this;
}
Finally, you'll need a destructor to destroy an object and (particularly) delete the memory it holds:
SafeArray::~SafeArray() {
delete [] array;
}
Then realize that all of that is an ugly mess that will never really work well. In particular, the basic methodology is restricted to an array that's basically fixed in size. As long as you only store ints, it's fairly easy to overlook the problems, and make a dynamic array that (sort of) works. When/if you want to store some other type, however, you just about need to separate allocating memory from initializing objects in that memory, which means throwing away essentially all the code above, and replacing it with something that:
keeps track of the array size and allocation size separately
allocates memory with ::operator new, an Allocator object, or something else similar
uses placement new to initialize objects in the memory when needed.
uses explicit destructor calls to destroy the objects
uses ::operator delete to release memory
and so on. To summarize, std::vector is not a trivial piece of work.
The error message refers to these two lines:
int& operator[] (int y);
const int operator [] (const int y); // you need this one too.
Your error message says that (int y) and (const int y) are too similar to be two different overloads of the [] operator. You cannot overload on (int y) and (const int y) because the calls would all be ambiguous.
You probably meant to return a const int if your SafeArray is const, but return an int& if your SafeArray is not const. In that case, you declare the second function to apply to const SafeArray, by putting the word const after the parameter list. This is what you should write in SafeArray.h:
int& operator[] (int y);
const int operator [] (int y) const; // you need this one too.
You would then have to write both of these functions in SafeArray.cpp:
int& SafeArray::operator[] (int y) {
return array[y];
}
const int SafeArray::operator[] (int y) const { // you need this one too.
return array[y];
}