C에 "foreach"루프 구조가 있습니까?


110

거의 모든 언어에는 foreach루프 또는 이와 유사한 것이 있습니다. C에 하나가 있습니까? 예제 코드를 게시 할 수 있습니까?


1
foreach무엇의 " "?
ALK

foreachC 프로그램에서 루프를 작성하는 것이 얼마나 어려웠 을까요?
MD XF

답변:


195

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 *"를 제거 할 수 있습니다. 루프 내부는 ... ") foreach는 (V, 값" "(대해서 typeof ((배열) +0) 항목에 대한 = ..."그럼 당신은으로 호출 할 수 같은 것을하게
린더

배열 예제에서 두 개의 for 루프가 필요한 이유는 무엇입니까? 이것에 대해 : #define foreach(item, array) int count=0, size=sizeof(array)/sizeof(*(array)); for(item = (array); count != size; count++, item = (array)+count)내가 볼 수있는 한 가지 문제는 변수의 개수와 크기가 for 루프 외부에 있으며 충돌을 일으킬 수 있다는 것입니다. 이것이 두 개의 for 루프를 사용하는 이유입니까? [여기에 코드 붙여 넣기 ( pastebin.com/immndpwS )]
Lazer

3
@eSKay 예 고려하십시오 if(...) foreach(int *v, values) .... 루프 외부에 있으면 확장되어 if(...) int count = 0 ...; for(...) ...;중단됩니다.
Johannes Schaub-litb

1
당신은 "휴식"을 사용하는 경우는 외부 루프를 중단하지 않습니다 @rem
요하네스 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 2010-06-28

9
@Rizo 아니요, 점은 C99 지정 이니셜 라이저 구문의 일부입니다 . 참조 en.wikipedia.org/wiki/C_syntax#Initialization
판사 Maygarden

@Rizo : 연결 목록을 작성하는 정말 해키 한 방법이기도합니다. 이 데모에서는 할 수 있지만 실제로 는 그렇게하지 마십시오!
Donal Fellows

@Donal "해키"로 만드는 것은 무엇입니까?
판사 Maygarden

2
@Judge : 글쎄요, 한 가지는 "놀라운"수명을 가지고 있습니다 (요소를 제거하는 코드로 작업하는 경우 충돌 할 가능성이 있습니다 free()). 또 다른 경우에는 정의 내부의 값에 대한 참조가 있습니다. 그것은 너무 영리한 무언가의 정말 예입니다. 코드는 의도적으로 영리함을 추가하지 않고도 충분히 복잡합니다. Kernighan의 격언 ( stackoverflow.com/questions/1103299/… )이 적용됩니다!
Donal Fellows

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

예를 조금 살려 냈습니다.
Adam Peck

원래 포인터를 변경하면 다음과 같이 할 수 있습니다. char * s; s = "..."; for (char * it = s; it! = NULL; it ++) {/ * it point to the character * / }
hiena

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 차원 배열에서 작동하는 것 같습니다.
  • 매크로 ( p, item) 에서 생성 된 모든 변수 는 루프 범위 밖에서 보이지 않습니다 (for 루프 헤더에서 선언 되었기 때문에).

단점 :

  • 다차원 배열에서는 작동하지 않습니다.
  • typeof()GNU 확장이 아닌 에 의존합니다 .표준 C의 일부가
  • 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에는 각 구성에 대한 a가 없지만 항상 배열의 끝을 지나서 하나에 대한 관용적 표현이 (&arr)[1]있습니다. 이를 통해 다음과 같이 각 루프에 대한 간단한 관용어를 작성할 수 있습니다.

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

3
확실하지 않다면 이것은 잘 정의되어 있습니다. (&arr)[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++).
룬딘

1
@Lundin 이것은 잘 정의되어 있습니다. 맞습니다. 배열의 끝을 지나서 하나의 배열이지만 해당 배열 유형은이 컨텍스트 (표현식)에서 포인터로 변환되고 그 포인터는 배열의 끝을 지나서 하나입니다.
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

> 예제 코드가 C 구문으로 작성되지 않았다는 점을 언급해야합니다. 맞습니다. 감사합니다. 게시물을 편집하겠습니다.
ChrisW

@ monjardin-> 구조체에서 함수에 대한 포인터를 정의 할 수 있는지 확인하고 이와 같이 호출하는 데 문제가 없습니다.
Ilya

2

다음은 내가 C를 사용할 때 사용하는 것입니다. 같은 범위에서 동일한 항목 이름을 두 번 사용할 수는 없지만 우리 모두가 멋진 새 컴파일러를 사용하는 것은 아니기 때문에 실제로 문제가되지는 않습니다.

#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-Monica 복원

이전 접근 방식의 포인터를 사용하더라도 결과는 '정의되지 않은 동작'인 것 같습니다. 오 잘. 이중 for 루프 접근 방식을 신뢰하십시오!
Watercycle 2015-10-22

이 버전은 범위를 오염시키고 동일한 범위에서 두 번 사용하면 실패합니다. 또한 가새가없는 블록으로 작동하지 않습니다 (예if ( bla ) FOREACH(....) { } else....
MM

1
1, C는 범위 오염의 언어이며 우리 중 일부는 이전 컴파일러로 제한됩니다. 2, 자신을 반복하지 마십시오. 3, 예, 불행히도 조건부 for 루프가 되려면 중괄호가 있어야합니다. for 루프에서 변수 선언을 지원하는 컴파일러에 액세스 할 수 있다면 반드시 수행하십시오.
Watercycle

@Watercycle : 나는 FOREACH네임 스페이스 오염을 피하기 위해 c99 구문을 사용 하는 대체 버전으로 답변을 자유롭게 편집했습니다 .
chqrlie

1

"중단"또는 "계속"을 사용하는 경우 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. 루프를 중첩 할 때 이름 지정 문제가있을 수 있습니다. 항목 및 인덱스 이름을 매크로에 대한 매개 변수로 만들 수 있습니다.

수정 : 다음은 수락 된 답변의 수정 된 버전입니다 foreach. 사용자가 지정할 수 있습니다 start인덱스는 size그래서 썩은 배열 (포인터)에 작동하는지, 대한 필요 int*와 변화 count != sizei < size대비해서 사용자가 실수로 수정이 '나'보다 더 할 수 size및 무한 루프에 박히면서.

#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. 배열을 포인트로 구문 분석 할 때 수신자는 배열의 길이를 알지 못하므로 배열의 끝에 도달했을 때 알 수있는 방법이 없습니다. 기억하세요, Cint* 에서 int를 포함하는 메모리 주소를 가리키는 점을 . 순서대로 배치되는 정수 수에 대한 정보를 포함하는 헤더 오브젝트가 없습니다. 따라서 프로그래머는이를 추적해야합니다.

그러나 목록의 경우 다음과 유사한 것을 구현하기가 쉽습니다. for-each 루프 .

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

배열에 대해 비슷한 것을 얻기 위해 두 가지 중 하나를 수행 할 수 있습니다.

  1. 첫 번째 요소를 사용하여 배열의 길이를 저장하십시오.
  2. 길이와 배열에 대한 포인터를 보유하는 구조체로 배열을 감 쌉니다.

다음은 이러한 구조체의 예입니다.

typedef struct job_t {
   int count;
   int* arr;
} arr_t;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.