Const correctness for array pointers?

后端 未结 3 1408
北恋
北恋 2020-11-30 09:59

Someone made an argument saying that in modern C, we should always pass arrays to functions through an array pointer, since array pointers have strong typing. Example:

相关标签:
3条回答
  • 2020-11-30 10:42

    C standard says that (section: §6.7.3/9):

    If the specification of an array type includes any type qualifiers, the element type is so- qualified, not the array type.[...]

    Therefore, in case of const int (*arr)[n], const is applied to the elements of the array instead of array arr itself. arr is of type pointer to array[n] of const int while you are passing a parameter of type pointer to array[n] of int. Both types are incompatible.

    How do I apply const correctness to array pointers passed as parameters? Is it at all possible?

    It's not possible. There is no way to do this in standard C without using explicit cast.

    But, GCC allow this as an extension:

    In GNU C, pointers to arrays with qualifiers work similar to pointers to other qualified types. For example, a value of type int (*)[5] can be used to initialize a variable of type const int (*)[5]. These types are incompatible in ISO C because the const qualifier is formally attached to the element type of the array and not the array itself.

     extern void
     transpose (int N, int M, double out[M][N], const double in[N][M]);
     double x[3][2];
     double y[2][3];
     ...
     transpose(3, 2, y, x); 
    

    Further reading: Pointer to array with const qualifier in C & C++

    0 讨论(0)
  • 2020-11-30 10:51

    There is no way to do it except for the cast. This is significant drawback of the idea to pass arrays in this way.

    Here is a similar thread where the C rules are compared to the C++ rules. We could conclude from this comparison that the C rules are not so well designed, because your use case is valid but C doesn't allow the implicit conversion. Another such example is conversion of T ** to T const * const *; this is safe but is not allowed by C.

    Note that since n is not a constant expression, then int n, int (*arr)[n] does not have any added type safety compared to int n, int *arr. You still know the length (n), and it is still silent undefined behaviour to access out of bounds, and silent undefined behaviour to pass an array that is not actually length n.

    This technique has more value in the case of passing non-VLA arrays , when the compiler must report if you pass a pointer to an array of the wrong length.

    0 讨论(0)
  • 2020-11-30 10:53

    OP describes a function func() that has the following signature.

    void func(size_t n, const int (*arr)[n])
    

    OP wants to call it passing various arrays

    #define SZ(a) (sizeof(a)/sizeof(a[0]))
    
    int array1[3];
    func(SZ(array1), &array1);  // problem
    
    const int array2[3] = {1, 2, 3};
    func(SZ(array2), &array2);
    

    How do I apply const correctness to array pointers passed as parameters?

    With C11, use _Generic to do the casting as needed. The cast only occurs when the input is of the acceptable non-const type, thus maintaining type safety. This is "how" to do it. OP may consider it "bloated" as it is akin to this. This approach simplifies the macro/function call to only 1 parameter.

    void func(size_t n, const int (*arr)[n]) {
      printf("sz:%zu (*arr)[0]:%d\n", n, (*arr)[0]);
    }
    
    #define funcCC(x) func(sizeof(*x)/sizeof((*x)[0]), \
      _Generic(x, \
      const int(*)[sizeof(*x)/sizeof((*x)[0])] : x, \
            int(*)[sizeof(*x)/sizeof((*x)[0])] : (const int(*)[sizeof(*x)/sizeof((*x)[0])])x \
      ))
    
    int main(void) {
      #define SZ(a) (sizeof(a)/sizeof(a[0]))
    
      int array1[3];
      array1[0] = 42;
      // func(SZ(array1), &array1);
    
      const int array2[4] = {1, 2, 3, 4};
      func(SZ(array2), &array2);
    
      // Notice only 1 parameter to the macro/function call
      funcCC(&array1);  
      funcCC(&array2);
    
      return 0;
    }
    

    Output

    sz:4 (*arr)[0]:1
    sz:3 (*arr)[0]:42
    sz:4 (*arr)[0]:1
    

    Alternatively code could use

    #define funcCC2(x) func(sizeof(x)/sizeof((x)[0]), \
        _Generic(&x, \
        const int(*)[sizeof(x)/sizeof((x)[0])] : &x, \
              int(*)[sizeof(x)/sizeof((x)[0])] : (const int(*)[sizeof(x)/sizeof((x)[0])])&x \
        ))
    
    funcCC2(array1);
    funcCC2(array2);
    
    0 讨论(0)
提交回复
热议问题