How to point to an array of arrays in C++? - c++

I have a 4D array and i would like to create a function that i could use to refer to a 2D array inside the 4D array and read its data by appending square braces with indexes to the function call.
int numbers[2][3][4][4] = {
{
{{1,2,3,4}, {4,3,2,1}, {6,7,8,9}, {9,8,7,6}},
{{0,1,0,1}, {1,0,1,0}, {1,1,0,0}, {0,0,1,1}},
{{5,4,1,8}, {4,2,5,1}, {7,2,5,8}, {4,2,5,1}}
},
{ /* same stuff */ }
};
If i wanted to access just {4,2,5,1} for example, i could create a function that returns a pointer to the first int of that array (&number[0][2][1][0]), and access the elements with func()[1], func()[2], func()[3], well... you know that.
int* func() {
return &numbers[0][2][1][0];
}
// func()[2] => 5
But how could i create a function that returns a reference to numbers[0][2], so for example i could do func()[1][0] and it would return 5?
It seems i should work up my understanding of C/C++ pointers, references and arrays, but i will also appreciate "std" and C++11-specific solutions (using some cool std::class over standard arrays). Thanks :)

In C++ the best approach would be to return a reference to a 2D array
int (&func())[4][4] {
return numbers[0][2];
}
Now you can access it as
func()[i][j]
This fully preserves the 2D array type and keeps the natural access syntax.
If you insist on returning a pointer specifically, then you can return a pointer to the first 1D array in that 2D array
int (*func())[4] {
return numbers[0][2];
}
which will preserve the access syntax as
func()[i][j]
but, as you can see, the price of this is decay of 2D array type to 1D array type.
Or, as has already been suggested, you can return a pointer to a 2D array
int (*func())[4][4] {
return &numbers[0][2];
}
but in this case you will have to remember to access that 2D array elements as
(*func())[i][j]
i.e. it preserves the 2D array type, but forces you to use a more convoluted access syntax.
For this reason reference seems to be the best approach (as long as you insist on working with raw arrays), combining the "best" features of both pointer-based approaches.
P.S. Typedefing the 2D array type will produce much more readable function declarations. E.g. if you do
typedef int Numbers2D[4][4];
then the first variant will turn into
Numbers2D &func() {
return numbers[0][2];
}

Related

Trying to pass 3d array to function c++ [duplicate]

So I have several questions. First how do I pass a 3D array into a function. I need to pass the whole array as the function is to run a loop to output the contents of the array to a file. This is what I currently have
int array[5][3][3]
void function(int a[5][3][3])
{
//...
}
void function(array); //or void function(array[5][3][3]);
I have found a way to make it work using pointers to the array, however I have asked my teacher and he does not want us to use pointers.
My second question is if I plan to modify a global variable inside a function, I do not need to pass it to the function? I can just use it inside the function as I would inside main?
Yet another problem I am having now is passing a single value from an array into a function.
In a loop I need to pull a value from an array[i][j][2] (i and j being indexes of an outer and inner loop) and pass it to a function to evaluate whether or not it is greater than 90. This is for a school assignment, so understand there are certain specifications I have to meet. (Like not using pointers, and passing a whole array, and passing one value from an array, because as a class we have not yet learned how to use pointers)
Your code is correct, but actually there no such thing as an array parameter in C++ (or in C). Silently the compiler will convert your code to the equivalent pointer type, which is
int array[5][3][3];
void function(int (*a)[3][3])
{
...
}
So although your professor told you not to use pointers, actually you cannot avoid them, because there's really no such thing as an array type parameter in C++.
Second question, the only point of globals is that you can refer to them anywhere, so no need to pass them as parameters.
For passing complex arrays I prefer to wrap them in a structure:
struct array {
int a[5][3][3];
};
void function(struct array *a) ...
This avoids a lot of pitfalls with trying to pass arrays as function arguments.
you might use a pointer instead int ***a
int array[5][3][3]
void dummy(int d[][3][3])
{
d[1][1][1] = 0;
}
you may also pass it as a void * then make
int array[5][3][2]
void function(int* b)
{
int i=0;
int j=1;
int k=2;
l[ k*(3*2)+j*(2)+i ] = 9;
}
function((int*) array);

Reference to a subarray within an array

