Heeft C een “foreach” lusconstructie?

Bijna alle talen hebben een foreach-lusof iets dergelijks. Heeft C er een? Kun je wat voorbeeldcode posten?


Antwoord 1, autoriteit 100%

C heeft geen foreach, maar macro’s worden vaak gebruikt om dat te emuleren:

#define for_each_item(item, list) \
    for(T * item = list->head; item != NULL; item = item->next)

En kan worden gebruikt als

for_each_item(i, processes) {
    i->wakeup();
}

Iteratie over een array is ook mogelijk:

#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)

En kan worden gebruikt als

int values[] = { 1, 2, 3 };
foreach(int *v, values) {
    printf("value: %d\n", *v);
}

Bewerken: als je ook geïnteresseerd bent in C++-oplossingen, C++ heeft een native for-each-syntaxis genaamd “range based for”


Antwoord 2, autoriteit 5%

Hier is een volledig programmavoorbeeld van een voor elke macro 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;
}

Antwoord 3, autoriteit 5%

Zoals je waarschijnlijk al weet, is er geen “foreach”-achtige lus in C.

Hoewel er hier al talloze geweldige macro’s beschikbaar zijn om dit te omzeilen, vindt u deze macro misschien nuttig:

// "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 kan worden gebruikt met for(zoals in for each (...)).

Voordelen van deze aanpak:

  • itemwordt gedeclareerd en verhoogd binnen het for-statement (net als
    in Python!).
  • Het lijkt te werken op elke 1-dimensionale array
  • Alle variabelen die in macro (p, item) zijn gemaakt, zijn niet zichtbaar buiten de
    bereik van de lus (omdat ze zijn gedeclareerd in de for-lus-header).

Nadelen:

  • Werkt niet voor multidimensionale arrays
  • Vertrouwt op typeof(), een GNU-extensie, geenonderdeel van standaard C
  • Omdat het variabelen declareert in de for-lus-header, werkt het alleen in C11 of hoger.

Om je wat tijd te besparen, kun je het als volgt 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;
}

lijkt standaard op GCC en Clang te werken; heb geen andere compilers getest.


Antwoord 4, Autoriteit 4%

Er is geen forreach in c.

U kunt een voor lus gebruiken om door de gegevens te lussen, maar de lengte moet weten of de gegevens moeten worden beëindigd door een kende waarde (bijv. NULL).

char* nullTerm;
nullTerm = "Loop through my characters";
for(;nullTerm != NULL;nullTerm++)
{
    //nullTerm will now point to the next character.
}

Antwoord 5, Autoriteit 2%

Dit is een vrij oude vraag, maar ik moet dit posten. Het is een foreach-lus voor 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;
}

Deze code is getest om te werken met gcc, icc en clang op GNU/Linux.


Antwoord 6, autoriteit 2%

Hoewel C geen a heeft voor elk construct, heeft het altijd een idiomatische representatie gehad voor één voorbij het einde van een array (&arr)[1]. Hiermee kunt u als volgt een eenvoudige idiomatische uitdrukking voor elke lus schrijven:

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

Antwoord 7

Dit is wat ik gebruik als ik vastzit aan C. Je kunt dezelfde itemnaam niet twee keer in dezelfde scope gebruiken, maar dat is niet echt een probleem, omdat we niet allemaal leuke nieuwe compilers kunnen gebruiken 🙁

#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 */

Gebruik:

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);

EDIT: Hier is een alternatief voor foreachmet behulp van de C9DE-syntaxis om de vervuiling van de naamruimte te voorkomen:

#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)

Antwoord 8

C heeft ‘voor’ en ‘terwijl’ trefwoorden. Als een foreach-verklaring in een taal zoals C # er zo uitziet …

foreach (Element element in collection)
{
}

… Dan is het equivalent van deze foreach-verklaring in C is misschien:

for (
    Element* element = GetFirstElement(&collection);
    element != 0;
    element = GetNextElement(&collection, element)
    )
{
    //TODO: do something with this element instance ...
}

Antwoord 9

Eric’s antwoordwerkt niet als je “pauze” of “doorgaan” gebruikt.

Dit kan worden opgelost door de eerste regel te herschrijven:

Oorspronkelijke regel (opnieuw geformatteerd):

for (unsigned i = 0, __a = 1; i < B.size(); i++, __a = 1)

Opgelost:

for (unsigned i = 0, __a = 1; __a && i < B.size(); i++, __a = 1)

Als je het vergelijkt met Johannes’ loop, zul je zien dat hij eigenlijk hetzelfde doet, alleen een beetje ingewikkelder en lelijker.


Antwoord 10

Hier is een eenvoudige, enkele voor lus:

#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

Geeft u toegang tot de index als u dat wilt (i) en het huidige item dat we herhalen (it). Houd er rekening mee dat u problemen kunt hebben met de naamgeving bij het nesten van lussen, u kunt de item- en indexnamen parameters voor de macro maken.

Bewerken: hier is een aangepaste versie van het geaccepteerde antwoord foreach. Hiermee kunt u de index startspecificeren, de sizezodat deze werkt op vervallen arrays (pointers), geen int*nodig en gewijzigde count != sizetot i < sizevoor het geval de gebruiker per ongeluk ‘i’ wijzigt om groter te zijn dan sizeen vast komt te zitten in een oneindige lus.

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

Uitgang:

index: 2. element: 3
index: 3. element: 4
index: 4. element: 5

Antwoord 11

Als u van plan bent om met functieaanwijzers te werken

#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)

Gebruik:

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);

Maar ik denk dat dit alleen werkt op GCC (niet zeker).


Antwoord 12

C heeft geen implementatie van for-each. Bij het parseren van een array als een punt dat de ontvanger niet weet hoe lang de array is, is er dus geen manier om te vertellen wanneer u het einde van de array bereikt.
Onthoud, in C int*is een punt naar een geheugenadres met een int. Er is geen header-object met informatie over hoeveel gehele getallen die in volgorde worden geplaatst. De programmeur moet dus dit bijhouden.

Voor lijsten is het echter eenvoudig om iets te implementeren dat lijkt op een for-eachlus.

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

Om iets soortgelijks te bereiken voor arrays kunt u een van de twee dingen doen.

  1. Gebruik het eerste element om de lengte van de array op te slaan.
  2. Wikkel de array in een structuur die de lengte en een aanwijzer op de array bevat.

Het volgende is een voorbeeld van dergelijke struct:

typedef struct job_t {
   int count;
   int* arr;
} arr_t;

Other episodes