

C言語で foreach のような配列処理をするにはどうすればよいだろうと思い調べてみたところ、  
下記のようなマクロが紹介されているページを見つけました。

[C 言語: foreach マクロを利用する](https://blog.sarabande.jp/post/59731422637)

>
[code]

>     #define foreach(item, array) \
>     for (int keep = 1, \
>         count = 0,\
>         size = sizeof (array) / sizeof *(array); \
>         keep && count != size; \
>         keep = !keep, count++)  \
>         for (item = (array) + count; keep; keep = !keep) \
>  
[/code]

上記のコードを動かしてみた所、期待通りの動作をしてくれたのですが  
読み解こうとしたところ疑問点がありまして、２点、質問させてください。

  1. このマクロは Linux Kernel で定義されているとのことですが、どのファイルで定義されているかお分かりになる方いらっしゃいますでしょうか？

  2. このマクロの１つ目の for の条件式に keep && が必要な理由がわかりません。  
keep は 2つ目の for を1回だけ実行するために必要な条件だと理解しましたが、だとすると、 1つ目の for の条件式評価時に keep が 0
になることはあり得ないような気がします。  
この keep && は何を意図しての記載なのでしょうか？

もしかしたら読み違いでとんちんかんな質問をしておりましたらすみません。


CC BY-SA 4.0
2020-11-20T09:50:57.240
72097
2020-11-21T13:20:39.947
2020-11-20T16:31:21.073
3060
42810
question
c
C言語での foreach の実装について


外側のforループでkeepをテストしているのは、中のforループをbreakしたときに外側のforループも抜けるためじゃないでしょうか?


CC BY-SA 4.0
2020-11-20T13:57:58.443
72102
2020-11-20T13:57:58.443
9464
72097
answer


2 について、 break で脱出するためだと思いますが補足

通常の処理は

  1. 外forの初期化ブロックで `keep = TRUE`
  2. 外forの判定で `keep != FALSE` => 通過
  3. 内forの判定で `keep != FALSE` => 通過
  4. ループ内処理
  5. 内forの更新ブロックで `keep = FALSE`
  6. 外forの更新ブロックで `keep = TRUE`
  7. 2へ

ですが、ループ内で break が入ると、

  1. 外forの初期化ブロックで `keep = TRUE`
  2. 外forの判定で `keep != FALSE` => 通過
  3. 内forの判定で `keep != FALSE` => 通過
  4. break: 6へ
  5. ~~内forの更新ブロックで`keep = FALSE`~~
  6. 外forの更新ブロックで `keep = FALSE`
  7. 2へ => 脱出

と外側のループも脱出できます。

外forと内forの更新ブロックで `keep` を互いにフリップさせ、不整合が発生したら break したとして全体を脱出するわけですね。


CC BY-SA 4.0
2020-11-20T15:07:35.290
72105
2020-11-20T15:07:35.290
20885
72097
answer


【追記】orangecatさんの回答を試してみました。breakを考慮すると、`keep && count !=
size;`は1つ目のforループ抜け出すために必要な処理でした。

* * *

> ２．このマクロの１つ目の for の条件式に keep && が必要な理由がわかりません。 keep は 2つ目の for  
>  を1回だけ実行するために必要な条件だと理解しました が、だとすると、 1つ目のfor の条件式評価時に keep が 0  
>  になることはあり得ないような気がします。 この keep && は何を意図しての記載なのでしょうか？

私もkeepは2つ目のforループを1回だけ実行するための条件だと思います。  
` keep && count != size; \`の`keep && `は不要です。  
削っても問題ないと思います。

試しにkeepを削って、2つ目のforループを1回だけ実行するようにマクロを変えてみました。

[code]

    #define foreach(item, array) \
    for ( \
        int count = 0,\
        size = sizeof (array) / sizeof *(array); \
        count != size; \
        count++)  \
        for (item = (array) + count, i = 0;i < 1; i++ ) \
    
    
[/code]

このコードは制御変数iがitemと同じ型を前提としているため問題がありますが、arrayの各要素について、foreachに続く分を要素数分だけ繰り返し実行します。keepは繰り返しを実現する処理には無関係です。

以下は確認のためのコードと実行結果です。  
【コード】

[code]

    #include <stdio.h>
    #define foreach(item, array) \
    for (int keep = 1, \
        count = 0,\
        size = sizeof (array) / sizeof *(array); \
        keep && count != size; \
        keep = !keep, count++)  \
        for (item = (array) + count; keep; keep = !keep) \
    
    #define foreach2(item, array) \
    for ( \
        int count = 0,\
        size = sizeof (array) / sizeof *(array); \
        count != size; \
        count++)  \
        for (item = (array) + count, i = 0;i < 1; i++ ) \
    
    
    int main (void) {
        int values[] = { 10, 20, 30, 40, 50 };
        foreach(int *v, values) {
            printf("value: %d\n", *v);
        }
        printf("--------------------------\n");
        foreach2(int *v, values) {
            printf("value: %d\n", *v);
        }
        return 0;
    }
    
[/code]

【実行結果】

[code]

    $ clang -Weverything qw.c;
    $ ./a.out
    value: 10
    value: 20
    value: 30
    value: 40
    value: 50
    --------------------------
    value: 10
    value: 20
    value: 30
    value: 40
    value: 50
    
[/code]


CC BY-SA 4.0
2020-11-20T15:38:59.350
72106
2020-11-21T13:20:39.947
2020-11-21T13:20:39.947
35558
35558
72097
answer


1について回答します。手元のLinux 5.2.13のソースコード中にはそのようなforeachマクロは実装されていないようでした。

記事が投稿された2013年当時のカーネルのソースコードを[kernel.org](https://mirrors.edge.kernel.org/pub/linux/kernel/)からダウンロードして探してみると、何か見つかるかもしれません。


CC BY-SA 4.0
2020-11-21T11:43:27.527
72125
2020-11-21T11:43:27.527
7500
72097
answer