简体   繁体   中英

arrays in C++: can you explain what this line of code is doing?

I need to modify some C++ code, but since I'm relatively new to the language I'm having trouble understanding some expressions.

I have a function

void func(double m[2][12],double n[2][3])

that is invoked from inside an other function like this

double A[12];
double B[6];
(...)
func( (double (*)[12])A, (double (*)[3])B )

The last line of code is casting a 1D array to a 2D one, but what is happening there exactly? Can I use the same technique to convert a 1D array into a 2D, like this?:

double A[12];
double B[6];
(double (*)[12])A[0][5] = 5;

What is happening is exactly what you mentioned - the caller is casting the 1 dimensional arrays into in order to pass them to func() , which has two pointer-to-2D-array parameters. Since 2D arrays in C are just 'arrays of arrays' (ie there are no pointers involved) this translation is certainly possible. For example, this 1D array:

int oneDimensionalArray[] = { 0, 1, 2, 3 };

and this 2D array:

int twoDimensionalArray[] = { { 0, 1 }, 
                              { 2, 3 } };

Have exactly the same memory layout. You could coerce the types into matching by using crazy-town casting operations like the one in your question.

And as to your second question, yes, you can do that, but you need one more set of parentheses:

((double (*)[12])A)[0][5] = 5;

I don't recommend writing code with this kind of crazy stuff going on, though. Where did the program come from?

The casting is not from 1D to 2D array but to a pointer to a static 1D array!

double m1[12];

means a variable m1 of type ' static array of 12 doubles '

double m2[2][12];

means a variable m2 of implicitly inferred type ' static array of 2 elements of type "static array of 12 doubles" ' When static arrays are used as function arguments they are degenerated to pointers of the type of the array elements. Therefore, for array m1, above we can use any the following functions

f11(double arg[12]) {}  
f12(double * arg) {}

both can be called with any pointer to double or any array of double elements like:

double a=0.0;
double b[10];
f11(&a); f11(b);
f12(&b); f12(b);

Similarly, for array m2 (above), the type of a single element is 'double[12]'. Thus we can use any the following functions:

f21(double arg[2][12]) {}  
f22(double * arg[12]) {}

which can be called with any pointer to ' array of 12 doubles ' or any array of elements of type ' array of 12 doubles ' like:

double c[12];
double d[6][12];
f21((double (*)[12])c); f21(d);   
f22((double (*)[12])c); f22(d);  

Now for your code, the function void func(double m[2][12],double n[2][3]) actually expects a pointer to ' static array of 12 doubles ' and a pointer to ' static array of 3 doubles '. Thus the call that you see

double A[12];
double B[6];
func( (double (*)[12])A, (double (*)[3])B )

actually casts A (which is degenerated to pointer to double) to pointer to ' static array of 12 doubles ' and casts B (which is also degenerated to pointer to double) to pointer to ' static array of 3 doubles ' Because all of the given arrays work with doubles and static arrays are represented sequentially in memory such casting may not cause logical errors.

The declaration

void func(double m[2][12],double n[2][3])

is equivalent to

void func(double (*m)[12],double (*n)[3])

ie the first array dimension does not matter. The top-level array type decays to pointer type, thus turning a 2D-array parameter declaration into a pointer-to-1D-array parameter declaration.

This function can be called with these arguments

double A[12];
double B[3]; /* note 3 instead of 6 */

The proper way to do it would be simply

func(&A, &B);

Note, no cast is necessary, just an application of & operator, given that A and B have have proper types ( double[12] and double[3] in this example). In this case inside the function one can only access m[0][i] and n[0][i] , but not m[1][i] or n[1][i] , since these do not exist.

In your example the author of the code took some truly bizarre steps.

Firstly, apparently the author did not realize that the first argument can be expressed as &A , no cast necessary, since A is declared with proper type.

Secondly, it is entirely not clear why B is declared as array of size 6 , when the function expects an array of size 3 . In order to force this argument into that function an explicit cast is indeed required.

The way both arguments are passed is just an example of bad code.


As for converting 1D array into 2D array... Just remember that in C and C++ any object of type T can be interpreted as an 1D array with 1 element of type T . One just needs to apply the & operator to the object. For example

int i = 0;
(&i)[0] = 5; /* access `&i` as `int[1]` array */
assert(i == 5);

The same thing can be done if the object in question is itself an array. For example, 1D array can be accessed as a 2D array with first size equal to 1

int a[10] = { 0 };
(&a)[0][8] = 42; /* access `&a` as an `int[1][10]` array */
assert(a[8] == 42);

That's exactly what happens in your example, except that the proper way to do it is to apply the & operator to the original object. The author of the code achieves the same effect using a cast. Cast is not the right way to do it. It is not guaranteed to work, it just happens to have the same effect in practice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM