Casting between 1D and 2D arrays

B

Boon

Hello everyone,

Consider a "set" of M=N*N integers. In some parts of my program, I want
to handle this set as a 1D array of M integers. In other parts of my
program, I want to handle this set as a 2D matrix of N*N integers.

Is the following code 100% legal C89 code ?

#include <assert.h>
#define N 9
typedef int row_t[N];

void compare(int *array)
{
int i, j;
row_t *matrix = (row_t *)array;
for (i=0; i < N; ++i)
for (j=0; j < N; ++j)
{
assert( &matrix[j] == &array[i*N+j] );
assert( matrix[j] == array[i*N+j] );
}
}

int main(void)
{
int i;
int foo[N*N];
for (i=0; i < N*N; ++i) foo = 7*i+11;
compare(foo);
return 0;
}

Can I be sure that the asserts will never fail ?

Regards.
 
B

Ben Bacarisse

Boon said:
Consider a "set" of M=N*N integers. In some parts of my program, I
want to handle this set as a 1D array of M integers. In other parts of
my program, I want to handle this set as a 2D matrix of N*N integers.

Is the following code 100% legal C89 code ?

I think so, yes. I was hoping someone else would stick their neck out
first, but it may as well be me! Note that 100% legal != 100%
portable -- I see one possible gotcha.
#include <assert.h>
#define N 9
typedef int row_t[N];

void compare(int *array)
{
int i, j;
row_t *matrix = (row_t *)array;

The only issues seems to be to be this conversion. The standard says
very little about it except that the pointer must be properly aligned.
I think it is possible for row_t to have stricter alignment
requirements than int, but this problem would vanish if the pointer
passed were malloced rather than being converted from a declared array
in main (malloced space must be correctly aligned for any use).

An implementation that requires such an alignment would be odd indeed
since it would only apply to certain sizes of row_t, but I can't
persuade myself that the standard prohibits it.
for (i=0; i < N; ++i)
for (j=0; j < N; ++j)
{
assert( &matrix[j] == &array[i*N+j] );
assert( matrix[j] == array[i*N+j] );
}
}

int main(void)
{
int i;
int foo[N*N];
for (i=0; i < N*N; ++i) foo = 7*i+11;
compare(foo);
return 0;
}

Can I be sure that the asserts will never fail ?


That alone is not enough. UB (if present) could cause the asserts to
"pass" when in fact they "fail". I think you are OK if the array is
malloced or you target implementations have no bizarre alignment
requirements for arrays of arrays.
 
D

danmath06

void compare(int *array)
{
int i, j;
row_t *matrix = (row_t *)array;
for (i=0; i < N; ++i)
for (j=0; j < N; ++j)
{
assert( &matrix[j] == &array[i*N+j] );
assert( matrix[j] == array[i*N+j] );
}
}


That is legal.
Can I be sure that the asserts will never fail ?

As far as I know yes.
I think it is possible for row_t to have stricter alignment
requirements than int

I don't see why, as row_t is an array of ints. And as far as I know
ints don't NEED to be aligned on most architectures, it's just faster
when they are. On any architecture where alignment is necesary
foo[N*N] will be aligned when allocated on the stack.
 
B

Ben Bacarisse

(e-mail address removed) writes:

[You've lost the attributions and confused the quote levels. Please
try not to do that if you post again.]
void compare(int *array)
{
int i, j;
row_t *matrix = (row_t *)array;
for (i=0; i < N; ++i)
for (j=0; j < N; ++j)
{
assert( &matrix[j] == &array[i*N+j] );
assert( matrix[j] == array[i*N+j] );
}
}


That is legal.
Can I be sure that the asserts will never fail ?

As far as I know yes.
I think it is possible for row_t to have stricter alignment
requirements than int


I wrote this (but not the other stuff quoted with ">>").
I don't see why, as row_t is an array of ints. And as far as I know
ints don't NEED to be aligned on most architectures, it's just faster
when they are. On any architecture where alignment is necesary
foo[N*N] will be aligned when allocated on the stack.

Yes, but my point was about some array pointers needing more alignment
than others. I can't think why on earth this would ever be done, but
I can't show, using quotes from the standard, that it is never the
case.
 
P

Peter Nilsson

Boon said:
Hello everyone,

Consider a "set" of M=N*N integers. In some parts of my
program, I want to handle this set as a 1D array of M
integers. In other parts of my program, I want to handle
this set as a 2D matrix of N*N integers.

There are references to this in the FAQ.
Is the following code 100% legal C89 code ?

Whilst you are extremely unlikely to ever ancounter an
implementation where it will fail, it is not guaranteed
by the standard.
#include <assert.h>
#define N 9
typedef int row_t[N];

void compare(int *array)
{
   int i, j;
   row_t *matrix = (row_t *)array;

This conversion isn't as well defined as it could be.
There is no guarantee that the pointer array's first
element is suitably aligned for a pointer to an array
of 9 integers. Even if it happens to be, the only
thing you can reliably do with matrix is convert it
back to a pointer to int.
   for (i=0; i < N; ++i)
     for (j=0; j < N; ++j)
     {
       assert( &matrix[j] == &array[i*N+j] );
       assert(  matrix[j] ==  array[i*N+j] );
     }
}

int main(void)
{
   int i;
   int foo[N*N];
   for (i=0; i < N*N; ++i) foo = 7*i+11;
   compare(foo);
   return 0;
}

Can I be sure that the asserts will never fail?


Theoretically, No.
 
P

Peter Nilsson

Ben Bacarisse said:
Ben Bacarisse said:
OP wrote:
#include <assert.h>
#define N 9
typedef int row_t[N];
void compare(int *array)
{
  int i, j;
  row_t *matrix = (row_t *)array;
  for (i=0; i < N; ++i)
    for (j=0; j < N; ++j)
    {
      assert( &matrix[j] == &array[i*N+j] );
      assert(  matrix[j] ==  array[i*N+j] );
    }
}
I think it is possible for row_t to have stricter
alignment requirements than int...

I don't see why, as row_t is an array of ints. ...


...my point was about some array pointers
needing more alignment than others.  I can't think
why on earth this would ever be done, but I can't
show, using quotes from the standard, that it is
never the case.


Consider...

struct X { int a; int b; };

If it's reasonable for an implementation to assign this
2 * sizeof(int) byte alignment, especially if 2 ints fit
nicely into a machine word, then wouldn't it also be
reasonable for int[2] to have the same alignment
requirements?

[Of course, int[9] is harder to explain. ;-]
 
K

Keith Thompson

Peter Nilsson said:
Consider...

struct X { int a; int b; };

If it's reasonable for an implementation to assign this
2 * sizeof(int) byte alignment, especially if 2 ints fit
nicely into a machine word, then wouldn't it also be
reasonable for int[2] to have the same alignment
requirements?

It would be reasonable to align a declared object of type int[2] on a
2*sizeof(int) boundary, but the compiler could not in general
*require* such alignment. You can treat any 2-element "slice" of an
int array as an array of type int[2]:

typedef int two_ints[2];
/* just to make the declarations less confusing */
int arr[3];
two_ints *p = (two_ints*)&arr[1];
 
B

Ben Bacarisse

Keith Thompson said:
Peter Nilsson said:
Consider...

struct X { int a; int b; };

If it's reasonable for an implementation to assign this
2 * sizeof(int) byte alignment, especially if 2 ints fit
nicely into a machine word, then wouldn't it also be
reasonable for int[2] to have the same alignment
requirements?

It would be reasonable to align a declared object of type int[2] on a
2*sizeof(int) boundary, but the compiler could not in general
*require* such alignment. You can treat any 2-element "slice" of an
int array as an array of type int[2]:

typedef int two_ints[2];
/* just to make the declarations less confusing */
int arr[3];
two_ints *p = (two_ints*)&arr[1];

Yes. This is, in essence, what the OP's code does. The bit I am
missing is the wording that says resulting pointer *must be* properly
aligned.
 
B

Boon

Ben said:
Boon said:
Consider a "set" of M=N*N integers. In some parts of my program, I
want to handle this set as a 1D array of M integers. In other parts of
my program, I want to handle this set as a 2D matrix of N*N integers.

Is the following code 100% legal C89 code ?

I think so, yes. I was hoping someone else would stick their neck out
first, but it may as well be me! Note that 100% legal != 100%
portable -- I see one possible gotcha.
#include <assert.h>
#define N 9
typedef int row_t[N];

void compare(int *array)
{
int i, j;
row_t *matrix = (row_t *)array;

The only issues seems to be to be this conversion. The standard says
very little about it except that the pointer must be properly aligned.
I think it is possible for row_t to have stricter alignment
requirements than int, but this problem would vanish if the pointer
passed were malloced rather than being converted from a declared array
in main (malloced space must be correctly aligned for any use).

An implementation that requires such an alignment would be odd indeed
since it would only apply to certain sizes of row_t, but I can't
persuade myself that the standard prohibits it.

OK. What about casting the "other way" ?

Is it 100% legal and 100% portable ? :)

NB : I want to be able to use matrix[j] and array[i*N+j] interchangeably.

#include <assert.h>
#define N 9
int main(void)
{
int i, j;
int matrix[N][N];
int *array = (int *)matrix;

for (i=0; i < N; ++i)
for (j=0; j < N; ++j)
{
matrix[j] = i + j + 7;
assert( &matrix[j] == &array[i*N+j] );
assert( matrix[j] == array[i*N+j] );
}

return 0;
}

AFAICT, the cast is mandatory, if I want to do what I've described.
Is this a situation where a cast is acceptable ?

Regards.
 
P

Peter Nilsson

Keith Thompson said:
Peter Nilsson said:
Consider...

  struct X { int a; int b; };

If it's reasonable for an implementation to assign
this 2 * sizeof(int) byte alignment, especially if
2 ints fit nicely into a machine word, then wouldn't
it also be reasonable for int[2] to have the same
alignment requirements?

It would be reasonable to align a declared object of
type int[2] on a 2*sizeof(int) boundary, but the
compiler could not in general *require* such alignment.
You can treat any 2-element "slice" of an int array
as an array of type int[2]:

Chapter and verse please.

AFAICS, you can treat a pointer to an element as a
pointer to a sequence of elements up to the end of the
array (and one byte beyond), but I can't see that you
can impose a new effective type on that array.
    typedef int two_ints[2];
    /* just to make the declarations less confusing */
    int arr[3];
    two_ints *p = (two_ints*)&arr[1];

You're assuming that the explicit conversion holds, ergo
there's no way to enforce the alignment. I state that
the conversion needn't hold because alignment may be
enforced.

I might be missing something (again ;-), but where
does the standard give latitude for the array pointer
conversion that is not also given to the struct pointer?
 
B

Ben Bacarisse

Boon said:
Ben said:
Boon said:
Consider a "set" of M=N*N integers. In some parts of my program, I
want to handle this set as a 1D array of M integers. In other parts of
my program, I want to handle this set as a 2D matrix of N*N integers.

Is the following code 100% legal C89 code ?

I think so, yes. I was hoping someone else would stick their neck out
first, but it may as well be me! Note that 100% legal != 100%
portable -- I see one possible gotcha.
#include <assert.h>
#define N 9
typedef int row_t[N];

void compare(int *array)
{
int i, j;
row_t *matrix = (row_t *)array;

The only issues seems to be to be this conversion. The standard says
very little about it except that the pointer must be properly aligned.
I think it is possible for row_t to have stricter alignment
requirements than int, but this problem would vanish if the pointer
passed were malloced rather than being converted from a declared array
in main (malloced space must be correctly aligned for any use).

An implementation that requires such an alignment would be odd indeed
since it would only apply to certain sizes of row_t, but I can't
persuade myself that the standard prohibits it.

OK. What about casting the "other way" ?

Is it 100% legal and 100% portable ? :)

NB : I want to be able to use matrix[j] and array[i*N+j]
interchangeably.


I am tempted to ask "Why?" but a more important question is probably
what degree of portability risk you are prepared to take. Your
previous code has no practical problems that I can see. A quick
glance in the documentation for any given C implementation will
assure you that no bizarre alignment for arrays is enforced and you
are home and dry. Any risk (and you will see that not everyone agrees
there is a risk) is well below my radar and my radar for portability
hazards is set very much more sensitive than most.
#include <assert.h>
#define N 9
int main(void)
{
int i, j;
int matrix[N][N];
int *array = (int *)matrix;

The problem here is now not alignment but what, exactly, is the array
that 'array' points to[1]. Is it the whole of 'matrix' or only
'matrix[0]'? Does it make a difference if we write

int *array = (int *)&matrix;

or

int *array = &matrix[0][0];

? These are fun questions to ponder with respect to the standard but
I don't think it matters in practise. All reasonable compilers will
assume that a pointer into an array can be used to alias any element
and the optimiser (always your worst enemy when you contemplate
bending the language rules) will take that into account.

Anyway, it may be worth backing up and asking yourself if you can't do
what you want another way that stays entirely within the undisputed
bounds of the language.
for (i=0; i < N; ++i)
for (j=0; j < N; ++j)
{
matrix[j] = i + j + 7;
assert( &matrix[j] == &array[i*N+j] );
assert( matrix[j] == array[i*N+j] );
}

return 0;
}

