C99で導入された restrict キーワードに関するメモ。restrictキーワード の続き。
読込アクセスとalias
restrictポインタが指すオブジェクトに対して変更(書き込み)を行わなければ、restrictポインタ同士が同一オブジェクトを指していても良い。下記コードでは関数h内のrestrictポインタqとrは同一オブジェクトbを指しているが、関数hではqやrが指すオブジェクトの更新を行っていない。(6.7.3.1/p10)
void h(int n, int * restrict p, int * restrict q, int * restrict r) { int i; for (i = 0; i < n; i++) p[i] = q[i] + r[i]; } int a[100], b[100]; h(100, a, b, b); // well-defined
オブジェクトのalias
restrictキーワードが意味する「aliasが存在しない」とは、restrictポインタが指す先の「オブジェクト」に対する表明となる。下記コードでは x と *p は同一オブジェクトを指しており、また処理(1), (2)は同オブジェクトの変更を行っている。このケースはrestrictの要件に違反しているため未定義動作となる。(6.7.3.1/p4)
int x; { int * restrict p = &x; ++(*p); // (1) restrictポインタpを介したオブジェクト更新 ++x; // (2) 左辺値xを介したオブジェクト更新 // undefined behavior! }
仮に、処理(1)または(2)のいずれか一方のみを行う、もしくはオブジェクトに対する変更を行われなければwell-defiend。
no-aliasの表明範囲
restrictポインタが指すオブジェクトのaliasが存在しないと表明する範囲は、restrictポインタ型変数を宣言した場所に応じて3種類に分類される。(6.7.3.1/p2)
- [A] restrictポインタ型変数が関数定義中のパラメータリストで宣言されるとき、該当関数の実行中にaliasが存在しないとみなす。
- [B] (非externな)restrictポインタ型変数がブロック内で宣言されるとき、該当ブロックの実行中にaliasが存在しないとみなす。
- [C] 上記いずれにも該当しないとき、プログラム全体の実行中にaliasが存在しないとみなす。
int * restrict p3_1; // [C] プログラム全体 void func(int * restrict p1) // [A] 関数本体 { extern int * restrict p3_2; // [C] プログラム全体 //... { int * restrict p2_1; // [B] ブロック static int * restrict p2_2; // [B] ブロック //... } }
メンバ変数への使用
構造体メンバ変数にrestrictポインタを持つ場合、no-alias表明範囲は構造体変数を宣言した場所に依存する。下記コードにおいてs1.a1とs1.a2はプログラム全体が表明範囲となり、s2.a1とs2.a2は関数f3の本体のみが表明範囲となる。(Rationale Rev5.10 6.7.3.1)
struct t { int n; float * restrict a1, * restrict a2; }; struct t s1; void f3(struct t s2) { /* ... */ }
標準Cライブラリでの使用
C99では古くからあるC標準ライブラリ関数に対して、一部のポインタ型引数にrestrict修飾を追加しており、おおまかに下記の3パターンに分類できる。(Rationale Rev5.10 7.1)
- [a] 効率的な実装のために範囲オーバーラップを許容しない関数。代表例:
memcpy
のコピー元/コピー先。 - [b] 書式文字列と可変引数リストを指定する関数。代表例:
printf
の書式文字列。 - [c] char型ポインタと他ポインタを指定する関数。代表例:
fgets
の文字列バッファとFILEポインタ。
// [a] void *memcpy(void * restrict s1, const void * restrict s2, size_t n); // [b] int printf(const char * restrict format, ...); // [c] char *fgets(char * restrict s, int n, FILE * restrict stream);
パターン[c]は、コンパイラは「char型ポインタは任意オブジェクトのaliasとなりうる」というaliasing ruleに基づき*1、char型と他ポインタがaliasになる可能性を考慮しなければならない*2。この仮定が不要であるとコンパイラに表明し、ライブラリ関数の効率的な実装が行えるようrestrict修飾が追加されている。
関連URL
- (PDF) Rationale for International Standard Programming Languages C, Revision 5.10, April-2003
- (PDF) http://c0x.coding-guidelines.com/6.7.3.1.pdf
- When using a restrict Pointer in C, is it OK to change a variable using its initial Identifier? - Stack Overflow
- c - Is this an invalid use of restrict pointers? - Stack Overflow