?
Ce document utilise Manuel du site Web PHP chinois Libérer
C類型系統(tǒng)中的每個單獨類型都具有該類型的多個限定版本,對應于const,volatile中的一個,兩個或全部三個,并且對于指向?qū)ο箢愋偷闹羔槪?em style="font-style: italic;">限制限定符。本頁介紹的影響限制預選賽。
僅指向?qū)ο箢愋涂梢允窍拗坪细瘢ㄌ貏e地,int restrict *p
和float (* restrict f9)(void)
是錯誤)。
限制語義僅適用于左值表達式; 例如,轉(zhuǎn)換為限制限定指針或返回限制限定指針的函數(shù)調(diào)用不是左值,并且限定符不起作用。
在P
聲明限制指針的塊的每次執(zhí)行過程中(通常每個函數(shù)體的每個執(zhí)行P
都是一個函數(shù)參數(shù)),如果通過P
(直接或間接)方式訪問的某個對象以任何方式被修改,則所有在該塊中訪問該對象(讀取和寫入)必須通過P
(直接或間接)發(fā)生,否則行為未定義:
void f(int n, int * restrict p, int * restrict q){ while(n-- > 0) *p++ = *q++; // none of the objects modified through *p is the same // as any of the objects read through *q // compiler free to optimize, vectorize, page map, etc.}void g(void){ extern int d[100]; f(50, d + 50, d); // OK f(50, d + 1, d); // Undefined behavior: d[1] is accessed through both p and q in f}
如果對象從不修改,則可能會被別名和通過不同的限制限定指針訪問(請注意,如果由別名限制限定指針指向的對象又是指針,則此別名可能會禁止優(yōu)化)。
從一個受限指針指向另一個受限指針是未定義的行為,除非將指向某個外部塊中的對象的指針指定給某個內(nèi)部塊中的指針(包括在調(diào)用具有受限指針參數(shù)的函數(shù)時使用受限指針參數(shù)),或者何時從一個函數(shù)返回(否則當from-pointer結(jié)束時):
int* restrict p1 = &a;int* restrict p2 = &b;p1 = p2; // undefined behavior
受限制的指針可以自由分配給無限制的指針,只要編譯器能夠分析代碼,優(yōu)化機會就會保持原狀:
void f(int n, float * restrict r, float * restrict s) { float * p = r, * q = s; // OK while(n-- > 0) *p++ = *q++; // almost certainly optimized just like *r++ = *s++}
如果使用限制類型限定符(通過使用typedef)聲明數(shù)組類型,則數(shù)組類型不受限制,但其元素類型為:
typedef int *array_t[10];restrict array_t a; // the type of a is int *restrict[10]
在函數(shù)聲明中,關(guān)鍵字restrict
可能出現(xiàn)在用于聲明函數(shù)參數(shù)的數(shù)組類型的方括號內(nèi)。它限定了數(shù)組類型轉(zhuǎn)換的指針類型:
void f(int m, int n, float a[restrict m][n], float b[restrict m][n]);void g12(int n, float (*p)[n]) { f(10, n, p, p+10); // OK f(20, n, p, p+10); // possibly undefined behavior (depending on what f does)}
限制限定符(如注冊存儲類)的預期用途是促進優(yōu)化,并且從組成合格程序的所有預處理翻譯單元中刪除所有限定符的實例不會改變其含義(即可觀察到的行為)。
編譯器可以自由地忽略使用的任何或所有別名含義restrict
。
為了避免未定義的行為,程序員必須確保不受違反限制限制指針的別名聲明。
作為語言擴展,許多編譯器提供了與之相反restrict
的屬性:一個屬性,指示即使類型不同,指針也可以是別名:may_alias(gcc),
限制限定指針有幾種常見的使用模式:
文件范圍限制限定指針必須在程序期間指向單個數(shù)組對象。該數(shù)組對象不能通過受限制的指針和聲明的名稱(如果它有一個)或另一個受限制的指針來引用。
文件范圍受限指針在提供對動態(tài)分配的全局數(shù)組的訪問時很有用; 限制語義可以通過這個指針來優(yōu)化引用,就像通過聲明名稱引用靜態(tài)數(shù)組一樣有效:
float * restrict a, * restrict b;float c[100]; int init(int n) { float * t = malloc(2*n*sizeof(float)); a = t; // a refers to 1st half b = t + n; // b refers to 2nd half}// compiler can deduce from the restrict qualifiers that// there is no potential aliasing among the names a, b, and c
限制限定指針最常用的用例是用作函數(shù)參數(shù)。
在以下示例中,編譯器可能會推斷出沒有修改對象的別名,因此積極優(yōu)化循環(huán)。一旦進入f,受限指針a必須提供對其相關(guān)陣列的獨占訪問權(quán)限。特別是,在f中,b和c都不會指向與a相關(guān)的數(shù)組,因為它們都不會被指定基于a的指針值。對于b,從聲明中的const限定詞中可以明顯看出,但對于c,需要檢查f的主體:
float x[100];float *c;void f(int n, float * restrict a, float * const b) { int i; for ( i=0; i<n; i++ ) a[i] = b[i] + c[i];}void g3(void) { float d[100], e[100]; c = x; f(100, d, e); // OK f( 50, d, d+50); // OK f( 99, d+1, d); // undefined behavior c = d; f( 99, d+1, e); // undefined behavior f( 99, e, d+1); // OK}
請注意,允許c指向與b關(guān)聯(lián)的數(shù)組。還要注意,出于這些目的,與特定指針關(guān)聯(lián)的“數(shù)組”只意味著通過該指針實際引用的數(shù)組對象的那部分。
塊范圍限制限定指針使得限制為其塊的別名斷言成為可能。它允許局部斷言只適用于重要的塊,比如緊密的循環(huán)。它還可以將一個將限制合格指針的函數(shù)轉(zhuǎn)換為一個宏:
float x[100];float *c;#define f3(N, A, B) \{ int n = (N); \ float * restrict a = (A); \ float * const b = (B); \ int i; \ for ( i=0; i<n; i++ ) \ a[i] = b[i] + c[i]; \}
由作為結(jié)構(gòu)成員的限制限定指針產(chǎn)生的別名聲明的范圍是用于訪問該結(jié)構(gòu)的標識符的范圍。
即使結(jié)構(gòu)在文件范圍內(nèi)聲明,當用于訪問結(jié)構(gòu)的標識符具有塊范圍時,結(jié)構(gòu)中的別名聲明也具有塊范圍; 別名聲明僅在塊執(zhí)行或函數(shù)調(diào)用中生效,具體取決于如何創(chuàng)建此結(jié)構(gòu)類型的對象:
struct t { // Restricted pointers assert that int n; // members point to disjoint storage. float * restrict p; float * restrict q;}; void ff(struct t r, struct t s) { struct t u; // r,s,u have block scope // r.p, r.q, s.p, s.q, u.p, u.q should all point to // disjoint storage during each execution of f. // ...}
restrict
.
代碼生成示例; 用-S(gcc,clang等)或/ FA(visual studio)編譯。
int foo(int *a, int *b){ *a = 5; *b = 6; return *a + *b;} int rfoo(int *restrict a, int *restrict b){ *a = 5; *b = 6; return *a + *b;}
可能的輸出:
# generated code on 64bit Intel platform:foo: movl $5, (%rdi) # store 5 in *a movl $6, (%rsi) # store 6 in *b movl (%rdi), %eax # read back from *a in case previous store modified it addl $6, %eax # add 6 to the value read from *a ret rfoo: movl $11, %eax # the result is 11, a compile-time constant movl $5, (%rdi) # store 5 in *a movl $6, (%rsi) # store 6 in *b ret