从一个指针转换为类型的指针类型安全的阵列? [英] Is a conversion from a pointer to type to a pointer to array of type safe?

查看:161
本文介绍了从一个指针转换为类型的指针类型安全的阵列?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前几天我无意中发现了一个code其中大量使用从指针转换键入指针类型的数组是为了给在内存中的线性载体的双维视图。这种技术的一个简单的例子报道下面清晰:

A few days ago I stumbled on a code where an extensive use of conversions from pointer to type to pointer to array of type was made to give a bi-dimensional view of a linear vector in memory. A simple example of such a technique is reported below for clarity:

#include <stdio.h>
#include <stdlib.h>

void print_matrix(const unsigned int nrows, const unsigned int ncols, double (*A)[ncols]) {  
  // Here I can access memory using A[ii][jj]
  // instead of A[ii*ncols + jj]
  for(int ii = 0; ii < nrows; ii++) {
    for(int jj = 0; jj < ncols; jj++)
      printf("%4.4g",A[ii][jj]);
    printf("\n");
  }
}

int main() {

  const unsigned int nrows = 10;
  const unsigned int ncols = 20;

  // Here I allocate a portion of memory to which I could access
  // using linear indexing, i.e. A[ii]
  double * A = NULL;
  A = malloc(sizeof(double)*nrows*ncols);

  for (int ii = 0; ii < ncols*nrows; ii++)
    A[ii] = ii;

  print_matrix(nrows,ncols,A);
  printf("\n");
  print_matrix(ncols,nrows,A);

  free(A);
  return 0;
}

由于一个指针键入不是一个指针类型的阵列兼容的,我想问一下,如果有这种铸造相关的风险,或我可以假设按预期在任何平台上这件会工作。

Given that a pointer to type is not compatible with a pointer to array of type, I would like to ask if there are risks associated with this casting, or if I can assume that this casting will work as intended on any platform.

推荐答案

这是保证多维数组 T ARR [M] [N] 有相同的内存布局与元素 T ARR [M *​​ N] 的相同总数的一维数组。布局是一样的,因为阵列是邻接的(6.2.5p20),并且由于 sizeof的阵列/​​ sizeof的阵列[0] 保证返回的数组中的元素的数量( 6.5.3.4p7)。

It is guaranteed that a multidimensional array T arr[M][N] has the same memory layout as a single-dimensional array with the same total number of elements T arr[M * N]. The layout is the same because arrays are contiguous (6.2.5p20), and because sizeof array / sizeof array[0] is guaranteed to return the number of elements in the array (6.5.3.4p7).

然而,这并不意味着它是安全的投射的指针类型的指针类型的阵列,反之亦然。首先,对齐是一个问题;虽然与基本对准的类型的数组还必须具有基本对准(由6.2.8p2)它不保证该路线是相同的。因为数组包含基本类型的对象,数组类型的对齐必须至少为严格为基本对象类型的对齐方式,但它可以更严格(不,我见过这样的情况下)。然而,这是不相关的的分配的记忆,的malloc 保证返回适当分配给任何基本对齐(7.22.3p1)的指针。这并不意味着你不能安全地投出指针自动或静态存储到一个数组的指针,但相反的是允许的:

