29842


時に、以下のようなプログラム(プログラム自体は特に意味は無いです)を見かけます。

[code]

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void){
        char *input = "1 5 9 2 6 4 8";
        int array[16];
    
        char *p = input;
        int i;
        for(i = 0; i < 16 && *p; ++i){
            array[i] = strtol(p, &p, 10);
        }
        for(int n = i, i = 0; i < n; ++i)
            printf("%d ", array[i]);
        return 0;
    }
    
[/code]

気になるのは、`strtol(p, &p, 10)` の部分です。

`strtol` のプロトタイプを見ると、

[code]

    long strtol(
        const char * restrict nptr,
        char ** restrict endptr
        int base
    );
    
[/code]

のようになっていて、  
第１引数と第２引数にrestrict修飾がされています。  
私のざっくりとした理解ではそれは関数内でその引数を使う時に他の引数とかぶってはいけない(同じオブジェクト指すようなものであってはいけない)ということです。（つまり、私の理解では、制約違反。理解が間違っている場合そこら辺も解説して欲しいです。）  
この場合、restrict制約に違反していることになるのでしょうか？

それとも型が違うのだから大丈夫？  
型が違うから大丈夫なら、そもそも２つの引数にrestrict修飾する必要などないのでは？  
逆に、このケースでrestrict制約違反になる場合とはどんな場合？

あと、  
restrict制約に違反するようなプログラムを書いた場合、  
それはすなわち動作未定義ですか？（例えば第一引数はconstでオブジェクトを変更しないことが明らかなので実質問題無いように思える）

* * *

(他に書く所がないのでここで)  
コメントで、`memcpy(p, p, 0)` が動作未定義かどうか？  
ということを書いたのですが、  
規格のサンプルで、

[code]

    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];
    }
    
[/code]

の場合、

> h(100, a, b, b) has defined behavior, because array b is not modified within
> function h.

って書いてある。  
(この場合第１引数と第２・３引数との制約で、第２と第３引数の間の制約ではないとも言える。)  
理由として変更がされないからということであれば、  
動作未定義かどうかは単にパラメータ(のポインタが同一オブジェクトを指すかどうか)だけでは（実装を知らない使用者側としては）判断ができずに、その実装によることになる。（実際には動作の説明が必要不可欠）  
（逆に言えば、未定義動作(期待しないような動作)するような状況になったら、動作未定義だったのだなとわかるｗ）  
だから、`memcpy(p, p, 0)`の場合も、明らかに変更するような動作が行われないので、defined behaviorだと言える。


CC BY-SA 3.0
2016-10-18T14:41:42.530
29673
2016-10-23T15:11:33.940
2016-10-19T17:04:30.500
5044
5044
question
c
strtol の restrict 修飾について


`strtol` の第一引数は `char` (とその後ろに連続してある char) へのポインター (i.e. input へのポインター)
であり、第2引数は `char *` へのポインター (i.e. p へのポインター)
です。よってそれぞれのポインターが指してる先は別のオブジェクトなので、restirct 制約に違反してないように見えます。restrict
制約は、そのポインターが指してる先しか影響しない（ポインターのポインターだからといって、ポインターの先のまた先を見ない）はずです。

手元に規格書がないのですが、<https://en.wikipedia.org/wiki/Restrict> によれば

> If the declaration of intent is not followed and the object is  
>  accessed by an independent pointer, this will result in undefined  
>  behavior.

と書いてあるので、制約に違反するプログラムの動作は未定義のようです。

**追記**  
そもそも型が違う時点で `restrict` は意味無いでしょう。 <http://d.hatena.ne.jp/yohhoy/20120223/p1>
によれば

> なお「異なる型へのポインタ型同士」の場合は、restrictキーワードによる指示がなくてもstrict aliasing
> ruleに基づいて、コンパイラは互いに他方のaliasにならないと仮定した最適化を行う。

とありますし。意味があるかどうかはおいておくとして、無理やり