I was trying to figure out how can I create sub arrays from within a larger array and got a piece of code here and started using it.
I created an array of ints
int arr[10];
for(int h=0;h<10;h++)
{
arr[h]=20+h;
}
Now say I want a sub-array (of 4 ints) within the same larger array
int (&arrOnly4Elements)[4]=(int (&)[4])(*arr);
It works well and does what I want.
While I understand references and that they point to actual objects, What I am not able to understand how the above code works.
why do we need the braces to surround &arrOnly4Elements
Also, can anyone explain me the RHS (int (&)[4])(*arr); in detail step by step manner.
cdecl.org translates it for you:
int (&arrrOnly4Elements)[4]: declare arrrOnly4Elements as reference to array 4 of int
int &arrrOnly4Elements[4]: declare arrrOnly4Elements as array 4 of reference to int
As NathanOliver pointed out, C++20 introduces std::span. You should take a look at it (also compare this SO question). A std::span is a templated view into an array/contiguous sequence of objects. It consists of a pointer and a size. It makes accessing arrays and sub arrays convenient (allows range based for) and safe (keeps track of the size).
int arr[10];
std::span<int> arr_span = arr;
std::span<int,4> arr_subspan1 = arr_span.first<4>();
std::span<int> arr_subspan2 = arr_span.first(4);
If you cannot yet switch to C++20 you might consider checking GSL which provides a gsl::span which was lately aligned to match std::span.

error : no match for 'operator[]'

I am solving a problem in which I m trying to pass address a 2-D array of a structure and trying to manipulate it but, whenever I use [] operator to access the array elements , I get a compile time error:
no match for 'operator[]'
in my codeblocks IDE.
#include <iostream>
using namespace std;
typedef struct mat
{
int data;
int flag;
} cell;
int mat(cell *);
int main()
{
int m,n;
cin>>m>>n;
cell game[n][m];
cout<<"The length of matrix is "<<mat(&game[0][0]);
}
int mat(cell *arr)
{
return (sizeof(arr[0])/sizeof(arr[0][0]));
}
cell game[n][m];
This is not legal C++. You are using a compiler-specific extension. At this point I advise you against using any compiler-specific extensions. Use standard C++ only. In standard C++ there are no Variable Length Arrays. Don't use arrays in your C++ programs. To get proper variable length array functionality. You should use std::vector instead, like this:
std::vector<std::vector<cell>> game;
Further,
&game[0][0]
is not an address of a 2D array. This is an address of the first element in the array. It contains no information about the number of elements. It is lost forever. You cannot pass it to some function and expect the size of the array to be recovered. To get proper array functionality with a built-in size function, use std::vector.
Last but not least,
(sizeof(arr[0])/sizeof(arr[0][0]));
arr is a cell*. arr[0] is a cell. arr[0][0] is invalid because a cell is neither an array not a pointer, nor it has a custom [] operator defined. In any case you cannot use sizeof to recover the number of elements in the array from a pointer to its first element. To get proper array functionality with a built-in size function, use std::vector.
Two things:
Your function takes a pointer to cell but since you're treating it like a 2D array, you should probably change your signature to either accept a cell ** arr or a cell arr[m][] where m is the (fixed) size of the array and must be specified. Note that these data structures have fundamentally different representations in memory - they just have the same [][] syntax for accessing elements.
You can't use the sizeof function to determine the length of an array if you pass it as a pointer to an elem. You will need to pass the dimensions along with your array, like this:
int mat(cell **arr, int m, int n);
The definition being given basically says that your class doesn't define the operator [], meaning you can't use the syntax you are trying to use.

How to pass a two dimensional array to a function in c++

