Cには「foreach」ループ構造がありますか?


109

ほとんどすべての言語にforeachループまたはそれに類似したものがあります。Cにはありますか?いくつかのサンプルコードを投稿できますか?


1
foreach」の何?
2016年

foreachCプログラムでループを書くのはどれほど大変でしたか?
MD XF

回答:


194

Cにはforeachはありませんが、それをエミュレートするためにマクロが頻繁に使用されます。

#define for_each_item(item, list) \
    for(T * item = list->head; item != NULL; item = item->next)

のように使用できます

for_each_item(i, processes) {
    i->wakeup();
}

配列の反復も可能です。

#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)

のように使用できます

int values[] = { 1, 2, 3 };
foreach(int *v, values) {
    printf("value: %d\n", *v);
}

編集:C ++ソリューションにも興味がある場合、C ++には「範囲ベースの」と呼ばれるネイティブのfor-each構文があります


1
「typeof」演算子(gcc拡張、他の多くのコンパイラではかなり一般的)がある場合は、その「int *」を取り除くことができます。forループの内側には、 "(typeof演算((配列)+0)項目に= ..."そして、あなたは"foreachの(V、値)..."と呼ぶことができるようなものになり
リアンダー

配列の例で2つのforループが必要なのはなぜですか?これについてはどうでしょう:#define foreach(item, array) int count=0, size=sizeof(array)/sizeof(*(array)); for(item = (array); count != size; count++, item = (array)+count)私が見ることができる1つの問題は、変数のカウントとサイズがforループの外にあり、競合を引き起こす可能性があることです。これが2つのforループを使用する理由ですか?[ここに貼り付けられたコード(pastebin.com/immndpwS)]
Lazer

3
@eSKayはいはい検討してくださいif(...) foreach(int *v, values) ...。それらがループの外にある場合、それは拡張しif(...) int count = 0 ...; for(...) ...;て壊れます。
ヨハネスシャウブ-litb

1
@remは、 "break"を使用しても外側のループを壊しません
Johannes Schaub-litb

1
@remただし、内部の「keep =!keep」を「keep = 0」に変更すると、コードを簡略化できます。私は「対称性」が好きだったので、単純な代入ではなく否定を使用しました。
Johannes Schaub-litb

11

C99のfor-eachマクロの完全なプログラム例を次に示します。

#include <stdio.h>

typedef struct list_node list_node;
struct list_node {
    list_node *next;
    void *data;
};

#define FOR_EACH(item, list) \
    for (list_node *(item) = (list); (item); (item) = (item)->next)

int
main(int argc, char *argv[])
{
    list_node list[] = {
        { .next = &list[1], .data = "test 1" },
        { .next = &list[2], .data = "test 2" },
        { .next = NULL,     .data = "test 3" }
    };

    FOR_EACH(item, list)
        puts((char *) item->data);

    return 0;
}

ドットはlist[]定義で何をしますか?next代わりに単に書いていただけません.nextか?
Rizo

9
@Rizoいいえ、ドットはC99指定イニシャライザの構文の一部です。en.wikipedia.org/wiki/C_syntax#Initializationを
Maygarden裁判官、10

@Rizo:これは、リンクされたリストを作成するための本当にハッキーな方法であることにも注意してください。このデモでは効果がありますが、実際にはそのようにはしないでください!
ドナルフェロー

@Donal何が「ハッキー」にするのですか?
Maygarden裁判官、2010

2
@裁判官:まあ、1つには「驚くべき」有効期間があり(要素を削除するコードを使用している場合は、でクラッシュする可能性がありますfree())、もう1つはその定義内の値への参照があります。これは実際、あまりにも賢いものの例です。意図的に賢さを追加することなく、コードは十分に複雑です。カーニハンの格言(stackoverflow.com/questions/1103299/…)が適用されます!
ドナルフェロー

9

Cにはforeachはありません。

forループを使用してデータをループできますが、長さを知っている必要があるか、データを既知の値(たとえばnull)で終了する必要があります。

char* nullTerm;
nullTerm = "Loop through my characters";

for(;nullTerm != NULL;nullTerm++)
{
    //nullTerm will now point to the next character.
}

nullTermポインタの初期化をデータセットの先頭に追加する必要があります。OPは、不完全なforループについて混乱する可能性があります。
cschol 2008