AFAICT, the cast is mandatory, if I want to do what I've described.


It is, but see my other two examples, one of which dose not need a
cast.
Is this a situation where a cast is acceptable ?

Pass.

[1] The reason this matters is that the standard defines pointer
arithmetic entirely in terms of the arrays that pointers point into
(even when the array is a fictional 1-element array made up if a
single object).
 
K

Keith Thompson

Peter Nilsson said:
Keith Thompson said:
Peter Nilsson said:
Consider...

  struct X { int a; int b; };

If it's reasonable for an implementation to assign
this 2 * sizeof(int) byte alignment, especially if
2 ints fit nicely into a machine word, then wouldn't
it also be reasonable for int[2] to have the same
alignment requirements?

It would be reasonable to align a declared object of
type int[2] on a 2*sizeof(int) boundary, but the
compiler could not in general *require* such alignment.
You can treat any 2-element "slice" of an int array
as an array of type int[2]:

Chapter and verse please.

Hmm. I'm assuming the above is valid, but I can't actually cite C&V
to support my assumption.
AFAICS, you can treat a pointer to an element as a
pointer to a sequence of elements up to the end of the
array (and one byte beyond), but I can't see that you
can impose a new effective type on that array.
    typedef int two_ints[2];
    /* just to make the declarations less confusing */
    int arr[3];
    two_ints *p = (two_ints*)&arr[1];

You're assuming that the explicit conversion holds, ergo
there's no way to enforce the alignment. I state that
the conversion needn't hold because alignment may be
enforced.

I might be missing something (again ;-), but where
does the standard give latitude for the array pointer
conversion that is not also given to the struct pointer?

Quite possibly it doesn't.
 
T

Tim Rentsch

Keith Thompson said:
Peter Nilsson said:
Consider...

struct X { int a; int b; };

If it's reasonable for an implementation to assign this
2 * sizeof(int) byte alignment, especially if 2 ints fit
nicely into a machine word, then wouldn't it also be
reasonable for int[2] to have the same alignment
requirements?

It would be reasonable to align a declared object of type int[2] on a
2*sizeof(int) boundary, but the compiler could not in general
*require* such alignment. [...]

I believe it can. We can infer that the Standard permits more
restrictive alignment requirements for arrays of length > 1 than
for arrays of unknown length (and in particular of length 1) from
6.2.5 p 13

Each complex type has the same representation and alignment
requirements as an array type containing exactly two elements of
the corresponding real type; the first element is equal to the
real part, and the second element to the imaginary part, of the
complex number.

If arrays of length 2 couldn't have more restrictive alignment
requirements than other arrays, this paragraph probably would
have been worded differently.
 
T

Tim Rentsch

Peter Nilsson said:
Boon said:
Hello everyone,

Consider a "set" of M=N*N integers. In some parts of my
program, I want to handle this set as a 1D array of M
integers. In other parts of my program, I want to handle
this set as a 2D matrix of N*N integers.

There are references to this in the FAQ.
Is the following code 100% legal C89 code ?

Whilst you are extremely unlikely to ever ancounter an
implementation where it will fail, it is not guaranteed
by the standard.
#include <assert.h>
#define N 9
typedef int row_t[N];

void compare(int *array)
{
int i, j;
row_t *matrix = (row_t *)array;

This conversion isn't as well defined as it could be.
There is no guarantee that the pointer array's first
element is suitably aligned for a pointer to an array
of 9 integers. Even if it happens to be, the only
thing you can reliably do with matrix is convert it
back to a pointer to int.

The last statement here is a statement of opinion, not a statement
of fact. Plenty of people believe (and I'm one of them) that as
long as alignment requirements are met, the Standard provides that
the variable 'matrix' may be used as a two-dimensional overlay to
access elements in the original array.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top