I am trying to pass an array (2d) to a function as an parameter.
I have a code as follows:
int main()
{
float T[100][100];
void set_T(float T[][]);
}
void set_T(float T1[][])
{
for (int i =0 ; i<90;i++)
{
for(int j =0 ;j <90;j++)
{
T1[i][j] = 3;
}
}
}
I am not sure how to pass array to a function ...I am getting lot of errors. Can any one help please.
There are two issues here:
C does not support 2D arrays, only arrays of arrays or arrays of pointers to arrays, neither of which is quite the same thing as a 2D array
C does not allow passing arrays to functions as arguments, only pointers into arrays (generaly, you use a pointer to an array's 0th element, since that's what the array's name ends up being so indexing off of such a pointer looks just like an array access)
So because of the first problem, you have to decide how you're going to represent a 2D array -- either an array of arrays, or an array of pointers to arrays. If you go the first route, your code ends up looking like:
void set_T(float (*T1)[100]) {
... do stuff with T1[i][j] ...
}
int main() {
float T[100][100];
set_T(T);
}
Here, you've declared T to be an array of 100 arrays of 100 floats, and set_T takes a pointer to arrays of 100 floats as its argument. You pass 'T' directly to set_T, as the language treats array names as pointers to their 0th element.
If instead you want to use an array of pointers to arrays, you end up with something like:
void set_T(float **T1) {
... do stuff with T1[i][j] ...
}
int main() {
float *T[100];
float space[100*100];
for (int i = 0; i < 100; i++)
T[i] = space + i*100;
set_T(T);
}
The disadvantage here is that you need to allocate space for all of the second-level arrays and manually initialize all the first-level pointers to point at them. The advangtage is that the sizes of the second level arrays is not part of the type of the argument passed to set_T, so you can more easily deal with variable-sized arrays.
Of course, if you're really using C++ and not C, you should not be using C arrays at all -- you should be using std::vector or std::array instead -- both of which share the C array 1D only issue, so you need a vector of vectors or an array of arrays (or conceivably a vector of arrays or an array of vectors)
void set_T(float (&T)[100][100]);
Just call it like this:
int main ()
{
float T[100][100];
set_T(T);
}
And as #suddnely_me said, the type of T1 in the function declaration need to be float**.

How can I pass an array by reference to a function in C++?

I have the following program where two variables are to be passed by reference to a function where their values will be determined based on external factors before being returned to main() so that they can be used by other functions. The first variable I am trying to pass is an int, and that goes fine, but the other is an array of strings, which is causing me some problems.
I've done enough research into this to know that you can't have an array or references (though I've yet to figure out why) and I was wondering if anyone could help me figure out how to do this? The various methods I've tried have all resulted in segmentation faults.
NB: The code below has the array being passed by value since I just don't know what to write for it.
Update: I'm required to use an array for my coursework. Some other data structure, such as the vector that has been suggested, would be great, but I have to use specific structures.
void initialise_existing_devices(int& no_of_existing_devices, string existing_devices[100]);
int main()
{
int no_of_existing_devices = 0;
string existing_devices[100];
initialise_existing_devices(no_of_existing_devices, existing_devices[100]);
}
void initialise_existing_devices(int& no_of_existing_devices, string existing_devices[100])
{
string line;
ifstream DeviceList;
DeviceList.open("devices/device_list");
while (true)
{
getline(DeviceList, line, '\n');
if (DeviceList.eof())
{
break;
}
++ no_of_existing_devices;
}
DeviceList.close();
DeviceList.open("devices/device_list");
for (int i = 0; i < no_of_existing_devices; i ++)
{
getline(DeviceList, line, '\n');
existing_devices[i] = line;
}
}
A reference to an array looks like:
void f(std::string (&a)[N]) { }
where a is the name of the parameter and N is the number of elements in the array.
However, usually in C++ you don't pass an array by reference (you can; it's just not common). Other options include:
Pass a pointer to the initial element of the array; in this case, consider passing the size of the array as a second argument to the function.
Use a std::vector<std::string> or a std::array<std::string, N> instead and pass it by reference (you can also find the array psuedo-container in Boost; barring that, consider writing your own. If you take a look at the Boost source code, it's quite simple and straightforward).
Pass a pair of iterators (begin and end) to the function and use them to manipulate the range.
The last option is the most idiomatic C++ approach; it is also the most generic because you can use any type of container, including arrays, standard library containers, or containers that you've written yourself.
Since you are actually trying to use the parameter as an "out" parameter, it's probably better just to return a std::vector<string> or a std::array<string, 100> containing the results; this is much cleaner.
this line is not doing what you are expecting:
initialise_existing_devices(no_of_existing_devices, existing_devices[100])
hint: array index, 100 ...
I would suggest that you use a std::vector<std::string> rather than the array and pass that by reference.
EDIT: okay, given the update:
can you use a struct? If so, you can wrap the array in a struct:
struct string_array
{
string data[100];
};
and then define an instance of this in main, and pass that by reference? I'm sure you can fill in the details.. :)
You should use a std::vector<T> for heap-managed arrays or a boost::/std::array<T, N> for stack-based arrays. These objects will hold their own size.
This is one of those things C++ has in common with C. Arrays are not passed by value. They're demoted to pointers to their first elements. The array syntax in the function parameters is essentially just a comment. You can tell by doing a sizeof(existing_devices) inside your function call. So the answer to your question is that you're already doing it.
You can use templates, like so:
template <size_t size>
initialise_existing_devices(int& no_of_existing_devices, string (&existing_devices)[size])
{
}
or you can do:
typedef string hundred_strings[100];
initialise_existing_devices(int& no_of_existing_devices, hundred_strings &existing_devices)
{
}
For the actual argument, use just the array name, which represents the address of the array:
initialise_existing_devices(no_of_existing_devices, existing_devices);
For the parameter, use this for a constant pointer to the array:
void initialise_existing_devices(int& no_of_existing_devices, string existing_devices[])
That said, using a std::vector as the return type or a reference parameter would avoid the need to guess the number of devices before the call.