Fast alle Sprachen haben eine foreach
Schleife oder etwas ähnliches. Hat C einen? Kannst du einen Beispielcode posten?
Hat C ein “foreach”-Schleifenkonstrukt?
Frage
Johannes Schaub – litb
C hat kein foreach, aber Makros werden häufig verwendet, um dies zu emulieren:
#define for_each_item(item, list) \
for(T * item = list->head; item != NULL; item = item->next)
Und kann gerne verwendet werden
for_each_item(i, processes) {
i->wakeup();
}
Auch eine Iteration über ein Array ist möglich:
#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)
Und kann gerne verwendet werden
int values[] = { 1, 2, 3 };
foreach(int *v, values) {
printf("value: %d\n", *v);
}
Bearbeiten: Falls Sie auch an C++-Lösungen interessiert sind, C++ hat eine native for-each-Syntax namens “range based for”.
-
Wenn Sie den “typeof”-Operator (gcc-Erweiterung; bei vielen anderen Compilern ziemlich üblich) haben, können Sie dieses “int *” loswerden. Die innere for-Schleife wird zu etwas wie “for(typeof((array)+0) item = …” Dann können Sie als “foreach( v, values ) …” aufrufen.
– schlanker
6. August 2009 um 4:46 Uhr
-
@eSKay ja erwägen
if(...) foreach(int *v, values) ...
. Wenn sie sich außerhalb der Schleife befinden, wird sie erweitertif(...) int count = 0 ...; for(...) ...;
und wird brechen.– Johannes Schaub – litb
9. Mai 2010 um 11:37 Uhr
-
@rem es unterbricht die äußere Schleife nicht, wenn Sie “break” verwenden
– Johannes Schaub – litb
26. September 2016 um 11:05 Uhr
-
@rem Sie können meinen Code jedoch vereinfachen, wenn Sie das innere “keep = !keep” in “keep = 0” ändern. Ich mochte die “Symmetrie”, also habe ich nur Negation und keine direkte Zuweisung verwendet.
– Johannes Schaub – litb
26. September 2016 um 11:12 Uhr
-
@rem ja das ist möglich. Aber nicht tragbar. Typeof ist kein Standard
– Johannes Schaub – litb
28. September 2016 um 17:53 Uhr
Hier ist ein vollständiges Programmbeispiel eines for-each-Makros in 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;
}
-
Was macht der Punkt in der
list[]
Definition? Könntest du nicht einfach schreibennext
Anstatt von.next
?– Rizo
28. Juni 2010 um 10:25 Uhr
-
@Rizo Nein, der Punkt ist Teil der Syntax für C99 designierte Initialisierer. Sehen en.wikipedia.org/wiki/C_syntax#Initialisierung
– Richter Maygarden
28. Juni 2010 um 15:27 Uhr
-
@Rizo: Beachten Sie auch, dass dies eine wirklich hackige Art ist, eine verknüpfte Liste zu erstellen. Für diese Demo reicht es aber nicht Mach es in der Praxis so!
– Donal Fellows
21. Juli 2010 um 15:18 Uhr
-
@Donal Was macht es “hacky”?
– Richter Maygarden
22. Juli 2010 um 13:17 Uhr
-
@Judge: Nun, zum einen hat es eine „überraschende“ Lebensdauer (wenn Sie mit Code arbeiten, der Elemente entfernt, besteht die Möglichkeit, dass Sie abstürzen
free()
) und zum anderen hat es einen Verweis auf den Wert in seiner Definition. Es ist wirklich ein Beispiel für etwas, das einfach zu verdammt schlau ist; Code ist komplex genug, ohne ihm absichtlich Cleverness hinzuzufügen. Kernighans Aphorismus (stackoverflow.com/questions/1103299/…) trifft zu!– Donal Fellows
22. Juli 2010 um 23:25 Uhr
Said Baig
Wie Sie wahrscheinlich bereits wissen, gibt es in C keine Schleife im “foreach”-Stil.
Obwohl hier bereits unzählige großartige Makros bereitgestellt werden, um dies zu umgehen, finden Sie dieses Makro vielleicht nützlich:
// "length" is the length of the array.
#define each(item, array, length) \
(typeof(*(array)) *p = (array), (item) = *p; p < &((array)[length]); p++, (item) = *p)
…die verwendet werden können for
(wie in for each (...)
).
Vorteile dieses Ansatzes:
item
innerhalb der for-Anweisung deklariert und inkrementiert (genau wie in Python!).- Scheint auf jedem 1-dimensionalen Array zu funktionieren
- Alle im Makro erstellten Variablen (
p
,item
), sind außerhalb des Gültigkeitsbereichs der Schleife nicht sichtbar (da sie im Header der for-Schleife deklariert sind).
Nachteile:
- Funktioniert nicht für mehrdimensionale Arrays
- Beruht auf
typeof()
das ist eine GNU-Erweiterung, nicht Teil von Standard C - Da es Variablen im Header der for-Schleife deklariert, funktioniert es nur in C11 oder höher.
Um Ihnen etwas Zeit zu sparen, können Sie es folgendermaßen testen:
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;
}
Scheint standardmäßig auf gcc und clang zu funktionieren; habe keine anderen Compiler getestet.
Adam Pick
In C gibt es kein foreach.
Sie können eine for-Schleife verwenden, um die Daten zu durchlaufen, aber die Länge muss bekannt sein oder die Daten müssen mit einem bekannten Wert (z. B. null) abgeschlossen werden.
char* nullTerm;
nullTerm = "Loop through my characters";
for(;nullTerm != NULL;nullTerm++)
{
//nullTerm will now point to the next character.
}
Während C nicht für jedes Konstrukt ein Konstrukt hat, hatte es immer eine idiomatische Darstellung für eins nach dem Ende eines Arrays (&arr)[1]
. Auf diese Weise können Sie für jede Schleife eine einfache Idiomatik wie folgt schreiben:
int arr[] = {1,2,3,4,5};
for(int *a = arr; a < (&arr)[1]; ++a)
printf("%d\n", *a);
-
Wenn nicht so sicher, ist dies wohldefiniert.
(&arr)[1]
bedeutet nicht ein Array-Element nach dem Ende des Arrays, sondern ein Array nach dem Ende des Arrays.(&arr)[1]
ist nicht das letzte Element des Arrays [0]es ist das Array [1]der in einen Zeiger auf das erste Element (von array [1]). Ich glaube, es wäre viel besser, sicherer und idiomatischer zu tunconst int* begin = arr; const int* end = arr + sizeof(arr)/sizeof(*arr);
und dannfor(const int* a = begin; a != end; a++)
.– Ludin
5. Oktober 2016 um 9:22 Uhr
-
@Lundin Das ist gut definiert. Sie haben Recht, es ist ein Array nach dem Ende des Arrays, aber dieser Array-Typ wird in diesem Kontext in einen Zeiger (einen Ausdruck) konvertiert, und dieser Zeiger ist eins nach dem Ende des Arrays.
– Steve Cox
5. Oktober 2016 um 14:09 Uhr
Joe D
Dies ist eine ziemlich alte Frage, aber ich dachte, ich sollte dies posten. Es ist eine Foreach-Schleife für 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;
}
Dieser Code wurde getestet, um mit gcc, icc und clang unter GNU/Linux zu funktionieren.
-
Wenn nicht so sicher, ist dies wohldefiniert.
(&arr)[1]
bedeutet nicht ein Array-Element nach dem Ende des Arrays, sondern ein Array nach dem Ende des Arrays.(&arr)[1]
ist nicht das letzte Element des Arrays [0]es ist das Array [1]der in einen Zeiger auf das erste Element (von array [1]). Ich glaube, es wäre viel besser, sicherer und idiomatischer zu tunconst int* begin = arr; const int* end = arr + sizeof(arr)/sizeof(*arr);
und dannfor(const int* a = begin; a != end; a++)
.– Ludin
5. Oktober 2016 um 9:22 Uhr
-
@Lundin Das ist gut definiert. Sie haben Recht, es ist ein Array nach dem Ende des Arrays, aber dieser Array-Typ wird in diesem Kontext in einen Zeiger (einen Ausdruck) konvertiert, und dieser Zeiger ist eins nach dem Ende des Arrays.
– Steve Cox
5. Oktober 2016 um 14:09 Uhr
chqrlie
Folgendes verwende ich, wenn ich bei C feststecke. Sie können denselben Elementnamen nicht zweimal im selben Bereich verwenden, aber das ist nicht wirklich ein Problem, da nicht alle von uns nette neue Compiler verwenden können 🙁
#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 */
Verwendungszweck:
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);
BEARBEITEN: Hier ist eine Alternative für FOREACH
Verwendung der c99-Syntax, um Namespace-Verschmutzung zu vermeiden:
#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)
-
Notiz:
VAR(i) < (size) && (item = array[VAR(i)])
würde aufhören, sobald das Array-Element einen Wert von 0 hatte. Verwenden Sie dies also mitdouble Array[]
kann nicht durch alle Elemente iterieren. Scheint, als ob der Schleifentest der eine oder andere sein sollte:i<n
oderA[i]
. Fügen Sie zur Verdeutlichung möglicherweise Beispielanwendungsfälle hinzu.– chux – Wiedereinsetzung von Monica
16. Oktober 2015 um 17:32 Uhr
-
Selbst mit Hinweisen in meinem vorherigen Ansatz scheint das Ergebnis ein „undefiniertes Verhalten“ zu sein. Nun ja. Vertrauen Sie dem Double-for-Loop-Ansatz!
– Wasserkreislauf
22. Oktober 2015 um 1:54 Uhr
-
Diese Version verschmutzt den Geltungsbereich und schlägt fehl, wenn sie zweimal im selben Geltungsbereich verwendet wird. Funktioniert auch nicht als unverspannter Block (zB
if ( bla ) FOREACH(....) { } else....
– MM
5. November 2015 um 22:15 Uhr
-
1, C ist die Sprache der Bereichsverschmutzung, einige von uns sind auf ältere Compiler beschränkt. 2, Wiederholen Sie sich nicht / seien Sie beschreibend. 3, ja, leider MÜSSEN Sie Klammern haben, wenn es eine bedingte for-Schleife sein soll (die Leute tun es normalerweise sowieso). Wenn Sie Zugriff auf einen Compiler haben, der Variablendeklarationen in einer for-Schleife unterstützt, tun Sie dies auf jeden Fall.
– Wasserkreislauf
5. November 2015 um 23:13 Uhr
-
@Watercycle: Ich habe mir erlaubt, Ihre Antwort mit einer alternativen Version von zu bearbeiten
FOREACH
die die c99-Syntax verwendet, um Namespace-Verschmutzung zu vermeiden.– chqrlie
17. August 2019 um 21:14 Uhr
“
foreach
” von was?– alk
6. März 2016 um 11:13 Uhr
Wie schwer wäre es gewesen, zu versuchen, a zu schreiben
foreach
Schleife in einem C-Programm?– MDXF
17. Oktober 2016 um 0:04 Uhr