理解限制通过实例预选赛 [英] Understanding restrict qualifier by examples

查看:70
本文介绍了理解限制通过实例预选赛的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限制关键字的行为是由C99定义6.7.3.1:

The restrict keyword's behavior is defined in C99 by 6.7.3.1:

D是提供了一种普通标识符的声明
  指定的一个目标P为限制限定指针类型T

Let D be a declaration of an ordinary identifier that provides a means of designating an object P as a restrict-qualified pointer to type T.

若D出现块内,不具有存储类的extern,
  令B表示块。若D出现在参数列表
  函数定义的声明,令B表示相关
  块。否则,设B为主要块(或块
  无论功能是在一个独立的名为在程序启动
  环境)。

If D appears inside a block and does not have storage class extern, let B denote the block. If D appears in the list of parameter declarations of a function definition, let B denote the associated block. Otherwise, let B denote the block of main (or the block of whatever function is called at program startup in a freestanding environment).

在下文中,指针前pression E被说成是基于对象
  如果(在B的执行一些序列点之前到P
  修改P电子商务评估)指向数组对象的副本
  到它以前指出会改变E.119的值)注
  这就是基于'仅被定义为与指针类型的前pressions。

In what follows, a pointer expression E is said to be based on object P if (at some sequence point in the execution of B prior to the evaluation of E) modifying P to point to a copy of the array object into which it formerly pointed would change the value of E.119) Note that ''based'' is defined only for expressions with pointer types.

在的B每次执行,令L是具有与放大器的左值;基于L
  P.如果L被用来访问对象X的值这
  表示,且X也被修改(通过任何方式),则以下
  要求适用:T已不得const限定。每隔左值
  用于访问X的值也应已根据P.其地址
  一个修改点¯x每个接入应还考虑修改P,为
  本节的目的。如果P被分配的值
  这是基于另一个受限的指针指向前pressionË
  对象P2,与块B2,B2则任执行相关
  将B的执行,或B2的执行日期前开始
  之前的分配结束。如果这些要求得不到满足,那么
  该行为是不确定的。

During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified. Every other lvalue used to access the value of X shall also have its address based on P. Every access that modifies X shall be considered also to modify P, for the purposes of this subclause. If P is assigned the value of a pointer expression E that is based on another restricted pointer object P2, associated with block B2, then either the execution of B2 shall begin before the execution of B, or the execution of B2 shall end prior to the assignment. If these requirements are not met, then the behavior is undefined.

就像几乎所有人别的,我很难理解这个定义的所有复杂。作为这个问题的答案,我想看看一组很好的例子,每个需求在第4款,这将违反规定用法。文章:

Like just about everybody else, I have a hard time understanding all the intricacies of this definition. As an answer to this question, I'd like to see a set of good examples, for each requirement in the 4th paragraph, of usages that would violate the requirement. This article:

<一个href=\"http://web.archive.org/web/20120225055041/http://developers.sun.com/solaris/articles/cc_restrict.html\">http://web.archive.org/web/20120225055041/http://developers.sun.com/solaris/articles/cc_restrict.html

确实presenting规则来讲的一个好工作编译器可能承担......;扩大在该模式,并在假设的编译器可以让搭售,以及他们如何不抱,每个例子将是巨大的。

does a good job of presenting the rules in terms of "a compiler may assume..."; expanding on that pattern and tying in the assumptions the compiler can make, and how they fail to hold, with each example would be great.

推荐答案

下面,我将把在这个问题联系到太阳纸业的用例。

Below, I will refer to the usecases from the Sun paper linked to in the question.

(相对)不明显的情况下将是mem_copy()的情况下,其下的太阳纸业第二个用例类(即 F1()功能)下降。比方说,我们有如下两种实现方式:

The (relatively) obvious case would be the mem_copy() case, which falls under the 2nd usecase category in the Sun paper (the f1() function). Let's say we have the following two implementations:

void mem_copy_1(void * restrict s1, const void * restrict s2, size_t n);
void mem_copy_2(void *          s1, const void *          s2, size_t n);

由于我们知道有没有重叠的两个数组由S1和S2指出,在code为第一功能是直截了当:

Because we know there is no overlap between the two arrays pointed to by s1 and s2, the code for the 1st function would be straight forward:

void mem_copy_1(void * restrict s1, const void * restrict s2, size_t n)
{
     // naively copy array s2 to array s1.
     for (int i=0; i<n; i++)
         s1[i] = s2[i];
     return;
}

