web-dev-qa-db-fra.com

Est-ce que C a une construction de boucle "foreach"?

Presque toutes les langues ont une boucle foreach ou quelque chose de similaire. Est-ce que C en a un? Pouvez-vous poster un exemple de code?

96
Question

C n'a pas foreach, mais les macros sont fréquemment utilisées pour émuler cela:

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

Et peut être utilisé comme

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

L'itération sur un tableau est également possible:

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

Et peut être utilisé comme

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

Edit: si les solutions C++ vous intéressent également, C++ a une syntaxe native pour-chaque appelée "plage basée sur" 

178

Voici un exemple de programme complet d'une macro for-each dans 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;
}
10
Judge Maygarden

Il n'y a pas de foreach en C.

Vous pouvez utiliser une boucle for pour parcourir les données, mais la longueur doit être connue ou les données doivent être terminées par une valeur connue (par exemple, null).

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

for(;nullTerm != NULL;nullTerm++)
{
    //nullTerm will now point to the next character.
}
8
Adam Peck

C'est une question assez ancienne, mais je pensais que je devrais poster ceci. C'est une boucle foreach pour 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;
}

Ce code a été testé pour fonctionner avec gcc, icc et clang sous GNU/Linux.

5
Joe D

Comme vous le savez probablement déjà, il n'y a pas de boucle de style "foreach" en C.

Bien qu’il existe déjà des tonnes de super macros fournies ici pour résoudre ce problème, vous trouverez peut-être cette macro utile .

// "length" is the length of the array.   
#define each(item, array, length) \
(typeof(*(array)) *p = (array), (item) = *p; p < &((array)[length]); p++, (item) = *p)

... qui peut être utilisé avec for (comme dans for each (...)).

Avantages de cette approche:

  • item est déclaré et incrémenté dans l'instruction for (commein en Python!).
  • Semble travailler sur n'importe quel tableau à 1 dimension
  • Toutes les variables créées dans une macro (p, item) ne sont pas visibles en dehors de la portée de la boucle (Puisqu'elles sont déclarées dans l'en-tête de la boucle for).

Désavantages:

  • Ne fonctionne pas pour les tableaux multidimensionnels
  • S'appuie sur typeof(), qui est une extension de gcc; Ne fait pas partie de la norme C
  • Puisqu'il déclare des variables dans l'en-tête de la boucle for, il ne fonctionne qu'en C11 ou plus tard.

Juste pour vous faire gagner du temps, voici comment vous pouvez le tester:

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

Semble travailler sur gcc et clang; pas sûr des autres compilateurs.

4
Saeed Baig

Bien que C n'ait pas de construction pour chaque construction, il a toujours eu une représentation idiomatique pour un après la fin d'un tableau (&arr)[1]. Cela vous permet d’écrire un idiomatique simple pour chaque boucle comme suit:

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

C a 'pour' et 'tant que' mots-clés. Si une déclaration foreach dans un langage comme C # ressemble à ceci ...

foreach (Element element in collection)
{
}

... alors l'équivalent de cette déclaration foreach en C pourrait être le suivant:

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

C n'a pas d'implémentation de for-each. Lors de l'analyse d'un tableau en tant que point, le récepteur ne sait pas combien de temps le tableau est, il est donc impossible de dire quand vous atteignez la fin du tableau. Rappelez-vous, en C int* est un point vers une adresse mémoire contenant un int. Il n'y a pas d'objet d'en-tête contenant des informations sur le nombre d'entiers placés en séquence. Ainsi, le programmeur doit garder une trace de cela.

Cependant, pour les listes, il est facile d'implémenter quelque chose qui ressemble à une boucle for-each

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

Pour obtenir quelque chose de similaire pour les tableaux, vous pouvez faire l’une des deux choses suivantes. 

  1. utilisez le premier élément pour stocker la longueur du tableau. 
  2. encapsule le tableau dans une structure qui contient la longueur et un pointeur sur le tableau.

Voici un exemple d'une telle structure:

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

Voici un simple, simple pour boucle:

#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

Vous permet d'accéder à l'index si vous le souhaitez (i) et à l'élément en cours sur lequel nous effectuons une itération (it). Notez que vous pouvez avoir des problèmes de dénomination lors de l’imbrication des boucles. Vous pouvez définir les noms d’élément et d’index comme paramètres de la macro.

Edit: Voici une version modifiée de la réponse acceptée foreach. Vous permet de spécifier l'index start, le size pour qu'il fonctionne sur les tableaux décomposés (pointeurs), sans avoir besoin de int* et modifié count != size en i < size juste au cas où l'utilisateur modifierait accidentellement 'i' pour qu'il soit plus grand que size et reste bloqué dans un infini boucle.

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

Sortie:

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

La réponse d'Eric ne fonctionne pas lorsque vous utilisez "pause" ou "continuer". 

Cela peut être corrigé en réécrivant la première ligne:

Ligne originale (reformatée):

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

Fixé:

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

Si vous comparez cela à la boucle de Johannes, vous verrez qu'il fait la même chose, mais un peu plus compliqué et plus laid.

1
Michael Blurb

Voici ce que j'utilise lorsque je suis coincé avec C. Vous ne pouvez pas utiliser deux fois le même nom d'élément dans le même champ d'application, mais ce n'est pas vraiment un problème car nous ne sommes pas tous capables d'utiliser les nouveaux compilateurs de Nice :(

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

Usage:

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

Si vous prévoyez de travailler avec des pointeurs de fonction

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

Usage:

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

Mais je pense que cela ne fonctionnera que sur gcc (pas sûr).

0
Naheel

Grâce à la réponse acceptée, j'ai pu réparer ma macro C++. Ce n'est pas propre, mais il prend en charge l'imbrication, la rupture et la poursuite, et pour un list<t>, il permet de parcourir le t& au lieu de list<t>::elem*, de sorte que vous ne devez pas déréférencer.

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

Avec la macro for_each_item de la réponse acceptée, vous devez procéder comme suit:

void example2() {
  list<string> Words;
  for_each_item(string, Elem, Words) {
    string& Word = Elem->Data;
    print(Word);
  }
}

Si vous voulez le réécrire dans une version épurée, n'hésitez pas à l'éditer.

0
Andreas Haferburg