क्या C के पास "foreach" लूप का निर्माण है?


110

लगभग सभी भाषाओं में ए foreach लूप या कुछ समान होता है। क्या C के पास एक है? क्या आप कुछ उदाहरण कोड पोस्ट कर सकते हैं?


1
" foreach" क्या?
एल्क

foreachC प्रोग्राम में लूप लिखने की कोशिश करना कितना कठिन रहा होगा ?
एमडी एक्सएफ

जवाबों:


195

C के पास फॉर्च्यूनर नहीं है, लेकिन मैक्रोज़ का उपयोग अक्सर उस अनुकरण के लिए किया जाता है:

#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 ++ में प्रत्येक के लिए एक देशी सिंटैक्स है, जिसे "श्रेणी के लिए आधारित" कहा जाता है।


1
यदि आपको "टाइपोफ़" ऑपरेटर मिला है (जीसीसी एक्सटेंशन; कई अन्य संकलक पर बहुत आम) तो आप उस "इंट *" से छुटकारा पा सकते हैं। पाश के लिए आंतरिक की तरह "(typeof ((सरणी) +0) आइटम के लिए = ..." तो फिर तुम के रूप में कॉल कर सकते हैं कुछ हो जाता है "foreach (V, मान) ..."
लिएंडर

हमें सरणी उदाहरण में दो छोरों की आवश्यकता क्यों है? इसके बारे में कैसे: #define foreach(item, array) int count=0, size=sizeof(array)/sizeof(*(array)); for(item = (array); count != size; count++, item = (array)+count)एक समस्या जो मैं देख सकता हूं वह यह है कि चर गिनती और आकार लूप के लिए बाहर रहते हैं, और संघर्ष का कारण हो सकते हैं। क्या यह कारण है कि आप दो छोरों के लिए उपयोग करते हैं? [कोड यहाँ पेस्ट किया गया ( pastebin.com/immndpwS )]
लेज़र

3
@eSKay हाँ विचार करें if(...) foreach(int *v, values) ...। यदि वे लूप के बाहर हैं, तो यह फैलता है if(...) int count = 0 ...; for(...) ...;और टूट जाएगा।
जोहान्स स्काउब -

1
@rem यह बाहरी पाश उल्लंघन नहीं करती है, तो आप "तोड़" का उपयोग
Johannes Schaub - litb

1
यदि आप आंतरिक "change =" को रखते हैं, तो "@ मेरे कोड को सरल बना सकते हैं, लेकिन" "= 0" में रखें। मुझे "समरूपता" पसंद थी इसलिए मैंने सिर्फ नकार का इस्तेमाल किया और सीधे असाइनमेंट का नहीं।
जोहान्स शाउब -

11

यहाँ 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?
रिज़ो

9
@Rizo नहीं, C99 निर्दिष्ट इनिशियलाइज़र के लिए वाक्यविन्यास का एक हिस्सा है । देखें en.wikipedia.org/wiki/C_syntax#Initialization
न्यायाधीश Maygarden

@Rizo: यह भी ध्यान दें कि लिंक की गई सूची बनाने का यह वास्तव में हैक करने का तरीका है। यह इस डेमो के लिए करेंगे, लेकिन अभ्यास में इस तरह मत करो!
डोनल फैलो

@ जोनल इसे "हैकी" बनाता है?
जज मेगार्डन

2
@ जूडगे: एक बात के लिए, इसमें "आश्चर्यजनक" जीवनकाल है (यदि आप कोड के साथ काम कर रहे हैं जो तत्वों को निकालता है, तो संभावना है कि आप दुर्घटनाग्रस्त हो जाएंगे free()) और दूसरे के लिए इसकी परिभाषा के अंदर मूल्य का संदर्भ है। यह वास्तव में कुछ का एक उदाहरण है जो अभी भी बहुत चालाक है; कोड का उद्देश्य पर्याप्त रूप से उद्देश्यपूर्णता के साथ चतुराई को जोड़ने के बिना। कर्निघन की कामोत्तेजना ( stackoverflow.com/questions/1103299/… ) लागू होती है!
डोनाल्ड फैलो

9

सी में कोई फॉर्च्यूनर नहीं है।

