Есть ли в C конструкция цикла foreach? - PullRequest
99 голосов
/ 30 декабря 2008

Почти во всех языках есть foreach петля или что-то подобное. Есть ли у С? Можете ли вы опубликовать пример кода?

Ответы [ 13 ]

181 голосов
/ 30 декабря 2008

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, называемый «range based for»

11 голосов
/ 30 декабря 2008

Вот полный пример программы макроса для каждого в C99:

#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;
}
8 голосов
/ 30 декабря 2008

В C. нет foreach

Вы можете использовать цикл for для циклического прохождения данных, но длина должна быть известна или данные должны быть завершены известным значением (например, null).

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

for(;nullTerm != NULL;nullTerm++)
{
    //nullTerm will now point to the next character.
}
5 голосов
/ 21 июля 2010

Это довольно старый вопрос, но я должен опубликовать это. Это цикл foreach для GNU C99.

#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;
}

Этот код был протестирован для работы с gcc, icc и clang в GNU / Linux.

4 голосов
/ 06 ноября 2017

Как вы, наверное, уже знаете, в 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!).
  • Кажется, работает на любом одномерном массиве
  • Все переменные, созданные в макросе (p, item), не видны вне область действия цикла (поскольку они объявлены в заголовке цикла for).

Недостатки:

  • Не работает для многомерных массивов
  • Полагается на typeof(), который является расширением gcc; НЕ является частью стандарта C
  • Поскольку он объявляет переменные в заголовке цикла for, он работает только в C11 или новее.

Чтобы сэкономить время, вот как вы можете это проверить:

typedef struct _point {
    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; не уверен насчет других компиляторов.

4 голосов
/ 02 июня 2016

Хотя C не имеет a для каждой конструкции, у него всегда было идиоматическое представление за один конец конца массива (&arr)[1]. Это позволяет вам написать простой идиоматический код для каждого цикла следующим образом:

int arr[] = {1,2,3,4,5};
for(int *a = arr; a < (&arr)[1]; ++a)
    printf("%d\n", *a);
2 голосов
/ 30 декабря 2008

C имеет ключевые слова for и while. Если оператор foreach на языке, подобном C #, выглядит следующим образом ...

foreach (Element element in collection)
{
}

... тогда эквивалент этого оператора foreach в C может быть таким:

for (
    Element* element = GetFirstElement(&collection);
    element != 0;
    element = GetNextElement(&collection, element)
    )
{
    //TODO: do something with this element instance ...
}
1 голос
/ 15 марта 2018

C не имеет реализации for-each. При анализе массива как точки получатель не знает, какова длина массива, поэтому невозможно определить, когда вы достигнете конца массива. Помните, в C int* указывается адрес памяти, содержащий 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;
1 голос
/ 15 мая 2017

Если вы планируете работать с указателями функций

#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 голос
/ 27 августа 2015

Вот один простой цикл 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 != size на i < size на случай, если пользователь случайно изменит 'i' на быть больше 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...