0

I have a struct, which, depending on user inputs at runtime, will either require a 1D array or a 3D array. It will never need both. Right now, I have it set up like in the sample code below, with separate variables that can point to either a 1D array, or a 3D array. I would like to have just one variable in the struct that can point to either a 1D array or a 3D array, where the dimension is set at runtime. I have intermediate knowledge of C, and am a beginner with C++. I'd be willing to accept an answer based on C++ concepts but only if there is no slowdown (or negligible slowdown) compared to using C when iterating over the values. If it's a 3D array, then the for loops that access and change the array's values are the biggest bottleneck in my code. Once the array is set up, I won't need to change the dimension or size of the array.

Is there a way to do this, or should I just settle for always having an extraneous variable in my struct?

#include <iostream> using namespace std; typedef struct { int dim; int *one_d_arr; int ***three_d_arr; } Struct; int main() { int count = 0; int *arr1 = (int*) malloc(2 * sizeof(int)); arr1[0] = 0; arr1[1] = 1; int ***arr3 = (int***) malloc(2 * sizeof(int**)); for (int i=0; i<2; i++) { arr3[i] = (int**) malloc(2 * sizeof(int*)); for (int j=0; j<2; j++) { arr3[i][j] = (int*) malloc(2 * sizeof(int)); for (int k=0; k<2; k++) { arr3[i][j][k] = count++; } } } Struct s; s.one_d_arr = NULL; s.three_d_arr = NULL; cout << "Enter number of dimensions: "; cin >> s.dim; if (s.dim==1) { s.one_d_arr = arr1; cout << s.one_d_arr[0] << ", " << s.one_d_arr[1] << endl; } else if (s.dim==3) { s.three_d_arr = arr3; cout << s.three_d_arr[0][0][0] << ", " << s.three_d_arr[0][0][1] << endl; cout << s.three_d_arr[0][1][0] << ", " << s.three_d_arr[0][1][1] << endl; cout << s.three_d_arr[1][0][0] << ", " << s.three_d_arr[1][0][1] << endl; cout << s.three_d_arr[1][1][0] << ", " << s.three_d_arr[1][1][1] << endl; } else { cout << "Must enter 1 or 3" << endl; } } 
21
  • 2
    I think you might want to use new (instead of malloc) in C++. Commented Feb 10, 2016 at 17:53
  • 6
    A pointer is not an array! You don't have a 3D array in your question. Being a 3-star programmer in C or C++ is not a compliment! Commented Feb 10, 2016 at 17:54
  • 2
    In C++ the type of a variable must be fixed at compile time. And the dimension of an array is part of its type. So literally, the answer is no. However, practically speaking there's any number of ways to deal with this. (1) Always make it a 1D array (or better a vector maybe!), and use a boolean flag to indicate how you are supposed to handle it / traverse it. (2) Have two different structs, and select between them when you get the data. Commented Feb 10, 2016 at 17:57
  • 1
    Firstly, code using your type would decide at compile time how to use it, but the syntax for different dimensions differs. So, what happens if you use a 3D array with the 1D syntax? Since it's the same type, the compiler will not be able to detect this. I think that your very approach is flawed, maybe taking a step back and describing your problem would help. Further, please read a C++ tutorial. Your code makes a bunch of mistakes that proper learning should be able to avoid. Commented Feb 10, 2016 at 17:57
  • 2
    set array size at runtime == std::vector Commented Feb 10, 2016 at 18:14

2 Answers 2

1

My recommendation is to use two different types here, instead of a single struct. Using an abstract base class, you can make both subclasses conform to a single interface, but they would have different underlying behavior. A very basic example:

class ArrayBase { int dim; public: // This function is pure virtual, which means it's impossible to // instantiate an instance of ArrayBase. Any class that inherits from // ArrayBase must implement printArray(). virtual void printArray() = 0; } class Array1D : public ArrayBase { int* array; void printArray() { // some code to print this one-dimensional array } } class Array3D : public ArrayBase { int*** array; void printArray() { // some code to print this three-dimensional array } } 

Later, when you need to use the array, you can dynamically allocate the type you need, like this:

ArrayBase* inputArray; // if the user wants a 1D array inputArray = new Array1D(); // if the user wants a 3D array inputArray = new Array3D(); // this will call the appropriate function to print the array inputArray->printArray(); 

If you really want to have a single type, using boost::any is one way to condense your two array pointers into one. I would not recommend this approach, but it would work.

Sign up to request clarification or add additional context in comments.

1 Comment

I accepted this answer because it points me in a good direction to restructure my code. I'll spend some time learning about classes and other C++ containers before implementing something like this. Thanks!
1

One of the juicy things about the C/C++ pointers is the existence of void pointers. A void pointer can point to anything you want, from int to int ***. So you can simply use the following code:

#define CAST1(arr) ((int *)arr) #define CAST3(arr) ((int ***)arr) #define CAST(arr,i) CAST##i(arr) typedef struct { int dim; void *arr; } Struct; int main() { Struct s; cin >> s.dim; int count = 0; if (s.dim == 1){ s.arr = malloc(2 * sizeof(int)); CAST(s.arr, 1)[0] = 0; CAST(s.arr, 1)[1] = 1; } else if (s.dim == 3){ s.arr = malloc(2 * sizeof(int ***)); for (int i = 0; i < 2; i++){ CAST(s.arr, 3)[i] = (int **) malloc(2 * sizeof(int **)); for (int j = 0; j < 2; j++){ CAST(s.arr, 3)[i][j] = (int *)malloc(2 * sizeof(int *)); for (int k = 0; k < 2; k++){ CAST(s.arr, 3)[i][j][k] = count++; } } } } if (s.dim == 1) { cout << CAST(s.arr, 1)[0] << ", " << CAST(s.arr, 1)[1] << endl; } else if (s.dim == 3) { cout << CAST(s.arr, 3)[0][0][0] << ", " << CAST(s.arr, 3)[0][0][1] << endl; cout << CAST(s.arr, 3)[0][1][0] << ", " << CAST(s.arr, 3)[0][1][1] << endl; cout << CAST(s.arr, 3)[1][0][0] << ", " << CAST(s.arr, 3)[1][0][1] << endl; cout << CAST(s.arr, 3)[1][1][0] << ", " << CAST(s.arr, 3)[1][1][1] << endl; } else { cout << "Must enter 1 or 3" << endl; } system("pause"); return 0; } 

1 Comment

Thank you, this is good to know. However, based on the comments, it looks like the best answer will be a more major overhaul of my coding concepts. I'll wait for an answer that shows me how to clean up my code, but if none comes up, I'll select this one.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.