आप डेटा के माध्यम से लूप के लिए लूप का उपयोग कर सकते हैं, लेकिन लंबाई जानने की जरूरत है या डेटा को एक मान मान (जैसे शून्य) द्वारा समाप्त करने की आवश्यकता है।

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

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

आपको डेटा सेट की शुरुआत में nullTerm पॉइंटर के आरंभीकरण को जोड़ना चाहिए। लूप के लिए अपूर्ण के बारे में ओपी भ्रमित हो सकता है।
सीएसचोल

Fleshed उदाहरण थोड़ा बाहर।
एडम पेक

आप अपना मूल सूचक बदल रहे हैं, मैं कुछ ऐसा करूंगा: char * s; s = "..."; के लिए (char it = s; it? = NULL; it ++) {/ * यह चरित्र की ओर इशारा करता है * / / }
हायना

6

जैसा कि आप शायद पहले से ही जानते हैं, सी में कोई "फॉर्च्यूनर" -स्टाइल लूप नहीं है।

हालाँकि इसके चारों ओर काम करने के लिए पहले से ही महान मैक्रोज़ उपलब्ध हैं, शायद आपको यह मैक्रो उपयोगी लगे:

// "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 घोषित किया गया है और बयान के लिए बढ़ा दिया गया है (ठीक अजगर की तरह!)।
  • किसी भी 1-आयामी सरणी पर काम करने लगता है
  • मैक्रो ( p, item) में बनाए गए सभी चर , लूप के दायरे के बाहर दिखाई नहीं देते हैं (क्योंकि वे लूप हैडर के लिए घोषित किए गए हैं)।

नुकसान:

  • बहु-आयामी सरणियों के लिए काम नहीं करता है
  • पर निर्भर करता है typeof(), जो एक GNU एक्सटेंशन है, मानक C का हिस्सा नहीं है
  • चूंकि यह लूप हैडर के लिए वैरिएबल घोषित करता है, यह केवल C11 या बाद के संस्करण में काम करता है।

बस आपको कुछ समय बचाने के लिए, यहां बताया गया है कि आप इसे कैसे परख सकते हैं:

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 पर काम करने लगता है; अन्य संकलक का परीक्षण नहीं किया है।


5

यह एक काफी पुराना प्रश्न है, लेकिन मुझे यह पोस्ट करना चाहिए। यह 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;
}

इस कोड को GNU / Linux पर gcc, icc और clang के साथ काम करने के लिए परीक्षण किया गया है।


4

जबकि C के पास प्रत्येक निर्माण के लिए नहीं है, यह हमेशा एक सरणी के अंत में एक अतीत के लिए एक मुहावरेदार प्रतिनिधित्व करता है (&arr)[1]। यह आपको प्रत्येक लूप के लिए एक सरल मुहावरेदार लिखने की अनुमति देता है:

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

3
यदि ऐसा नहीं है तो यह अच्छी तरह से परिभाषित है। (&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++)
लुंडिन

1
@ लुंडिन यह अच्छी तरह से परिभाषित है। आप सही हैं, यह सरणी के अंत में एक सरणी है, लेकिन वह सरणी प्रकार इस संदर्भ में एक सूचक (एक अभिव्यक्ति) में परिवर्तित होता है, और वह सूचक सरणी के अंत में एक अतीत होता है।
स्टीव कॉक्स

2

C में 'for' और 'जबकि' keywords हैं। यदि C # जैसी भाषा में एक फ़ॉरवर्ड स्टेटमेंट इस तरह दिखता है ...

foreach (Element element in collection)
{
}

... तो C में इस फ़ॉरवर्ड स्टेटमेंट के बराबर हो सकता है:

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

1
आपको उल्लेख करना चाहिए कि आपका उदाहरण कोड सी सिंटैक्स में नहीं लिखा गया है।
cschol

> आपको उल्लेख करना चाहिए कि आपका उदाहरण कोड सी सिंटैक्स में नहीं लिखा है। आप सही हैं, धन्यवाद: मैं पोस्ट संपादित करूंगा।
17

@ मोनजार्डिन-> सुनिश्चित करें कि आप केवल संरचना में कार्य करने के लिए पॉइंटर को परिभाषित कर सकते हैं और इस तरह से कॉल करने में कोई समस्या नहीं है।
इल्या

