“restrict”关键字只能在函数定义中使用吗?

4

我想知道是否可能仅在函数定义中包含restrict关键字,而不是在函数声明中像这样:

void foo(char *bar);

void foo(char * restrict bar)
{
    // do something
}

foo 只接受一个参数,任何指针别名都必须在 foo 内部发生。调用函数的人不需要知道 restrict 修饰符。在函数声明中省略该关键字是否可行,就像使用 const 一样?


注意:这些声明不是针对人的,而是针对编译器的。 - tadman
2
“调用函数的人不需要知道限定符”这种说法并不正确。考虑 int fred[42]; foo(fred),如果 foo() 操作全局的 fred[],那么这个调用就违反了 restrict 的限制。 - chux - Reinstate Monica
@chux-ReinstateMonica 为什么会这样?我认为只要函数内部没有出现 char *p = bar 这样的语句就没问题。 - ericw31415
1
@tadman:函数定义中的restrict是为编译器而设,但在声明中参数上使用restrict则是为了程序员,编译器会忽略它。 - chqrlie
2个回答

8
无论函数声明是不是定义,在参数上使用"restrict"关键字是合法的,因为这符合C语言的语法规则,没有任何反对的规定。但是,在非定义的声明中,这种限定符对编译器没有影响。这是因为6.5.2.2 7规定限定符在将参数传递给具有原型的函数时被删除:
"...参数被隐式转换,就像赋值一样,到相应参数的类型,假定每个参数的类型都是其声明类型的未限定版本。"
因此,如果一个函数声明有一个类型为"int * restrict a"的参数,那么您传递的任何参数都会转换为未限定类型"int *"。
进一步来说,两个除了参数限定符之外完全相同的函数声明是兼容的,因为C 2018 6.7.6.3 15表示:
"...(在确定类型兼容性和组合类型时,...每个带限定类型声明的参数被视为具有其已声明类型的未限定版本)。"
然而,这仅适用于参数本身。该参数不受限制它的限定符的影响。但是它可以指向一个受到限定限制的指针。例如:void foo(void * restrict *a);void foo(void **a);声明了不同的函数类型。
虽然在声明中的参数限定符对编译器没有影响,但它们可以向人们传递一个信号,表示希望参数符合限制。在函数定义内部,该参数是受到限制的,并且任何调用该函数的人都应该尊重这一点。

1
我认为在声明中加入它也可以帮助编译器提供更好的警告或优化。 - Nate Eldredge
6.5.2.2中的7表示限定符被移除。需要注意的是,这里指的是传递类型本身使用的限定符,而不是指向的类型使用的限定符。 "仿佛通过赋值"部分意味着我们不能将const int *传递给期望int *的函数,但我们可以传递int * const,因为这是赋值规则规定的方式。 - Lundin
最后,引用标准中相关的部分是关于左值转换的规则6.3.2.1/2:“...没有数组类型的左值被转换为存储在指定对象中的值(不再是左值);这被称为左值转换。如果左值具有限定类型,则该值具有左值类型的未限定版本;" - Lundin
@Lundin:答案确实说明了限定符从参数类型中删除,并且确实说明了指向指针可以是限定符限定的。 - Eric Postpischil
@Lundin:6.3.2.1 2是无关紧要的,因为参数类型由6.5.2.2 7所规定。无论6.3.2.1 2是否指出限定符在lvalue转换期间被移除,参数都会根据6.5.2.2 7转换为参数类型,该规定表示它们将被转换为未限定类型。相反,如果6.5.2.2 7表示它们将按其参数类型及其限定符进行转换,则它们将按其参数类型及其限定符进行转换,而不管lvalue转换做了什么。6.3.2.1 2对此没有影响。 - Eric Postpischil

0
下面的 C99 源代码可以向您展示程序的输出取决于 restrict :
__attribute__((noinline))
int process(const int * restrict const a, int * const b) {
    *b /= (*a + 1) ;
    return *a + *b ;
}

int main(void) {
    int data[2] = {1, 2};
    return process(&data[0], &data[0]);
}

使用restrict限定符时,软件以代码1终止,而不使用restrict限定符时则以0终止。
编译使用gcc -std=c99 -Wall -pedantic -O3 main.c进行。
标志-O1也可以完成任务。

等等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接