[code]

    int main(void) {
      char *p = (char *) malloc(sizeof(char *));
      p[0] = '0';
      p[1] = '\0';
      long l = strtol(p, (char **) p, 10);
    
[/code]

とすれば、`restrict` 制約に反するコードになるかもしれません。


CC BY-SA 3.0
2016-10-19T00:21:32.010
29682
2016-10-19T06:18:45.650
2016-10-19T06:18:45.650
2521
2521
29673
answer


サンプルが`restrict`制約の対象になら無いのは[Tatz
Sekineさんの回答](https://ja.stackoverflow.com/a/29682/3974)通りだと思います。

`restrict`修飾が付いている理由は推測でしか無いのですが、以下のようなことが考えられると思います（でも`endptr`へ書き込んだ後に`nptr`を読み込まないと思うので`restrict`やエイリアス関係無いと思うんですよね…）。

* * *

Strict
Aliasing(厳密な別名)規約によると、同じオブジェクトへの参照は以下のような型としてでしかしてはならない、とあります。逆に同スコープ内にこれらの型がある場合`restrict`を付けなければエイリアスが想定されることになると思います。

C99; ISO/IEC 9899:1999 6.5/7より (括弧内は自分がつけた訳です):

> An object shall have its stored value accessed only by an lvalue  
>  expression that has one of the following types:
>
>   * **a type compatible with the effective type of the object
> (オブジェクトと互換性のある型)** ,
>   * a qualiﬁed version of a type compatible with the effective type of  
>  the object (修飾された互換性のある型),
>   * a type that is the signed or unsigned type corresponding to the  
>  effective type of the object (オブジェクトの符号あり・なし版),
>   * a type that is the signed or unsigned type corresponding to a  
>  qualiﬁed version of the effective type of the object
> (修飾されたオブジェクトの符号あり・なし版),
>   * an aggregate or union type that includes one of the aforementioned  
>  types among its members (including, recursively, a member of a  
>  subaggregate or contained union) (上記4つをメンバに持つstruct/union/array), or
>   * **a character type (char型)**.
>

>
> The intent of this list is to specify those circumstances in which an object
> may or may not be aliased.

1つ目にある「Compatible
Type」の一覧は規格書内に見つからなかったのですが、[cppreference.com](http://en.cppreference.com/w/c/language/type#Compatible_types)によると以下を含むそうです
(括弧内は同上):

  * they are pointer types and are pointing to compatible types (互換性のある型へのポインタ)

なので`strtol`の2つの変数はデフォルトではエイリアスが想定されるのだと思います。

もう1つ、最後の項目によると、`char`として参照すると(`nptr[i]`や`*nptr`でしょうか)問答無用で他のどの型ともエイリアスがありえると解釈できるので、↑の他にこちらも`restrict`が付いている理由といえる気がします（個人的に初耳なので違うような気も）。

* * *

追記  
`restrict`はエイリアスを防止するわけではなく、プログラマ **が** コンパイラ **に対して** エイリアスが無い事を **約束**
するための修飾です。なのでコンパイラはそのつもりで最適化して良いですが、コードによっては関係無い場合もあります。ただrestrictした上で、同じオブジェクトを「変数1」で読み、「変数2」で書き込み、さらにもう1度「変数1」で読むとほぼ確実に期待とは違った結果になります（別のオブジェクトだと思い2度目の読み込みに1度目の結果を使い回すため）。

質問に追記された関数`h(100, a, b, b)`が問題無いのは書き込まれる変数`p`と読み込まれる変数`q`、`r`が別なためで、`h(100, a,
a, b)`とかでは仕様的にはダメだと思います（でも読み込みが先で、書き込んだ後に読まないので期待通りに動きそう）。`memcpy(p, p,
0)`は何もしないのでOKかもしれませんが`memcpy(p, p+1, 10)`とかはアウトです。  
そこの厳密なdefinedのラインはすみませんが分かりません。

あと、標準ライブラリでの`restrict`の使われ方を見ていると、実装の話より、ユーザーに対してエイリアスの良し悪しを伝えるために付けているような気もします（本来の意図と違う）。実際`strtol`はエイリアスがあっても大丈夫なように実装できますし、`memmove`もglibcなどの実装は書き込んだ領域を読まないので`restrict`が付いていても期待通りの動作をすると思います。ですが`memmove`は機能的に読み書きの領域がかぶっても良いので`restrict`とは逆の振る舞いであり、だから修飾されていないのだと思います。

最後に、コメントに書かれたように`restrict`は引数である必要はなく、普通の変数にも使えます。


CC BY-SA 3.0
2016-10-19T12:53:15.723
29709
2016-10-19T16:45:03.533
2020-06-17T08:14:45.997
-1
3974
29673
answer


前提：C99の`restrict`キーワードは「処理系に対する最適化のためのヒント情報」であり、 **妥当(valid)な**
Cソースコードから全ての`restrict`キーワードを削除しても、プログラムの意味は変化しません[C99
§6.7.3.1/3]。またrestrict修飾を理解するには、前提知識として [Strict Aliasing
Rules(厳密な別名付けの規則)](https://www.jpcert.or.jp/sc-rules/c-exp39-c.html)
を理解している必要があります。同規則については
[packet0さん回答](https://ja.stackoverflow.com/questions/29673/29709#29709)
に説明がありますが、大まかには次の通りです：

  * C言語コンパイラは、非互換な型へのポインタ同士は互いにaliasでないと仮定したコード生成を行ってよい。
  * ただし文字型へのポインタ型(`char*`)だけは、任意の型とのaliasを仮定する必要がある。

これらはalias解析に関するコンパイル時最適化を許可する規則であり、プログラマはStrict Aliasing
Rulesを遵守したコードを記述する義務があります。`restrict`キーワードは、本来の規則ではaliasを仮定せざるをえないケース（互換型へのポインタや`char*`）に対して、最適化を促すヒント情報「このポインタ値についてはaliasを考慮しなくてよい」をコンパイラに伝える仕組みです。

* * *

> 気になるのは、`strtol(p, &p, 10)`の部分です。  
>  （中略）  
>  この場合、restrict制約に違反していることになるのでしょうか？

いいえ。

  * 第1引数`p`は、`char`型オブジェクトの配列(文字列リテラル)を指す指すポインタ値です。
  * 第2引数`&p`は、`char*`型オブジェクト(変数`p`)を指すポインタ値です。

上記の2個のポインタ値は異なるオブジェクトを指します／互いにaliasではありませんから、関数呼び出し`strtol(p, &p, 10)`はwell-
definedです。

> それとも型が違うのだから大丈夫？  
>  型が違うから大丈夫なら、そもそも２つの引数にrestrict修飾する必要などないのでは？

Strict Aliasing
Rulesでも`char*`は特別扱いされますから、restrict修飾によって初めて「第1仮引数`nptr`と第2仮引数`endptr`は互いにaliasではない」ことを表明します。ただし、ここで関数`strtol`の引数がrestrict修飾されているのは、
**C標準ライブラリ実装者のため** です。アプリケーションプログラマにとってはメリットもデメリットもほぼありません（詳細後述）。

ISO/IEC JTC1/SC22/WG14より[C99仕様策定時のRationale(論理的根拠)](http://www.open-
std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf)が公開されており、§7にてC標準ライブラリにおける`restrict`キーワード利用指針が触れられています。関数`strtol`のケースに適用できる説明を引用します：

> Since the implementation costs are high if vendors are forced to cater to
> this extremely rare case, the `restrict` keyword is used to explicitly
> forbid situations like these.
>
> Another library routine that uses `restrict` is:
[code]

>     char *fgets(char * restrict s, int n, FILE * restrict stream);
>  
[/code]

>
> Again, since a character pointer can be a potential alias with other
> pointers, `restrict` is used to make it clear to the translator that
> parameter `s` is never an alias with parameter `stream` when the `fgets`
> function is called in a strictly conforming program.

C標準ライブラリのうち`char*`型とそれ以外のポインタ型を引数にとる関数では、上記理由によりrestrict修飾がされています。

> 逆に、このケースでrestrict制約違反になる場合とはどんな場合？

下記のような偏執的コードを書いたときです。関数`strtol`のセマンティクス上このような処理は一般的でないため、アプリケーションプログラマにとって関数`strtol`でのrestrict修飾有無は影響がありません：

[code]

    char a[] = “123456789”;
    char** p = (char**)(&a[0]); // 配列aの一部をchar*オブジェクトとみなす
    strtol(a, p, 10);
    
[/code]

* * *

> restrict制約に違反するようなプログラムを書いた場合、それはすなわち動作未定義ですか？

Strict Aliasing Rulesおよびrestrict修飾に関して、aliasに関する規則に違反したプログラムは未定義動作です。

> コメントで、`memcpy(p, p, 0)`が動作未定義かどうか？

これはwell-definedです。関数`memcpy`の第3引数に値`0`を指定した場合、ゼロ文字のコピー動作つまり何もしないことが保証されます[C99
§7.21.1/2]。関数`memcpy`が未定義動作を引き起こすのは、2つのポインタ値引数が指す領域のうち実際にオーバーラップしている部分でコピーが行われたときです[C99
§7.21.2.1/2]。


CC BY-SA 3.0
2016-10-23T13:58:28.620
29842
2016-10-23T15:11:33.940
2017-04-13T12:52:38.920
-1
49
29673
answer