However, it does not follow that it is safe to cast a pointer to type to a pointer to array of type, or vice versa. Firstly, alignment is an issue; although an array of a type with fundamental alignment must also have fundamental alignment (by 6.2.8p2) it is not guaranteed that the alignments are the same. Because an array contains objects of the base type, the alignment of the array type must be at least as strict as the alignment of the base object type, but it can be stricter (not that I've ever seen such a case). However, this is not relevant for allocated memory, as malloc is guaranteed to return a pointer suitably allocated for any fundamental alignment (7.22.3p1). This does mean that you cannot safely cast a pointer to automatic or static memory to an array pointer, although the reverse is allowed:

int a[100];
void f() {
    int b[100];
    static int c[100];
    int *d = malloc(sizeof int[100]);
    int (*p)[10] = (int (*)[10]) a;  // possibly incorrectly aligned
    int (*q)[10] = (int (*)[10]) b;  // possibly incorrectly aligned
    int (*r)[10] = (int (*)[10]) c;  // possibly incorrectly aligned
    int (*s)[10] = (int (*)[10]) d;  // OK
}

int A[10][10];
void g() {
    int B[10][10];
    static int C[10][10];
    int (*D)[10] = (int (*)[10]) malloc(sizeof int[10][10]);
    int *p = (int *) A;  // OK
    int *q = (int *) B;  // OK
    int *r = (int *) C;  // OK
    int *s = (int *) D;  // OK
}

其次,它不能保证该数组和非数组类型之间铸造实际上会导致一个指针在正确的位置后,作为铸造规则(6.3.2.3p7)不包括这种用法。它的不大可能的,虽然这将导致通过的char * 确实有保证的语义不是一个指针到正确的位置,而投其他任何东西。当从一个指针将数组类型的指针的基本类型,最好是只间接指针:

Next, it is not guaranteed that casting between array and non-array types actually results in a pointer to the correct location, as the casting rules (6.3.2.3p7) do not cover this usage. It's highly unlikely though that this would result in anything other than a pointer to the correct location, and a cast via char * does have guaranteed semantics. When going from a pointer to array type to pointer to base type, it's better to just indirect the pointer:

void f(int (*p)[10]) {
    int *q = *p;                            // OK
    assert((int (*)[10]) q == p);           // not guaranteed
    assert((int (*)[10]) (char *) q == p);  // OK
}

什么是在语义的数组下标?如众所周知的,在 [] 操作是用于加法和间接只是语法糖,所以语义是那些在 + 运营商;作为6.5.6p8描述,指针操作数必须指向阵列足够大,其结果落在阵列内或刚刚过去的端部的构件。这是在两个方向上石膏的问题;铸造的指针数组类型时,加料的无效,因为不存在在该位置多维数组;和铸造的指针基本类型时,在该位置该阵列只具有结合的内阵列的尺寸:

What are the semantics of array subscripting? As is well known, the [] operation is just syntactic sugar for addition and indirection, so the semantics are those of the + operator; as 6.5.6p8 describes, the pointer operand must point to a member of an array that is large enough that the result falls within the array or just past the end. This is a problem for casts in both direction; when casting to a pointer to array type, the addition is invalid as there does not exist a multidimensional array at that location; and when casting to a pointer to base type, the array at that location only has the size of the inner array bound:

int a[100];
((int (*)[10]) a) + 3;    // invalid - no int[10][N] array

int b[10][10];
(*b) + 3;          // OK
(*b) + 23;         // invalid - out of bounds of int[10] array

这是我们开始看到就共同实施,不只是理论实际的的问题。因为一个的优化器的有权假设未定义的行为不会发生,通过基对象指针访问一个多维数组可以假定不的别名的之外的任何元素第一内部数组:

This is where we start to see actual issues on common implementations, not just theory. Because an optimiser is entitled to assume that undefined behavior does not occur, accessing a multidimensional array through a base object pointer can be assumed not to alias any elements outside those in the first inner array:

int a[10][10];
void f(int n) {
    for (int i = 0; i < n; ++i)
        (*a)[i] = 2 * a[2][3];
}

优化器可以假设访问 A [2] [3] 不别名(*一)[我] 和它吊起外循环:

The optimiser can assume access to a[2][3] does not alias (*a)[i] and hoist it outside the loop:

int a[10][10];
void f_optimised(int n) {
    int intermediate_result = 2 * a[2][3];
    for (int i = 0; i < n; ++i)
        (*a)[i] = intermediate_result;
}

当然,这会给如果意想不到的效果˚F被称为与 N = 50

最后,值得一问这是否​​适用于分配的内存。 7.22.3p1指定指针由的malloc 返回的可能被分配到的指针的任何类型的对象与基本对齐要求,然后用于访问这样的对象或这样的物体的在分配的空间阵列;没有什么关于进一步铸造返回的指针到另一个对象的类型的,所以结论是,分配内存的类型的固定第一指针类型的返回无效指针转换;如果转换为双* 那么你就无法再转换为双(*)[N] ,如果在投射到双(*)[N] 您只能使用双* 来访问第一个<$ C $ ç> N 元素。

Finally it's worth asking whether this applies to allocated memory. 7.22.3p1 specifies that the pointer returned by malloc "may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated"; there's nothing about further casting the returned pointer to another object type, so the conclusion is that the type of the allocated memory is fixed by the first pointer type the returned void pointer is cast to; if you cast to double * then you can't further cast to double (*)[n], and if you cast to double (*)[n] you can only use double * to access the first n elements.

因此​​,我会说,如果你想成为的绝对安全你不应该投指针和指针之间数组类型,即使采用相同的基本类型。该布局是一样的事实是不相关的,除了的memcpy 并通过字符指针其他访问。

As such, I'd say that if you want to be absolutely safe you should not cast between pointer and pointer to array types, even with the same base type. The fact that layout is the same is irrelevant except for memcpy and other accesses via a char pointer.

这篇关于从一个指针转换为类型的指针类型安全的阵列?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