




В 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.
Немного конкретизировал пример.
вы меняете исходный указатель, я бы сделал что-то вроде: char * s; s = "..."; for (char * it = s; it! = NULL; it ++) {/ * он указывает на символ * / }
В C есть ключевые слова "для" и "пока". Если оператор 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 ...
}
Вы должны упомянуть, что ваш примерный код написан не на синтаксисе C.
> Вы должны упомянуть, что ваш примерный код написан не на синтаксисе C. Вы правы, спасибо: я отредактирую сообщение.
@ monjardin-> уверен, что вы можете просто определить указатель на функцию в структуре, и нет проблем сделать такой вызов.
В 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++ имеет собственный синтаксис для каждого, называемый "диапазон на основе для"
Если у вас есть оператор «typeof» (расширение gcc; довольно часто встречается во многих других компиляторах), вы можете избавиться от этого «int *». Внутренний цикл for становится чем-то вроде "for (typeof ((array) +0) item = ...". Затем вы можете вызывать как "foreach (v, values) ..."
Зачем нам нужны два цикла 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)]
@eSKay да считай if (...) foreach(int *v, values) .... Если они находятся вне цикла, он расширяется до if (...) int count = 0 ...; for(...) ...; и прерывается.
Для чего нужна переменная keep?
У меня есть несколько вопросов. Будет ли это сломано, если пользователь сделает foreach (int * keep, keep)? Кроме того, что делает скобка вокруг массива во внутреннем цикле?
#define for_each_item (item, listHead) \ for (T * item = listHead-> next; item! = NULL; item = item-> next)
@Russell Да, к сожалению, это сломается. Если вы используете это для реального кода, вам лучше всего использовать странные имена для keep и других.
@ JohannesSchaub-litb: было бы лучше, если бы вы обновили свой ответ, включив в него цикл на основе диапазона, представленный C++ 11.
Рекомендовать int -> size_t в for(size_t keep = 1, \ ...
У меня были небольшие проблемы с пониманием ваших двух утверждений keep = !keep. Я думаю, что следующее работает так же хорошо и (на мой взгляд) немного понятнее: #define array_elements(arr) ( sizeof(arr) / sizeof(arr[0]) )#define array_for(item, array) for(unsigned int cont=1, i=0; i<array_elements(array); cont=1, i++) for(item=&(array)[i]; cont; cont=0)
@rem он не прерывает внешний цикл, если вы используете "break"
@rem, однако, вы можете упростить мой код, изменив внутреннее «keep =! keep» на «keep = 0». Мне понравилась «симметрия», поэтому я просто использовал отрицание, а не прямое присвоение.
Я понял, что вы также можете полностью исключить вложенный цикл, увеличив указатель вместо отдельного итератора: #define array_for(item, array) for(typeof(array[0])* item=array; item < &array[sizeof(array) / sizeof(array[0])]; item++)
@rem да, это возможно. Но не переносной. Typeof нестандартный
T* - это что именно?
@rem Ваше решение возможно без typeof. Вместо этого просто объявите свою переменную вне цикла for: int *v; array_for(v, values) { ... }
Макрос for_each_item работает так же только для навязчивых списков, где следующий указатель является частью самих элементов.
Макросы плохие. Я думаю, вам следует использовать статические встроенные функции вместо использования таких макросов
Вот полный пример программы для каждого макроса в 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;
}
Что делает точка в определении list[]? Не могли бы вы просто написать next вместо .next?
@Rizo Нет, точка является частью синтаксиса C99 назначенные инициализаторы. См. en.wikipedia.org/wiki/C_syntax#Initialization
@Rizo: Также обратите внимание, что это действительно хакерский способ создания связного списка. Это годится для этой демонстрации, но не делает это на практике!
@Donal Что делает его "хакерским"?
@Judge: Ну, с одной стороны, у него «удивительное» время жизни (если вы работаете с кодом, который удаляет элементы, есть вероятность, что у вас произойдет сбой в free()), а с другой стороны, он имеет ссылку на значение внутри своего определения. Это действительно пример чего-то чертовски умного; код достаточно сложен, но без намеренного добавления в него умных способностей. Применяется афоризм Кернигана (stackoverflow.com/questions/1103299/…)!
Это довольно старый вопрос, но я должен опубликовать его. Это цикл 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.
Ответ Эрика не работает, когда вы используете "break" или "continue".
Это можно исправить, переписав первую строку:
Исходная строка (переформатированная):
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)
Если вы сравните это с циклом Йоханнеса, вы увидите, что он на самом деле делает то же самое, только немного сложнее и уродливее.
Вот простой, одиночный цикл 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
Вот что я использую, когда застрял на 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]. Возможно, добавьте примеры использования для ясности.
Даже с указателями в моем предыдущем подходе результат выглядит как «неопределенное поведение». Ну что ж. Доверьтесь подходу с двойным циклом!
Эта версия загрязняет область видимости и выйдет из строя, если будет использоваться дважды в одной и той же области. Также не работает как блок без скобок (например, if ( bla ) FOREACH(....) { } else....
1, C - это язык загрязнения области видимости, некоторые из нас ограничиваются более старыми компиляторами. 2, не повторяйся / будь описательным. 3, да, к сожалению, у вас ДОЛЖНЫ быть фигурные скобки, если это будет условный цикл for (как правило, люди так и делают). Если у вас есть доступ к компилятору, который поддерживает объявления переменных в цикле for, обязательно сделайте это.
@Watercycle: Я взял на себя смелость отредактировать ваш ответ с помощью альтернативной версии FOREACH, которая использует синтаксис c99, чтобы избежать загрязнения пространства имен.
Хотя C не имеет a для каждой конструкции, он всегда имел идиоматическое представление для одного за концом массива (&arr)[1]. Это позволяет вам написать простую идиоматику для каждого цикла следующим образом:
int arr[] = {1,2,3,4,5};
for(int *a = arr; a < (&arr)[1]; ++a)
printf("%d\n", *a);
Если не уверен, что это четко определено. (&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++).
@Lundin Это хорошо определено. Вы правы, это один массив за концом массива, но этот тип массива преобразуется в указатель в этом контексте (выражение), и этот указатель находится за концом массива.
Если вы планируете работать с указателями на функции
#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 (не уверен).
Как вы, наверное, уже знаете, в 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(), который является расширением GNU, нет частью стандарта CЧтобы сэкономить время, вот как вы можете это проверить:
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; не тестировал другие компиляторы.
C не имеет реализации 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" чего?