例を少し具体化しました。
Adam Peck

元のポインタを変更している場合は、次のようにします。char* s; s = "..."; for(char * it = s; it!= NULL; it ++){/ * it point the character * / }
hiena、2009

6

おそらくご存じのとおり、Cには「foreach」スタイルのループはありません。

これを回避するためにここで提供されているすばらしいマクロがすでにたくさんありますが、おそらくあなたはこのマクロが役立つことに気付くでしょう:

// "length" is the length of the array.   
#define each(item, array, length) \
(typeof(*(array)) *p = (array), (item) = *p; p < &((array)[length]); p++, (item) = *p)

...で使用できますfor(などfor each (...))。

このアプローチの利点:

  • item forステートメント内で宣言され、インクリメントされます(Pythonと同じです!)。
  • 任意の1次元配列で機能するようです
  • マクロ(pitem)で作成されたすべての変数は、ループのスコープ外では表示されません(それらはforループヘッダーで宣言されているため)。

短所:

  • 多次元配列では機能しません
  • 標準Cの一部でtypeof()なく、GNU拡張であるに依存します。
  • forループヘッダーで変数を宣言するため、C11以降でのみ機能します。

時間を節約するために、次の方法でテストできます。

typedef struct {
    double x;
    double y;
} Point;

int main(void) {
    double some_nums[] = {4.2, 4.32, -9.9, 7.0};
    for each (element, some_nums, 4)
        printf("element = %lf\n", element);

    int numbers[] = {4, 2, 99, -3, 54};
    // Just demonstrating it can be used like a normal for loop
    for each (number, numbers, 5) { 
        printf("number = %d\n", number);
        if (number % 2 == 0)
                printf("%d is even.\n", number);
    }

    char* dictionary[] = {"Hello", "World"};
    for each (word, dictionary, 2)
        printf("word = '%s'\n", word);

    Point points[] = {{3.4, 4.2}, {9.9, 6.7}, {-9.8, 7.0}};
    for each (point, points, 3)
        printf("point = (%lf, %lf)\n", point.x, point.y);

    // Neither p, element, number or word are visible outside the scope of
    // their respective for loops. Try to see if these printfs work
    // (they shouldn't):
    // printf("*p = %s", *p);
    // printf("word = %s", word);

    return 0;
}

デフォルトではgccとclangで動作するようです。他のコンパイラはテストしていません。


5

これはかなり古い質問ですが、私はこれを投稿する必要があります。これは、GNU C99のforeachループです。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define FOREACH_COMP(INDEX, ARRAY, ARRAY_TYPE, SIZE) \
  __extension__ \
  ({ \
    bool ret = 0; \
    if (__builtin_types_compatible_p (const char*, ARRAY_TYPE)) \
      ret = INDEX < strlen ((const char*)ARRAY); \
    else \
      ret = INDEX < SIZE; \
    ret; \
  })

#define FOREACH_ELEM(INDEX, ARRAY, TYPE) \
  __extension__ \
  ({ \
    TYPE *tmp_array_ = ARRAY; \
    &tmp_array_[INDEX]; \
  })

#define FOREACH(VAR, ARRAY) \
for (void *array_ = (void*)(ARRAY); array_; array_ = 0) \
for (size_t i_ = 0; i_ && array_ && FOREACH_COMP (i_, array_, \
                                    __typeof__ (ARRAY), \
                                    sizeof (ARRAY) / sizeof ((ARRAY)[0])); \
                                    i_++) \
for (bool b_ = 1; b_; (b_) ? array_ = 0 : 0, b_ = 0) \
for (VAR = FOREACH_ELEM (i_, array_, __typeof__ ((ARRAY)[0])); b_; b_ = 0)

/* example's */
int
main (int argc, char **argv)
{
  int array[10];
  /* initialize the array */
  int i = 0;
  FOREACH (int *x, array)
    {
      *x = i;
      ++i;
    }

  char *str = "hello, world!";
  FOREACH (char *c, str)
    printf ("%c\n", *c);

  return EXIT_SUCCESS;
}

このコードは、GNU / Linux上のgcc、icc、clangで動作するようにテストされています。


4

Cにはfor eachコンストラクトはありませんが、常に配列の終わりを過ぎたものの慣用的な表現がありました(&arr)[1]。これにより、次のように各ループの簡単な慣用句を記述できます。

