(I'm from C background and new in C++ and its STLs)
I'm writing a C++ array of vectors that will be passed (as a reference of an array of vectors) through a function and will be processed in it.
In this case [in C] I would have passed a pointer to my custom data type (call by value under the hood.)
My code that's giving errors in compile time while trying to do so:
#include <cstdio>
#include <vector>
using namespace std;
/*
the problem is I can't get the syntax. vector<type> &var is
a reference to a single dimension array of vectors.
*/
void pass_arrayOf_vect(vector<int> &array, int lmt);
int main() {
int lmt = 10;
vector<int> lst[lmt];
pass_arrayOf_vect(lst, lmt);
return 0;
}
/*
and the traditional ambiguity of whether using "." or "->" for
accessing or modifying indexes and their members.
*/
void pass_arrayOf_vect(vector<int> &lst, int lmt) {
for (int i = 0; i < lmt; i++) {
lst[i].push_back(i*i);
}
for (int i = 0; i < lmt; i++) {
printf("array[%d]: ", i);
for (int j = 0; j < lst[i].size(); j++) {
printf("%d ",lst[i][j]);
}
printf("\n");
}
printf("\n");
return;
}
In the main function the lst variable is an array of vectors. When you pass this to the pass_arrayOf_vect function you pass a pointer to the first element.
I.e. when you do
pass_arrayOf_vect(lst, lmt);
it's actually the same as doing
pass_arrayOf_vect(&lst[0], lmt);
So the function you call needs to accept a pointer to a vector as its first argument (not a reference):
void pass_arrayOf_vect(vector<int> *array, int lmt);
// ^
// Note use of asterisk instead of ampersand
An even better solution would be to use an std::array of vectors instead. Or if you're on an older compiler without support for std::array, or need the amount to be run-time configurable (in which case you can't use plain C-style arrays anyway), use a vector of vectors.
Related
Is it really possible?
I know how to pass 2-D arrays using double pointer. And as per my understanding, it should for 3-D arrays as well. But I would love to be proven wrong. This question will surely reveal how the arrays are interpreted.
The fact here is that the 3-D array is a contiguous block, not some array of pointers of smaller chunks.
This program gives me error :
#include <iostream>
using namespace std;
void display(int ***arr, int l, int m, int n)
{
for(int i=0; i<l; i++)
for(int j=0; j<m; j++)
for(int k=0; k<n; k++)
cout << *(*(*(arr+i)+j)+k) << endl;
}
int main()
{
int arr[][2][2] = {{{1,2},{3,4}},{{10,20},{30,40}}};
display((int***)arr,2,2,2);
}
OUTPUT
test.cpp:17:19: error: cannot convert 'int (*)[2][2]' to 'int***' for argument '1' to 'void display(int***, int, int, int)'
display(arr,2,2,2);
^
2D Arrays passed to double pointer
I believe I can do something similar to 3D arrays as well, but this is way too bad to read.
#include <iostream>
using namespace std;
void display(int **arr, int m, int n)
{
for(int i=0; i<m; i++)
for(int j=0; j<n; j++)
cout << *(*(arr+i)+j) << " " << arr[i][j] << endl;
}
int main()
{
int arr[][3] = {{1,2,3},{4,5,6}};
int *temp[2];
for(int i=0; i<2; i++)
temp[i] = *(arr+i);
display(temp,2,3);
}
OUTPUT
1 1
2 2
3 3
4 4
5 5
6 6
What you do for 2D arrays is correct because you have built an auxiliary array of pointers and you pass that array of pointer as a int **. Even for 2D arrays, this
void display(int **arr, int m, int n);
...
int arr[][3] = {{1,2,3},{4,5,6}};
display(arr,2,3); // WRONG! use a 2D array as an array of pointers
would be wrong.
And anyway, the C++ standard is unfriendly with multi-dimensional arrays: there is no way to write a strictly conformant program passing a multi-dimensional arrays of unknown dimension. Many compilers accept it as extensions, but it may be non portable on other compilers.
The idiomatic way is to only use 1D arrays as the underlying data structure, and provide methods to process it as a multi-dimensional container by internally doing index computations.
BTW I have tried to build a multi-dimensional contiguous container with arbitrary run-time dimensions, respecting all the constraints of the standard containers, and given up after realizing that the standard did not allow it: it is impossible to build a nice iterator over object that do not hold their own data. Here is my best attempt. The answers and comments explain why it is hopeless.
Although you can pass a 1-D array like this
void pass1Darray(int a[])
{
statements;
}
int main()
{
int a[10];
pass1Darray(a);
}
In fact, compiler would see int a[] as int* a and this is the reason why people wonder is it possible to pass a 2-D array by pointer_to_pointer.
But it doesn't make sense!
If you want to pass a 2-D arraybob[5][10], you can see bob as a array and its element is a array, and pass it like this
void pass2Darray( int (*array) [10] ) // it means you pass a pointer which points to a int[10]
{
statements;
}
int main()
{
int bob[5][10];
pass2Darray(bob);
}
This is about passing a 2-D array.
btw, English is not my native langueue, and I'm a beginner of c++ , too.
If there's something wrong, please let me know, thank you.
When you declare an array locally int arr[][2][2] the compiler instantiate a one dimensional vector and "remember" what the offset is to get the right index.
Also local arrays are stored in the stack which is not good if you need large matrices. Another property of int arr[][2][2] is that why when you try to pass it as argument to a function, int arr[][2][2] is the type. You have to specify all the dimensions.
Pointers work differently. If you instantiate a 2D array you need to allocate an array of pointers to rows, and allocate each row array to hold data individually. In C++ I think it's best to use the standard library, which has a standard implementation of dynamic pointers that take care of all allocations. std::vector
In conclusion:
I would use local arrays when the memory required is small and I don't need to use pointers. Local arrays are good if you want to avoid using the heap.
Using pointersm new/delete or malloc/free is allowed but I think it's better to use the standard library in C++, so I would use std::vector in all other scenarios.
I believe I can do something similar to 3D arrays as well, but this is way too bad to read.
Yes, as n.m. explained in their comment
In your 2D code you have created a brand new array of pointers, populated it, and passed it to your function instead of your original 2D array. In your 3D code you have not attempted anything of the sort.
Since the question is tagged as C++, we can use a different toolbox than C coders have to.
In C++, a parameter can be passed to a function by reference, not only by value, and we can write a template that can be used by the compiler to generate the right function for used type.
#include <iostream>
#include <iomanip>
template <class T, size_t L, size_t M, size_t N>
void display( T const (&arr)[L][M][N], int width )
// ^^^^^^
{
for (size_t i = 0; i < L; ++i) {
for (size_t j = 0; j < M; ++j) {
for (size_t k = 0; k < N; ++k) {
std::cout << std::setw(width) << arr[i][j][k];
}
std::cout << '\n';
}
std::cout << '\n';
}
}
int main()
{
int arr[3][2][4] = {
{ {1,2,3,4},
{5,6,7,8}
},
{ {10,20,30,40},
{50,60,70,80}
},
{ {100,200,300,400},
{500,600,700,800}
}
};
display(arr, 5);
}
Live, HERE.
The next step would be, of course, to write a class which encapsulates the concept of a multidimensional array.
So I made a function that takes arrays as parameters and I've tried calling the function by passing arrays that have not been defined as variables into said function (like {0,0,0,0}). However, I am given an error which says "too many initializer values."
Say we have a function defined as:
int func(int values[]) {
int average = 0;
for(int x = 0; x < values.size(); x++) {
average += values[x];
}
return average / values.size();
}
And we want to call it without defining an array to pass in like this: func({1,6,7,2});
Is there any way to do something like this or would I have to define an array and pass it into the function that way?
You cannot do that using built-in arrays. The fact that Arrays are neither Assignable nor Copy-able. Also They are not classes so they don't have member functions like size() or they take Initializer-list.
You can achieve that through using std::array if the size is constant or using std::vector if the size if dynamic.
#include <array>
int func(const std::array<int, 5>& values) {
int average = 0;
for (size_t x{}, sz{ values.size() }; x != sz ; ++x)
average += values[x];
return average / values.size();
}
int main() {
auto ret{
func({ 1, 6, 7, 2 })
};
std::cout << ret << std::endl;
}
Also don't mix Unsigned with Signed in calculations like in your loop:
for(int x = 0; x < values.size(); x++) // x is int while values.size() is unsigned int.
int func(const std::array<int, 5>& values): pass by reference to avoid the copy especially if the size is big. Also pass by const as long as the function doesn't intend to change the parameter also another benefit of using const reference is you can pass literals instead of an object.
N.B: I recommend to also to use range-based for because it is really relevant in your example as long as you want to iterate over all the elements and not intending to insert nor to delete elements:
int average = 0;
for (const auto& e : values)
average += e;
Another version of func as #M.M pointed out is to use std::accumalate to do the job for you:
int func(const std::array<int, 5>& values) {
return std::accumulate(values.begin(), values.end(), 0) /
values.size();
}
Using a vector, yes:
#include <vector>
using namespace std;
void f( const vector <int> & v ) {
}
int main() {
f( {1,2,3,4} );
}
Arrays don't work like that. When you pass an array to a function, the address of the first element gets passed like a pointer, and inside the function there is no more information about the size of the array. (Before the compiler itself could infer the size because the array was declared in the scope, but a function can be called from any number of places)
If you want to do something like that you would either have to use a container class, such as a vector, or you could pass a second argument into the function stating the size of the array. Another way is to have some sort of end point in your array, such as is the case with c-strings, for example a null value.
I am trying to return an array Data Member from one smaller 2D Array Object, and trying to insert the array into a larger 2D array object. But when attempting this, I came into two problems.
First problem is that I want to return the name of the 2D array, but I do not know how to properly syntax to return 2D Array name.
This is what my 2D Array data member looks like
private:
int pieceArray[4][4];
// 2D Smaller Array
and I want to return this array into a function, but this one causes a compiler error:
int Piece::returnPiece()
{
return pieceArray; //not vaild
// return the 2D array name
}
I tired using this return type and it worked:
int Piece::returnPiece()
{
return pieceArray[4][4];
}
But I am unsure if this is what I want, as I want to return the array and all of it's content.
The other problem is the InsertArray() function, where I would put the returnPiece() function in the InsertArray()'s argument.
The problem with the InsertArray() is the argument, heres the code for it:
void Grid::InsertArray( int arr[4][4] ) //Compiler accepts, but does not work
{
for(int i = 0; i < x_ROWS ; ++i)
{
for (int j = 0; j < y_COLUMNS ; ++j)
{
squares[i][j] = arr[i][j];
}
}
}
The problem with this is that it does not accept my returnPiece(), and if i remove the "[4][4]", my compiler does not accept.
Mostly all these are syntax errors, but how do I solve these problems?
Returning the whole pieceArray in returnPiece()
The correct syntax for the argument in InsertArray()
The argument of InsertArray() accepting the returnPiece()
These 3 are the major problems that I need help with, and had the same problem when I attempt to use the pointer pointer method. Does anyone know how to solve these 3 problems?
When passing your array around, you have to decide whether or not you want to make a copy of the array, or if you just want to return a pointer to the array. For returning arrays, you can't (easily) return a copy - you can only return a pointer (or reference in C++). For example:
// Piece::returnPiece is a function taking no arguments and returning a pointer to a
// 4x4 array of integers
int (*Piece::returnPiece(void))[4][4]
{
// return pointer to the array
return &pieceArray;
}
To use it, call it like so:
int (*arrayPtr)[4][4] = myPiece->returnPiece();
int cell = (*arrayPtr)[i][j]; // cell now stores the contents of the (i,j)th element
Note the similarity between the type declaration and using it - the parentheses, dereferencing operator *, and brackets are in the same places.
Your declaration for Grid::InsertArray is correct - it takes one argument, which is a 4x4 array of integers. This is call-by-value: whenever you call it, you make a copy of your 4x4 array, so any modification you make are not reflected in the array passed in. If you instead wanted to use call-by-reference, you could pass a pointer to an array instead:
// InsertArray takes one argument which is a pointer to a 4x4 array of integers
void Grid::InsertArray(int (*arr)[4][4])
{
for(int i = 0; i < x_ROWS; i++)
{
for(int j = 0; j < y_COLUMNS ; j++)
squares[i][j] = (*arr)[i][j];
}
}
These type declarations with pointers to multidimensional arrays can get really confusing fast. I recommend making a typedef for it like so:
// Declare IntArray4x4Ptr to be a pointer to a 4x4 array of ints
typedef int (*IntArray4x4Ptr)[4][4];
Then you can declare your functions much more readable:
IntArray4x4Ptr Piece::returnPiece(void) { ... }
void Grid::InsertArray(IntArray4x4Ptr arr) { ... }
You can also use the cdecl program to help decipher complicated C/C++ types.
It seems like you need to read up more on pointers in C++ and on pass by reference vs. pass by value.
Your returnPiece method is defined as returning the value of a single cell. Given the index (e.g., [4][4]) you return a copy of the contents of that cell, so you won't be able to change it, or more correctly, changing it would change the copy.
I'm sure someone will give you the correct syntax, but I would really recommend learning this stuff since otherwise you may use the code that you do get incorrectly.
Here is how I would do it:
class Array {
public:
Array() {
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
(*this)(i, j) = 0;
}
}
}
int &operator()(int i, int j)
{
return pieceArray[i][j];
}
private:
int pieceArray[4][4];
};
You can then do something like:
Array x; // create 4x4 array
x(1, 2) = 3; // modify element
Trouble with Adam Rosenfield's arrayPtr in that Piece::pieceArray can change out from under you. (Copy-by-reference vs Copy-by-value.)
Copy-by-value is inefficient. But if you really want to do it, just cheat:
struct FOO { int piece [4][4]; };
FOO Piece::returnPiece()
{
FOO f;
memcpy( f.piece, pieceArray, sizeof(pieceArray) );
return f;
}
void Grid::InsertArray( const FOO & theFoo )
{
// use theFoo.piece[i][j]
}
Of course, a better, more Object-Oriented solution, would be to have returnPiece() create and return a Piece or Array object. (As Juan suggested...)
Hello I have made class gabka and a function f1 to which I would like to pass an array of pointers to fill this array with gabka objects but I get weird error. How to correct it?
error:
cannot convert from gabka to int
the code :
#include <iostream>
using namespace std;
const int n = 4;
class gabka {
public:
float woda;
gabka(){
woda = 0;
}
void f1(gabka t[n]){
for(int i = 0; i < n; i++){
t[i] = new gabka();
}
}
};
int main() {
gabka** t = new gabka*[n];
return 0;
};
Your f1 function takes an array of gabka objects, and you are trying to assign a gabka* to each one of them. You need to decide what you want to pass to f1. For example,
void f1(gabka t[n]){
for(int i = 0; i < n; i++){
t[i] = gabka(); // assign default constructed gabkas
}
}
or
void f1(gabka* t[n]){
for(int i = 0; i < n; i++){
t[i] = new gabka();
}
}
In the latter case, bear in mind you have to call delete on all the elements of t.
It isn't clear what you are intending to do with your code, but, as a general rule, in C++ you should avoid raw pointers and manual dynamic allocation. Prefer standard library containers over dynamically allocates arrays or arrays of dynamically allocated objects.
t[i] = new gabka();
t is an array of gabka, not an array of gabka*.
Either change the declaration of t to be an array of gabka*, or fill it with gabka instances.
I am trying to return an array Data Member from one smaller 2D Array Object, and trying to insert the array into a larger 2D array object. But when attempting this, I came into two problems.
First problem is that I want to return the name of the 2D array, but I do not know how to properly syntax to return 2D Array name.
This is what my 2D Array data member looks like
private:
int pieceArray[4][4];
// 2D Smaller Array
and I want to return this array into a function, but this one causes a compiler error:
int Piece::returnPiece()
{
return pieceArray; //not vaild
// return the 2D array name
}
I tired using this return type and it worked:
int Piece::returnPiece()
{
return pieceArray[4][4];
}
But I am unsure if this is what I want, as I want to return the array and all of it's content.
The other problem is the InsertArray() function, where I would put the returnPiece() function in the InsertArray()'s argument.
The problem with the InsertArray() is the argument, heres the code for it:
void Grid::InsertArray( int arr[4][4] ) //Compiler accepts, but does not work
{
for(int i = 0; i < x_ROWS ; ++i)
{
for (int j = 0; j < y_COLUMNS ; ++j)
{
squares[i][j] = arr[i][j];
}
}
}
The problem with this is that it does not accept my returnPiece(), and if i remove the "[4][4]", my compiler does not accept.
Mostly all these are syntax errors, but how do I solve these problems?
Returning the whole pieceArray in returnPiece()
The correct syntax for the argument in InsertArray()
The argument of InsertArray() accepting the returnPiece()
These 3 are the major problems that I need help with, and had the same problem when I attempt to use the pointer pointer method. Does anyone know how to solve these 3 problems?
When passing your array around, you have to decide whether or not you want to make a copy of the array, or if you just want to return a pointer to the array. For returning arrays, you can't (easily) return a copy - you can only return a pointer (or reference in C++). For example:
// Piece::returnPiece is a function taking no arguments and returning a pointer to a
// 4x4 array of integers
int (*Piece::returnPiece(void))[4][4]
{
// return pointer to the array
return &pieceArray;
}
To use it, call it like so:
int (*arrayPtr)[4][4] = myPiece->returnPiece();
int cell = (*arrayPtr)[i][j]; // cell now stores the contents of the (i,j)th element
Note the similarity between the type declaration and using it - the parentheses, dereferencing operator *, and brackets are in the same places.
Your declaration for Grid::InsertArray is correct - it takes one argument, which is a 4x4 array of integers. This is call-by-value: whenever you call it, you make a copy of your 4x4 array, so any modification you make are not reflected in the array passed in. If you instead wanted to use call-by-reference, you could pass a pointer to an array instead:
// InsertArray takes one argument which is a pointer to a 4x4 array of integers
void Grid::InsertArray(int (*arr)[4][4])
{
for(int i = 0; i < x_ROWS; i++)
{
for(int j = 0; j < y_COLUMNS ; j++)
squares[i][j] = (*arr)[i][j];
}
}
These type declarations with pointers to multidimensional arrays can get really confusing fast. I recommend making a typedef for it like so:
// Declare IntArray4x4Ptr to be a pointer to a 4x4 array of ints
typedef int (*IntArray4x4Ptr)[4][4];
Then you can declare your functions much more readable:
IntArray4x4Ptr Piece::returnPiece(void) { ... }
void Grid::InsertArray(IntArray4x4Ptr arr) { ... }
You can also use the cdecl program to help decipher complicated C/C++ types.
It seems like you need to read up more on pointers in C++ and on pass by reference vs. pass by value.
Your returnPiece method is defined as returning the value of a single cell. Given the index (e.g., [4][4]) you return a copy of the contents of that cell, so you won't be able to change it, or more correctly, changing it would change the copy.
I'm sure someone will give you the correct syntax, but I would really recommend learning this stuff since otherwise you may use the code that you do get incorrectly.
Here is how I would do it:
class Array {
public:
Array() {
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
(*this)(i, j) = 0;
}
}
}
int &operator()(int i, int j)
{
return pieceArray[i][j];
}
private:
int pieceArray[4][4];
};
You can then do something like:
Array x; // create 4x4 array
x(1, 2) = 3; // modify element
Trouble with Adam Rosenfield's arrayPtr in that Piece::pieceArray can change out from under you. (Copy-by-reference vs Copy-by-value.)
Copy-by-value is inefficient. But if you really want to do it, just cheat:
struct FOO { int piece [4][4]; };
FOO Piece::returnPiece()
{
FOO f;
memcpy( f.piece, pieceArray, sizeof(pieceArray) );
return f;
}
void Grid::InsertArray( const FOO & theFoo )
{
// use theFoo.piece[i][j]
}
Of course, a better, more Object-Oriented solution, would be to have returnPiece() create and return a Piece or Array object. (As Juan suggested...)