Passing array to a function (and why it does not work in C++)

后端 未结 7 1430
死守一世寂寞
死守一世寂寞 2020-12-15 15:03

I have come across some C code that compiles, but I do not understand why. Specifically, I have a C library that has a lot of code using this format:

void ge         


        
7条回答
  •  离开以前
    2020-12-15 15:57

    The difficulty with your code sample is that one of the function parameters is protoyped, double xu_col[n_col][n_x + n_u], where n_x and n_u are variables, not constants. If you just pass this as a double[] instead, some C++ compilers might allow a cast such as double (&table)[n_col][n_x + n_u] = (double(&)[n_col][n_x + n_u])xu_col; to work as a non-standard extension, but the portable approach would be to write accesses like xu_col[i*(n_x+n_u) + j], which you could simplify with a helper function if that’s too ugly.

    An alternative approach, probably more in keeping with the spirit of the STL, might be to write a minimal container class that knows its dimensions, stores elements in a linear array for efficiency. Then you might declare redim_array table = redim_array(xu_col, n_col*(n_x+n_u)).redim(n_col, n_x+n_u); and access table(i,j).

    Several of the other answers have described the syntax of variable-length arrays, but another aspect of your question is how it’s legal to implicitly convert a rectangular¹ two-dimensional array to a one-dimensional array.

    What happens is that the rectangular array is laid out as consecutive elements in memory, so it can degenerate to a pointer to the elements, and then the function parameter can interpret that as an array with a different geometry.

    Here’s a short little program that demonstrates this behavior.

    #include 
    #include 
    #include 
    
    #define ROWS 2
    #define COLS 4
    #define ELEMS (ROWS*COLS)
    
    int flatten_array( const ptrdiff_t n, const int a[n] )
    {
      int printed = 0;
    
      for ( ptrdiff_t i = 0; i < n; ++i )
        printed += printf( "%d ", a[i] );
    
      return printed + printf("\n");
    }
    
    int rectangular_array( const ptrdiff_t m,
                           const ptrdiff_t n,
                           const int a[m][n] )
    {
      int printed = 0;
    
      for ( ptrdiff_t i = 0; i < m; ++i ) {
        for ( ptrdiff_t j = 0; j < n; ++j )
          printed += printf( "%d ", a[i][j] );
    
        printed += printf("\n");
      }
    
      return printed + printf("\n");
    }
    
    int main(void)
    {
      static const int matrix[ROWS][COLS] = {
        {11, 12, 13, 14},
        {21, 22, 23, 24}
      };
      static const int vector[ELEMS] = {11, 12, 13, 14, 21, 22, 23, 24};
    
      flatten_array( ELEMS, *(const int (*const)[ELEMS])matrix );
      printf("\n");
      rectangular_array( ROWS, COLS, *(const int (*const)[ROWS][COLS])vector );
    
      return EXIT_SUCCESS;
    }
    

    There’s some language-lawyering in the comments below² about whether passing the array arguments without the explicit casts is technically legal by standard. I’ve chosen to relegate that to a footnote and just delete the example with no casts. In the real world, you will sometimes see code without the pointer-to-array-of-different-geometry cast, and it might generate a warning. The memory layout of the two arrays is required by the standard to be the same.

    To convert to C++, you can use the pointer-conversion trick, or you now can code-golf it a bit by using references.

    Here is a C++ translation of the program above. It requires all but the first dimension of the array being passed in to be constexpr, but some compilers support C99-style variable-length arrays as an extension.

    #include 
    #include 
    #include 
    
    constexpr ptrdiff_t rows = 2;
    constexpr ptrdiff_t cols = 4;
    constexpr ptrdiff_t elems = rows * cols;
    
    int flatten_array( const ptrdiff_t n, const int a[] )
    {
      int printed = 0;
    
      for ( ptrdiff_t i = 0; i < n; ++i )
        printed += printf( "%d ", a[i] );
    
      return printed + printf("\n");
    }
    
    int rectangular_array( const ptrdiff_t n, const int a[][cols] )
    {
      int printed = 0;
    
      for ( ptrdiff_t i = 0; i < n; ++i ) {
        for ( ptrdiff_t j = 0; j < cols; ++j )
          printed += printf( "%d ", a[i][j] );
    
        printed += printf("\n");
      }
    
      return printed + printf("\n");
    }
    
    int main(void)
    {
      static const int matrix[rows][cols] = {
        {11, 12, 13, 14},
        {21, 22, 23, 24}
      };
      static const int vector[elems] = {11, 12, 13, 14, 21, 22, 23, 24};
    
      flatten_array( elems, (const int(&)[elems])matrix );
      printf("\n");
      rectangular_array( rows, (const int(&)[rows][cols])vector );
    
      return EXIT_SUCCESS;
    }
    

    ¹ C programmers sometimes call either arrays like int matrix[ROWS][COLS] or arrays like char** argv “two-dimensional arrays.” Here, I call the former rectangular and the latter ragged.

    ² The constraint on function arguments in the C11 standard is ‘Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.’ Furthermore ‘A declaration of a parameter as ''array of type'' shall be adjusted to ''qualified pointer to type''’ and, if this applies recursively, a multidimensional array of some type will be adjusted to a flat pointer of that type.

提交回复
热议问题