int arr[] = {1,2,3,4,5};
for(int *a = arr; a < (&arr)[1]; ++a)
    printf("%d\n", *a);

3
そうでない場合、これは明確に定義されています。(&arr)[1]配列の終わりを過ぎた1つの配列項目を意味するのではなく、配列の終わりを過ぎた1つの配列を意味します。(&arr)[1]配列[0]の最後の項目ではなく、配列[1]です。これは、(配列[1]の)最初の要素へのポインターに減衰します。私はそれを行うにはより良い、より安全かつ慣用だろうと信じてconst int* begin = arr; const int* end = arr + sizeof(arr)/sizeof(*arr);、その後とfor(const int* a = begin; a != end; a++)
ランディン、2016年

1
@Lundinこれは明確に定義されています。そうです、それは配列の終わりを過ぎた1つの配列ですが、その配列タイプはこのコンテキスト(式)でポインターに変換され、そのポインターは配列の終わりを過ぎた1つです。
Steve Cox

2

Cには「for」および「while」キーワードがあります。C#のような言語のforeachステートメントが次のようになっている場合...

foreach (Element element in collection)
{
}

...次に、Cでのこのforeachステートメントと同等のものは次のようになります。

for (
    Element* element = GetFirstElement(&collection);
    element != 0;
    element = GetNextElement(&collection, element)
    )
{
    //TODO: do something with this element instance ...
}

1
サンプルコードはC構文で記述されていないことにも言及する必要があります。
cschol 2008

>サンプルコードはC構文で記述されていないことをお伝えください。そうです、ありがとうございます。投稿を編集します。
ChrisW 2008

@ monjardin->構造体で関数へのポインターを定義できることを確認してください。このように呼び出しても問題はありません。
イリヤ

2