2

जब मैं सी के साथ फंस गया हूं तो यहां मैं वही उपयोग कर रहा हूं। आप एक ही आइटम नाम का दो बार एक ही दायरे में उपयोग नहीं कर सकते हैं, लेकिन यह वास्तव में एक मुद्दा नहीं है क्योंकि हम सभी को नए नए कंपाइलरों का उपयोग करने के लिए नहीं मिलता है :(

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

EDIT: यहाँ 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]। शायद स्पष्टता के लिए नमूना उपयोग के मामलों को जोड़ें।
chux -

मेरे पिछले दृष्टिकोण के संकेत के साथ भी परिणाम 'अपरिभाषित व्यवहार' प्रतीत होता है। ओह अच्छा। पाश दृष्टिकोण के लिए डबल पर भरोसा करें!
1

यह संस्करण दायरे को प्रदूषित करता है और यदि एक ही दायरे में दो बार उपयोग किया जाता है तो यह विफल हो जाएगा। इसके अलावा एक अन-ब्रेस्ड ब्लॉक के रूप में काम नहीं करता है (जैसेif ( bla ) FOREACH(....) { } else....
MM

1
1, सी स्कोप प्रदूषण की भाषा है, हम में से कुछ पुराने संकलक तक सीमित हैं। 2, अपने आप को दोहराएं / वर्णनात्मक न हों। 3, हाँ, दुर्भाग्य से आपके पास ब्रेसिज़ हैं यदि यह लूप के लिए सशर्त होने जा रहा है (लोग आमतौर पर वैसे भी करते हैं)। यदि आपके पास एक संकलक तक पहुंच है जो लूप के लिए चर घोषणाओं का समर्थन करता है, तो सभी तरीकों से ऐसा करें।
वाटर साइकिल

@Watercycle: मैंने अपने जवाब को संपादित करने के लिए स्वतंत्र रूप से लिया, FOREACHजो नामस्थान प्रदूषण से बचने के लिए c99 सिंटैक्स का उपयोग करता है।
चकरली

1

एरिक का जवाबजब आप "ब्रेक" या "जारी" का उपयोग कर रहे हैं तो काम नहीं करता है।

यह पहली पंक्ति को फिर से लिखकर तय किया जा सकता है:

मूल पंक्ति (सुधारित):

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)

यदि आप इसकी तुलना जोहान्स लूप से करते हैं, तो आप देखेंगे कि वह वास्तव में ऐसा ही कर रहा है, बस थोड़ा और अधिक जटिल और बदसूरत।


1

यहाँ एक सरल है, लूप के लिए एकल:

#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सिर्फ मामले में उपयोगकर्ता गलती से संशोधित 'मैं' से भी बड़ा होने के लिए 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

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 (निश्चित नहीं) पर काम करेगा।


1

C का कार्यान्वयन नहीं होता है for-each। एक बिंदु के रूप में एक सरणी को पार्स करते समय रिसीवर को पता नहीं होता है कि सरणी कितनी लंबी है, इस प्रकार यह बताने का कोई तरीका नहीं है कि आप सरणी के अंत तक कब पहुंचते हैं। याद रखें, सी मेंint* में एक स्मृति पते का एक बिंदु है जिसमें एक int है। कोई पूर्णांक वस्तु नहीं है जिसमें इस बात की जानकारी हो कि कितने पूर्णांक अनुक्रम में रखे गए हैं। इस प्रकार, प्रोग्रामर को इस पर नज़र रखने की आवश्यकता है।

हालांकि, सूचियों के लिए, for-eachलूप जैसा दिखने वाला कुछ लागू करना आसान है ।

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

सरणियों के लिए समान कुछ हासिल करने के लिए आप दो चीजों में से एक कर सकते हैं।

  1. सरणी की लंबाई को संग्रहीत करने के लिए पहले तत्व का उपयोग करें।
  2. सरणी को उस संरचना में लपेटें जो लंबाई और सरणी के लिए एक संकेतक रखती है।

निम्नलिखित ऐसी संरचना का एक उदाहरण है:

typedef struct job_t {
   int count;
   int* arr;
} arr_t;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.