代码之家  ›  专栏  ›  技术社区  ›  mbauman

对于指向指针的指针,C99的“限制”的语义是什么?

  •  11
  • mbauman  · 技术社区  · 15 年前

    我正在做很多矩阵运算,想利用C99 restrict 指针限定符。

    我想将我的矩阵设置为指向指针的指针,以便于订阅,如下所示:

    int **A = malloc (ncols * sizeof(int *));
    A[0] = malloc (nrows * ncols * sizof(int));
    for (int i=1; i < ncols; i++) {
        A[i] = A[0] + i*nrows;
    }
    

    现在,对于矩阵乘法函数

    void mmultiply ( int nrows, int ncols, int **Out, int **A, int **B);
    

    我必须限定参数的两个指针为受限指针吗?这是有效的语法,但我很难确定 int *restrict *restrict 行为与 int **restrict .

    然后,在指针受到适当限制的情况下,通过 A[0][col*nrows + row] 未定义?(即,编译器会假定 只有 通过访问矩阵 A[col][row] 对于价值 row 这样的话 row < nrow )是吗?还是我必须保持一致?

    3 回复  |  直到 12 年前
        1
  •  3
  •   DigitalRoss    15 年前

    对于第一个问题,“是”,如果您同时使用这两个选项,则意味着不同的内容。 restrict 限定符,特别是指针也不会有别名。至于它是否有任何区别:理论上是的,在实践中,它取决于优化器。

    对于第二个问题,“是”,它假定通过行指针访问的任何内容都只能通过行指针访问。

    你可以投掷 const 也在那里。

    最后,如果这是gcc at-o2、-o3或-os,编译器已经在基于类型执行别名分析。我相信其他编译器也会这样做。这意味着对指针和整数的限制已经被理解,只留下可能相互存储的数组。

    总之,优化器将假定指针没有存储在ints中,并且它知道在循环期间它没有进行任何指针写入。

    因此,您可能只需要一个限制就可以得到相同的代码。

        2
  •  2
  •   Chris Dodd    15 年前

    外部(第二个)限制告诉编译器没有指针数组(A、B和out)别名。内部(第一个)限制告诉编译器没有一个int数组(由指针数组的元素指向)别名。

    如果您同时访问[0][col*nrows+row]和[col][row],那么您违反了内部限制,因此事情可能会中断。

        3
  •  2
  •   Steve Jessop    15 年前

    int **restrict 仅断言out、a和b所寻址的内存不重叠(除非a和b可以重叠,假设函数不修改它们中的任何一个)。这意味着指针数组。它对out、a和b所指的内存内容没有任何断言。N1124中的脚注117表示:

    如果标识符P具有类型(int **restrict),则指针表达式p和p+1基于 指定的受限指针对象 按p,但指针表达式*p P[1]不是。

    类推 const ,我怀疑 restrict 两次断言您想要的,即数组中的任何值都不指向重叠的内存。但是阅读标准,我不能向自己证明它确实做到了。我认为“let d是一个普通标识符的声明,它提供了一种将对象p指定为类型t的限制限定指针的方法”,这确实意味着 int *restrict *restrict A ,然后a[0]和a[1]是指定为int的限制限定指针的对象。但它的法律含义相当重。

    我不知道你的编译器是否真的会用这些知识做任何事情,记住。很明显,这是一个是否实施的问题。

    所以我真的不知道你在传统的C 2-D阵列上得到了什么,你只需要分配 rows * cols * sizeof(int) 和索引 A[cols*row + col] . 显然,您只需要使用一次restrict,以及使用 限制 将能够重新排序从A到B的读操作。没有 限制 当然,它不能这样做,所以通过做你正在做的事情,你将自己置于编译器的怜悯之下。如果它不能处理双重限制,只处理单一限制的情况,那么您的双重间接性会使您付出优化的代价。

    乍一看,乘法可能比其他指针间接法更快。显然,您关心性能,或者根本不会使用restrict,所以为了稍微好一点的语法,在进行此更改之前,我会相当仔细地测试性能(在您关心的所有编译器上),而不必记住每次访问数组时有多少列。

    通过[0][col*nrows+row]访问元素是否未定义?

    是的,如果某个访问修改了元素,因为这使得[0]成为内存别名,也可以通过[col]访问。如果只有A和B是限定的指针,那就好了,但是如果A[0]和A[col]是限定的指针,那就不行了。

    我假设在这个函数中不修改,所以实际上别名是可以的。但是,如果您在out中做同样的事情,那么行为将是未定义的。