これは、Cに悩まされているときに使用するものです。同じスコープで同じアイテム名を2回使用することはできませんが、私たち全員が新しいコンパイラを使用できるわけではないので、それは実際には問題になりません:(

#define FOREACH(type, item, array, size) \
    size_t X(keep), X(i); \
    type item; \
    for (X(keep) = 1, X(i) = 0 ; X(i) < (size); X(keep) = !X(keep), X(i)++) \
        for (item = (array)[X(i)]; X(keep); X(keep) = 0)

#define _foreach(item, array) FOREACH(__typeof__(array[0]), item, array, length(array))
#define foreach(item_in_array) _foreach(item_in_array)

#define in ,
#define length(array) (sizeof(array) / sizeof((array)[0]))
#define CAT(a, b) CAT_HELPER(a, b) /* Concatenate two symbols for macros! */
#define CAT_HELPER(a, b) a ## b
#define X(name) CAT(__##name, __LINE__) /* unique variable */

使用法:

int ints[] = {1, 2, 0, 3, 4};
foreach (i in ints) printf("%i", i);
/* can't use the same name in this scope anymore! */
foreach (x in ints) printf("%i", x);

編集:FOREACH名前空間の汚染を回避するためにc99構文を使用する別の方法を次に示します。

#define FOREACH(type, item, array, size) \
    for (size_t X(keep) = 1, X(i) = 0; X(i) < (size); X(keep) = 1, X(i)++) \
    for (type item = (array)[X(i)]; X(keep); X(keep) = 0)

注:VAR(i) < (size) && (item = array[VAR(i)])配列要素の値が0になると停止double Array[]します。したがって、これをで使用すると、すべての要素を反復できない場合があります。ループテストはどちらかである必要があります:i<nまたはA[i]。わかりやすくするために、サンプルのユースケースを追加する場合があります。
chux-モニカを2015年

私の以前のアプローチのポインターを使用しても、結果は「未定義の動作」のようです。しかたがない。ダブルforループアプローチを信頼してください!
2015年

このバージョンはスコープを汚染し、同じスコープで2回使用すると失敗します。ブレースなしのブロックとしても機能しません(例if ( bla ) FOREACH(....) { } else....
MM

1
1、Cはスコープ汚染の言語であり、私たちの一部は古いコンパイラに限定されています。2、自分自身を繰り返さないでください。3、ええ、残念ながら、条件付きforループになる場合は中かっこが必要です(通常、人々はとにかく行う)。forループで変数宣言をサポートするコンパイラーにアクセスできる場合は、必ずそうしてください。
ウォーターサイクル2015年

@Watercycle:FOREACH名前空間の汚染を回避するためにc99構文を使用する別のバージョンを使用して、自由に回答を編集しました。
chqrlie

1

「break」または「continue」を使用している場合、Ericの回答は機能しません。

これは、最初の行を書き換えることで修正できます。

元の行(再フォーマット):

for (unsigned i = 0, __a = 1; i < B.size(); i++, __a = 1)

修繕:

for (unsigned i = 0, __a = 1; __a && i < B.size(); i++, __a = 1)

それをヨハネスのループと比較すると、彼が実際に同じことをしていることがわかります。もう少し複雑で醜いです。


1

次に、単純なforループを示します。

#define FOREACH(type, array, size) do { \
        type it = array[0]; \
        for(int i = 0; i < size; i++, it = array[i])
#define ENDFOR  } while(0);

int array[] = { 1, 2, 3, 4, 5 };

FOREACH(int, array, 5)
{
    printf("element: %d. index: %d\n", it, i);
}
ENDFOR

必要に応じてインデックスにアクセスできます(i)と、繰り返し処理する現在のアイテム(it)。ループをネストするときに命名の問題が発生する可能性があることに注意してください。アイテム名とインデックス名をマクロのパラメーターにすることができます。

編集:承認された回答の修正版を次に示しますforeachstartインデックスを指定できます。sizeこれにより、減衰した配列(ポインター)で機能します。ユーザーが誤って 'i'を変更して無限ループに陥った場合に備えて、これint*を変更count != sizeする必要はありません。i < sizesize

#define FOREACH(item, array, start, size)\
    for(int i = start, keep = 1;\
        keep && i < size;\
        keep = !keep, i++)\
    for (item = array[i]; keep; keep = !keep)

int array[] = { 1, 2, 3, 4, 5 };
FOREACH(int x, array, 2, 5)
    printf("index: %d. element: %d\n", i, x);

出力:

index: 2. element: 3
index: 3. element: 4
index: 4. element: 5

1

関数ポインタを使用する予定の場合

#define lambda(return_type, function_body)\
    ({ return_type __fn__ function_body __fn__; })

#define array_len(arr) (sizeof(arr)/sizeof(arr[0]))

#define foreachnf(type, item, arr, arr_length, func) {\
    void (*action)(type item) = func;\
    for (int i = 0; i<arr_length; i++) action(arr[i]);\
}

#define foreachf(type, item, arr, func)\
    foreachnf(type, item, arr, array_len(arr), func)

#define foreachn(type, item, arr, arr_length, body)\
    foreachnf(type, item, arr, arr_length, lambda(void, (type item) body))

#define foreach(type, item, arr, body)\
    foreachn(type, item, arr, array_len(arr), body)

使用法:

int ints[] = { 1, 2, 3, 4, 5 };
foreach(int, i, ints, {
    printf("%d\n", i);
});

char* strs[] = { "hi!", "hello!!", "hello world", "just", "testing" };
foreach(char*, s, strs, {
    printf("%s\n", s);
});

char** strsp = malloc(sizeof(char*)*2);
strsp[0] = "abcd";
strsp[1] = "efgh";
foreachn(char*, s, strsp, 2, {
    printf("%s\n", s);
});

void (*myfun)(int i) = somefunc;
foreachf(int, i, ints, myfun);

しかし、これはgccでのみ機能すると思います(わかりません)。


1

Cにはの実装がありませんfor-each。配列をポイントとして解析する場合、レシーバーは配列の長さがわからないため、配列の最後に到達したことを通知する方法はありません。Cではint*、intを含むメモリアドレスへのポイントであることを覚えておいてください。シーケンスに配置される整数の数に関する情報を含むヘッダーオブジェクトはありません。したがって、プログラマはこれを追跡する必要があります。

ただし、リストの場合、for-eachループに似たものを実装するのは簡単です。

for(Node* node = head; node; node = node.next) {
   /* do your magic here */
}

配列に対して同様のことを達成するには、2つのことの1つを行うことができます。

  1. 最初の要素を使用して、配列の長さを格納します。
  2. 長さと配列へのポインタを保持する構造体で配列をラップします。

以下はそのような構造体の例です:

typedef struct job_t {
   int count;
   int* arr;
} arr_t;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.