Hầu hết tất cả các ngôn ngữ đều có một foreach
vòng lặp hoặc một cái gì đó tương tự. C có một cái không? Bạn có thể đăng một số mã ví dụ?
foreach
vòng lặp trong chương trình C?
Hầu hết tất cả các ngôn ngữ đều có một foreach
vòng lặp hoặc một cái gì đó tương tự. C có một cái không? Bạn có thể đăng một số mã ví dụ?
foreach
vòng lặp trong chương trình C?
Câu trả lời:
C không có foreach, nhưng macro thường được sử dụng để mô phỏng điều đó:
#define for_each_item(item, list) \
for(T * item = list->head; item != NULL; item = item->next)
Và có thể được sử dụng như
for_each_item(i, processes) {
i->wakeup();
}
Lặp lại trên một mảng cũng có thể:
#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)
Và có thể được sử dụng như
int values[] = { 1, 2, 3 };
foreach(int *v, values) {
printf("value: %d\n", *v);
}
Chỉnh sửa: Trong trường hợp bạn cũng quan tâm đến các giải pháp C ++, C ++ có một cú pháp riêng cho từng cú pháp được gọi là "phạm vi dựa trên cho"
#define foreach(item, array) int count=0, size=sizeof(array)/sizeof(*(array)); for(item = (array); count != size; count++, item = (array)+count)
Một vấn đề mà tôi có thể thấy là số lượng và kích thước các biến nằm ngoài vòng lặp for và có thể gây ra xung đột. Đây có phải là lý do bạn sử dụng hai vòng lặp for? [mã được dán ở đây ( pastebin.com/immndpwS )]
if(...) foreach(int *v, values) ...
. Nếu chúng nằm ngoài vòng lặp, nó sẽ mở rộng if(...) int count = 0 ...; for(...) ...;
và sẽ phá vỡ.
Đây là một ví dụ chương trình đầy đủ về macro cho từng macro trong 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[]
định nghĩa? Bạn không thể viết đơn giản next
thay vì .next
?
free()
) và mặt khác, nó có tham chiếu đến giá trị bên trong định nghĩa của nó. Nó thực sự là một ví dụ về một cái gì đó quá thông minh; mã đủ phức tạp nếu không có mục đích thêm sự thông minh vào nó. Cách ngôn của Kernighan ( stackoverflow.com/questions/1103299/… ) được áp dụng!
Không có foreach nào trong C.
Bạn có thể sử dụng vòng lặp for để lặp qua dữ liệu nhưng độ dài cần phải biết hoặc dữ liệu cần được kết thúc bởi một giá trị biết (ví dụ: null).
char* nullTerm;
nullTerm = "Loop through my characters";
for(;nullTerm != NULL;nullTerm++)
{
//nullTerm will now point to the next character.
}
Như bạn có thể đã biết, không có vòng lặp kiểu "foreach" trong C.
Mặc dù đã có rất nhiều macro tuyệt vời được cung cấp ở đây để giải quyết vấn đề này, nhưng có thể bạn sẽ thấy macro này hữu ích:
// "length" is the length of the array.
#define each(item, array, length) \
(typeof(*(array)) *p = (array), (item) = *p; p < &((array)[length]); p++, (item) = *p)
... có thể được sử dụng với for
(như trong for each (...)
).
Ưu điểm của phương pháp này:
item
được khai báo và tăng dần trong câu lệnh for (giống như trong Python!).p
, item
), không hiển thị bên ngoài phạm vi của vòng lặp (vì chúng được khai báo trong tiêu đề vòng lặp for).Nhược điểm:
typeof()
, là một phần mở rộng GNU, không phải là một phần của tiêu chuẩn CChỉ để giúp bạn tiết kiệm thời gian, đây là cách bạn có thể kiểm tra nó:
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;
}
Có vẻ như hoạt động trên gcc và tiếng kêu theo mặc định; chưa thử nghiệm các trình biên dịch khác.
Đây là một câu hỏi khá cũ, nhưng tôi nên đăng câu hỏi này. Nó là một vòng lặp foreach cho 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;
}
Mã này đã được thử nghiệm để hoạt động với gcc, icc và clang trên GNU / Linux.
Trong khi C không có một cho mỗi cấu trúc, nó luôn có một đại diện thành ngữ cho một quá khứ cuối của một mảng (&arr)[1]
. Điều này cho phép bạn viết một thành ngữ đơn giản cho mỗi vòng lặp như sau:
int arr[] = {1,2,3,4,5};
for(int *a = arr; a < (&arr)[1]; ++a)
printf("%d\n", *a);
(&arr)[1]
không có nghĩa là một mục của mảng ở cuối mảng, nó có nghĩa là một mảng ở cuối mảng. (&arr)[1]
không phải là mục cuối cùng của mảng [0], nó là mảng [1], phân rã thành con trỏ đến phần tử đầu tiên (của mảng [1]). Tôi tin rằng nó sẽ tốt hơn, an toàn hơn và dễ hiểu hơn khi làm const int* begin = arr; const int* end = arr + sizeof(arr)/sizeof(*arr);
và sau đó for(const int* a = begin; a != end; a++)
.
C có các từ khóa 'for' và 'while'. Nếu một câu lệnh foreach bằng ngôn ngữ như C # trông như thế này ...
foreach (Element element in collection)
{
}
... thì tương đương với câu lệnh foreach này trong C có thể giống như sau:
for (
Element* element = GetFirstElement(&collection);
element != 0;
element = GetNextElement(&collection, element)
)
{
//TODO: do something with this element instance ...
}
Đây là những gì tôi sử dụng khi tôi gặp khó khăn với C. Bạn không thể sử dụng cùng một tên mục hai lần trong cùng một phạm vi, nhưng đó thực sự không phải là vấn đề vì không phải tất cả chúng ta đều có thể sử dụng các trình biên dịch mới tốt đẹp :(
#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 */
Sử dụng:
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);
CHỈNH SỬA: Đây là một giải pháp thay thế cho FOREACH
việc sử dụng cú pháp c99 để tránh ô nhiễm không gian tên:
#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)])
sẽ dừng khi phần tử mảng có giá trị bằng 0. Vì vậy, việc sử dụng điều này với double Array[]
có thể không lặp lại qua tất cả các phần tử. Có vẻ như kiểm tra vòng lặp phải là một hoặc khác: i<n
hoặc A[i]
. Có thể thêm các trường hợp sử dụng mẫu cho rõ ràng.
if ( bla ) FOREACH(....) { } else....
FOREACH
sử dụng cú pháp c99 để tránh ô nhiễm không gian tên.
Câu trả lời của Eric không hoạt động khi bạn đang sử dụng "break" hoặc "continue".
Điều này có thể được khắc phục bằng cách viết lại dòng đầu tiên:
Dòng gốc (được định dạng lại):
for (unsigned i = 0, __a = 1; i < B.size(); i++, __a = 1)
Đã sửa:
for (unsigned i = 0, __a = 1; __a && i < B.size(); i++, __a = 1)
Nếu bạn so sánh nó với vòng lặp của Johannes, bạn sẽ thấy rằng anh ấy thực sự đang làm như vậy, chỉ phức tạp hơn và xấu hơn một chút.
Đây là một vòng lặp đơn giản, đơn giản:
#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
Cung cấp cho bạn quyền truy cập vào chỉ mục nếu bạn muốn nó ( i
) và mục hiện tại mà chúng tôi đang lặp lại ( it
). Lưu ý rằng bạn có thể gặp sự cố đặt tên khi lồng các vòng lặp, bạn có thể đặt tên mục và chỉ mục là tham số cho macro.
Chỉnh sửa: Đây là phiên bản sửa đổi của câu trả lời được chấp nhận foreach
. Cho phép bạn chỉ định start
chỉ mục, size
để nó hoạt động trên các mảng đã phân rã (con trỏ), không cần int*
và thay đổi count != size
thành i < size
chỉ trong trường hợp người dùng vô tình sửa đổi 'i' lớn hơn size
và bị mắc kẹt trong một vòng lặp vô hạn.
#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);
Đầu ra:
index: 2. element: 3
index: 3. element: 4
index: 4. element: 5
Nếu bạn định làm việc với con trỏ hàm
#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)
Sử dụng:
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);
Nhưng tôi nghĩ rằng điều này sẽ chỉ hoạt động trên gcc (không chắc chắn).
C không có triển khai của for-each
. Khi phân tích cú pháp một mảng dưới dạng một điểm, bộ thu không biết mảng dài bao nhiêu, do đó không có cách nào để biết khi nào bạn đến cuối mảng. Hãy nhớ rằng, trong C int*
là một điểm đến địa chỉ bộ nhớ có chứa int. Không có đối tượng tiêu đề nào chứa thông tin về bao nhiêu số nguyên được đặt trong chuỗi. Vì vậy, lập trình viên cần theo dõi điều này.
Tuy nhiên, đối với danh sách, có thể dễ dàng triển khai một cái gì đó giống như một for-each
vòng lặp.
for(Node* node = head; node; node = node.next) {
/* do your magic here */
}
Để đạt được điều gì đó tương tự cho các mảng, bạn có thể làm một trong hai điều.
Sau đây là một ví dụ về cấu trúc như vậy:
typedef struct job_t {
int count;
int* arr;
} arr_t;
foreach
" của cái gì?