Есть ли в C конструкция цикла foreach?
Почти все языки имеют foreach
петля или что-то подобное. Есть ли у С? Можете ли вы опубликовать пример кода?
15 ответов
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"
Как вы, наверное, уже знаете, в 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; не уверен насчет других компиляторов.
Вот полный пример программы макроса для каждого в 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;
}
В C. нет foreach
Вы можете использовать цикл for для циклического прохождения данных, но длина должна быть известна или данные должны быть завершены известным значением (например, null).
char* nullTerm;
nullTerm = "Loop through my characters";
for(;nullTerm != NULL;nullTerm++)
{
//nullTerm will now point to the next character.
}
Хотя C не имеет для каждой конструкции, он всегда имел идиоматическое представление для одного после конца массива (&arr)[1]
, Это позволяет вам написать простой идиоматический код для каждого цикла следующим образом:
int arr[] = {1,2,3,4,5};
for(int *a = arr; a < (&arr)[1]; ++a)
printf("%d\n", *a);
Это довольно старый вопрос, но я должен опубликовать это. Это цикл 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.
Вот что я использую, когда я застрял с 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);
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 ...
}
Вот один простой цикл 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
Ответ Эрика не работает, когда вы используете "перерыв" или "продолжить".
Это можно исправить, переписав первую строку:
Исходная строка (переформатированная):
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)
Если вы сравните это с циклом Йоханнеса, вы увидите, что он на самом деле делает то же самое, только немного сложнее и уродливее.
Если вы планируете работать с указателями функций
#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 (не уверен).
С не имеет реализации for-each
, При разборе массива как точки получатель не знает, какова длина массива, поэтому невозможно определить, когда вы достигнете конца массива. Помните, в C int*
является точкой адреса памяти, содержащей int. Не существует объекта заголовка, содержащего информацию о том, сколько целых чисел размещено в последовательности. Таким образом, программист должен следить за этим.
Однако для списков легко реализовать что-то похожее на for-each
петля.
for(Node* node = head; node; node = node.next) {
/* do your magic here */
}
Чтобы добиться чего-то похожего для массивов, вы можете сделать одну из двух вещей.
- используйте первый элемент для хранения длины массива.
- обернуть массив в структуру, которая содержит длину и указатель на массив.
Ниже приведен пример такой структуры:
typedef struct job_t {
int count;
int* arr;
} arr_t;
Вот еще несколькоforeach
макросы:
Они полностью соответствуют стандартам, но требуют C99.
Стандартный
- не может быть константным
- Искажает указатель на текущий элемент: при необходимости вы можете изменить его на более понятное имя.
#include <stdio.h>
#define foreach(type, varName, array, count) for ( \
type *__p_##varName = array, varName = array[0]; \
__p_##varName != array + count; \
varName = *(++__p_##varName))
int main()
{
int arr[] = { 1,2,3,4,5 };
foreach(int, myInt, arr, 5)
{
printf("%d\n", myInt);
}
return 0;
}
Указатель
- может быть константным
-
varName
будет иметьtype*
#include <stdio.h>
#define foreach(type, varName, array, count) for ( \
type *varName = array; \
varName != array + count; \
++varName)
int main()
{
int arr[] = { 1,2,3,4,5 };
foreach(int const, myInt, arr, 5)
{
printf("%d\n", *myInt);
}
return 0;
}
Тело
-
type
может быть константным - Требуется, чтобы тело цикла было передано в качестве аргумента.
- Искажает указатель на текущий элемент: вы можете изменить
__p_##varName
при необходимости на более дружелюбное имя.
#include <stdio.h>
#define foreach(type, varName, array, count, body) for ( \
type *__p_##varName = array; \
__p_##varName != array + count; \
++__p_##varName) \
{ type varName = *__p_##varName; { body } }
int main()
{
int arr[] = { 1,2,3,4,5 };
foreach(int const, myInt, arr, 5,
{
printf("%d\n", myInt);
})
// Also supports single-statement style:
foreach(int const, myInt, arr, 5,
printf("%d\n", myInt);)
return 0;
}
Однако будьте осторожны: определение пользовательских структур управления с помощью макросов может ухудшить читаемость кода.
Благодаря принятому ответу я смог исправить свой макрос C++. Это не чисто, но оно поддерживает вложение, разрыв и продолжение, и для list<t>
это позволяет перебирать t&
вместо list<t>::elem*
так что вам не нужно разыскивать.
template <typename t> struct list { // =list=
struct elem {
t Data;
elem* Next;
};
elem* Head;
elem* Tail;
};
#define for_list3(TYPE, NAME, NAME2, LIST) \
bool _##NAME##NAME2 = true;\
for (list<TYPE>::elem* NAME##_E = LIST.Head;\
NAME##_E && _##NAME##NAME2;\
_##NAME##NAME2 = !_##NAME##NAME2, NAME##_E = NAME##_E->Next) \
for (auto& NAME = NAME##_E->Data; _##NAME##NAME2; _##NAME##NAME2=false)
#define for_list2(TYPE, NAME, NAME2, LIST) for_list3(TYPE, NAME, NAME2, LIST)
#define for_list(TYPE, NAME, LIST) for_list2(TYPE, NAME, __COUNTER__, LIST)
void example() {
list<string> Words;
for_list(string, Word, Words) {
print(Word);
}
}
С for_each_item
макрос из принятого ответа, вам придется сделать это:
void example2() {
list<string> Words;
for_each_item(string, Elem, Words) {
string& Word = Elem->Data;
print(Word);
}
}
Если вы хотите переписать его в чистую версию, не стесняйтесь редактировать.
Я определил foreach с помощью макроса и вспомогательной функции. Он допускает вложенные циклы (хотя и ограниченные), а элемент определяется только в рамках цикла. Единственная проблема в том, что макрос содержит 2 оператора, поэтому нетif(...) foreach(...) printf(...);
, но я бы не рекомендовал делать это с любым оператором.
Вы, вероятно, можете улучшить его с помощью gnutypeof
макрос и#define in ,
, если ты хочешь.
#define MAX_FOREACH_NESTING 16
#define foreach(type, item, set) \
helper_foreach_next(NULL, 0, NULL, 0, 1); \
for(type item;helper_foreach_next((char *) array, sizeof(array)/sizeof(array[0]), &item, sizeof(type), 0);)
int helper_foreach_next(char *array, size_t array_size, void *item, size_t item_size, int setup) {
static int nesting = -1;
static size_t indexes[MAX_FOREACH_NESTING] = { 0 };
if(setup) {
nesting++;
return 0;
}
if (indexes[nesting] < array_size) {
memcpy(item, array + indexes[nesting]*item_size, item_size);
indexes[nesting]++;
return 1;
} else {
indexes[nesting] = 0;
nesting--;
return 0;
}
}
Вы можете назвать это так:
int array1[] = { 3, 4, 5};
foreach(int, a, array1) {
foreach(int, b, array1) {
printf("%d-%d\n", a, b);
}
}