S2 ='.................... 1234567890abcde'&LT; - S2天真的副本之前 < BR>
S1 ='1234567890abcde ....................'&LT; - 天真的副本后,S1 结果
S2 ='.................... 1234567890abcde'&LT; - 天真的副本后,S2

OTOH,在第2功能,可能有重叠。在这种情况下,我们需要检查源阵列是否在目的地或反之亦然之前的位置,并据此选择循环索引边界。

OTOH, in the 2nd function, there may be an overlap. In this case, we need to check whether the source array is located before the destination or vice-versa, and choose the loop index boundaries accordingly.

举例来说,假设 S1 = 100 S2 = 105 。然后,如果 N = 15 ,复制后新复制的 S1 数组将溢出的第一个10个字节的源 S2 阵列。我们需要确保我们先复制的低位字节。

For example, say s1 = 100 and s2 = 105. Then, if n=15, after the copy the newly copied s1 array will overrun the first 10 bytes of the source s2 array. We need to make sure we copied the lower bytes first.

S2 ='..... 1234567890abcde'&LT; - S2天真的副本之前结果
S1 ='1234567890abcde .....'&LT; - 天真的副本后,S1 结果
S2 ='..... 67890abcdeabcde'&LT; - 天真的副本后,S2

但是,如果, S1 = 105 S2 = 100 ,然后写低字节将首先蹂躏最后10个字节的源 S2 中,我们结束了一个错误的副本。

However, if, s1 = 105 and s2 = 100, then writing the lower bytes first will overrun the last 10 bytes of the source s2, and we end up with an erroneous copy.

S2 ='1234567890abcde .....'&LT; - S2天真的副本之前结果
S1 ='...... 123451234512345'&LT; - 天真的副本后,S1 - 不是我们想要的结果
S2 ='123451234512345 ......'&LT; - 天真的副本后,S2

在这种情况下,我们需要先复制数组的最后一个字节,可能后退操作。在code看起来是这样的:

In this case, we need to copy the last bytes of the array first, possibly stepping backwards. The code will look something like:

void mem_copy_2(void *s1, const void *s2, size_t n)
{
    if (((unsigned) s1) < ((unsigned) s2))
        for (int i=0; i<n; i++)
             s1[i] = s2[i];
    else
        for (int i=(n-1); i>=0; i--)
             s1[i] = s2[i];
    return;
}

这是很容易看到的限制修改给出了更好的速度优化的机会,消除了额外的code,以及如果其他决定。

It is easy to see how the restrict modifier gives a chance for better speed optimization, eliminating extra code, and an if-else decision.

与此同时,这种情况是危险的不谨慎程序员,谁传球重叠阵列的限制 -ed功能。在这种情况下,没有警卫有用于确保阵列的正确复制。根据由编译器选择优化路径上,其结果是不确定的。

At the same time, this situation is hazardous to the incautious programmer, who passes overlapping arrays to the restrict-ed function. In this case, no guards are there for ensuring the proper copying of the array. Depending on the optimization path chosen by the compiler, the result is undefined.

第1用例(在的init()功能)可以被看作是在2之一的变型中,如上所述。在这里,两个阵列与一个单一的动态内存分配调用创建。

The 1st usecase (the init() function) can be seen as a variation on the 2nd one, described above. Here, two arrays are created with a single dynamic memory allocation call.

指定的两个指针为限制 -ed能够优化在该指令顺序,否则无所谓。例如,如果我们有code:

Designating the two pointers as restrict-ed enables optimization in which the instructions order would matter otherwise. For example, if we have the code:

a1[5] = 4;
a2[3] = 8;

则优化可以,如果它发现它是有用的。

then the optimizer can reorder these statements if it finds it useful.

OTOH,如果指针的限制 -ed,那么很重要的是,第一次分配将在第二个之前进行。这是因为有一种可能性,即 A1 [5] A2 [3] 实际上是相同的内存位置!这是很容易看出,当是这种情况,则该端值应该有8。如果我们重新排序的指示,则结束值将是4!

OTOH, if the pointers are not restrict-ed, then it is important that the 1st assignment will be performed before the second one. This is because there is a possibility that a1[5] and a2[3] are actually the same memory location! It is easy to see that when this is the case, then the end value there should be 8. If we reorder the instructions, then the end value will be 4!

此外,如果非相交指针给这个限制 -ed假设code,其结果是不确定的。

Again, if non-disjoint pointers are given to this restrict-ed assumed code, the result is undefined.

这篇关于理解限制通过实例预选赛的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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