Casting between 1D and 2D arrays

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Boon

    Casting between 1D and 2D arrays

    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[i][j] == &array[i*N+j] );
    assert( matrix[i][j] == array[i*N+j] );
    }
    }

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

    Can I be sure that the asserts will never fail ?

    Regards.
  • Ben Bacarisse

    #2
    Re: Casting between 1D and 2D arrays

    Boon <root@localhost writes:
    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[i][j] == &array[i*N+j] );
    assert( matrix[i][j] == array[i*N+j] );
    }
    }
    >
    int main(void)
    {
    int i;
    int foo[N*N];
    for (i=0; i < N*N; ++i) foo[i] = 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.

    --
    Ben.

    Comment

    • danmath06@gmail.com

      #3
      Re: Casting between 1D and 2D arrays

      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[i][j] == &array[i*N+j] );
      assert( matrix[i][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.

      Comment

      • Ben Bacarisse

        #4
        Re: Casting between 1D and 2D arrays

        danmath06@gmail .com 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[i][j] == &array[i*N+j] );
        > assert( matrix[i][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.

        --
        Ben.

        Comment

        • Peter Nilsson

          #5
          Re: Casting between 1D and 2D arrays

          Boon <root@localhost wrote:
          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[i][j] == &array[i*N+j] );
                 assert(  matrix[i][j] ==  array[i*N+j] );
               }
          }
          >
          int main(void)
          {
             int i;
             int foo[N*N];
             for (i=0; i < N*N; ++i) foo[i] = 7*i+11;
             compare(foo);
             return 0;
          }
          >
          Can I be sure that the asserts will never fail?
          Theoretically, No.

          --
          Peter

          Comment

          • Peter Nilsson

            #6
            Re: Casting between 1D and 2D arrays

            Ben Bacarisse <ben.use...@bsb .me.ukwrote:
            danmat...@gmail .com writes:
            Ben Bacarisse <ben.use...@bsb .me.ukwrote:
            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[i][j] == &array[i*N+j] );
                  assert(  matrix[i][j] ==  array[i*N+j] );
                }
            }
            <snip>
            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. ;-]

            --
            Peter

            Comment

            • Keith Thompson

              #7
              Re: Casting between 1D and 2D arrays

              Peter Nilsson <airia@acay.com .auwrites:
              [...]
              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];

              --
              Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
              Nokia
              "We must do something. This is something. Therefore, we must do this."
              -- Antony Jay and Jonathan Lynn, "Yes Minister"

              Comment

              • Ben Bacarisse

                #8
                Re: Casting between 1D and 2D arrays

                Keith Thompson <kst-u@mib.orgwrites :
                Peter Nilsson <airia@acay.com .auwrites:
                [...]
                >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.

                --
                Ben.

                Comment

                • Boon

                  #9
                  Re: Casting between 1D and 2D arrays

                  Ben Bacarisse wrote:
                  Boon wrote:
                  >
                  >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[i][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[i][j] = i + j + 7;
                  assert( &matrix[i][j] == &array[i*N+j] );
                  assert( matrix[i][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.

                  Comment

                  • Peter Nilsson

                    #10
                    Re: Casting between 1D and 2D arrays

                    Keith Thompson <ks...@mib.orgw rote:
                    Peter Nilsson <ai...@acay.com .auwrites:
                    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?

                    --
                    Peter

                    Comment

                    • Ben Bacarisse

                      #11
                      Re: Casting between 1D and 2D arrays

                      Boon <root@localhost writes:
                      Ben Bacarisse wrote:
                      >
                      >Boon wrote:
                      >>
                      >>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[i][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[i][j] = i + j + 7;
                      assert( &matrix[i][j] == &array[i*N+j] );
                      assert( matrix[i][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).

                      --
                      Ben.

                      Comment

                      • Keith Thompson

                        #12
                        Re: Casting between 1D and 2D arrays

                        Peter Nilsson <airia@acay.com .auwrites:
                        Keith Thompson <ks...@mib.orgw rote:
                        >Peter Nilsson <ai...@acay.com .auwrites:
                        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.

                        --
                        Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
                        Nokia
                        "We must do something. This is something. Therefore, we must do this."
                        -- Antony Jay and Jonathan Lynn, "Yes Minister"

                        Comment

                        • Tim Rentsch

                          #13
                          Re: Casting between 1D and 2D arrays

                          Keith Thompson <kst-u@mib.orgwrites :
                          Peter Nilsson <airia@acay.com .auwrites:
                          [...]
                          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.

                          Comment

                          • Tim Rentsch

                            #14
                            Re: Casting between 1D and 2D arrays

                            Peter Nilsson <airia@acay.com .auwrites:
                            Boon <root@localhost wrote:
                            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.

                            Comment

                            Working...