Функция Foreach в C - PullRequest
       2

Функция Foreach в C

3 голосов
/ 01 апреля 2019

Я хочу создать функцию, которая принимает массив и функцию и вызывает эту функцию для каждого элемента в массиве.

Я искал решение, но все они, кажется, используют макросы, тогда как я хотел быпредпочитаю функцию, если это вообще возможно.

Я ищу что-то, что будет работать примерно так:

void print_string()
{
    printf("%d\n", num);
}

int array[] = { 1, 2, 3, 4, NULL }; // So the foreach function knows when it has reached the end

for_each_function(array, print_number);

Вывод:

1
2
3
4

Редактировать: Этодолжен быть универсальным, поэтому, вероятно, потребуется макрос

Ответы [ 5 ]

4 голосов
/ 01 апреля 2019

Вы, вероятно, должны использовать указатели на функции.Одна полная реализация с использованием указателей на функции выглядит следующим образом.

#include <stdio.h>

void for_each(int * arr, int size, void (*fun)(int))
{
  for (int i = 0; i < size; i++)
  {
    (*fun)(arr[i]);
  }
}

void print_num(int num)
{
  printf("%d\n", num);
}

int main()
{
  int array [] = {1, 2, 3, 4};
  for_each(array, 4, print_num);
  return 0;
}
3 голосов
/ 01 апреля 2019

Модификация ответа davidlowryduda / gsamaras для совместимости с любым типом массива, а не только с int , однако это влияет на вызываемую функцию, которая получает указатель, а не значение:

#include <stdio.h>

void for_each(void * arr, size_t nElts, size_t sizeElt, void (*fun)(void *))
{
  char * p = arr;

  for (size_t i = 0; i < nElts; i++)
  {
    (*fun)(p);
    p += sizeElt;
  }
}

/* for_each can be called directly or via the following macro,
   the array can be an expression without any problem : is it given
   in argument and the other uses are only for sizeof */

#define FOR_EACH(a, f) for_each(a, sizeof(a)/sizeof(*a), sizeof(*a), f)

void print_int(void * p)
{
  printf("%d\n", *((int *) p));
}

void print_short(void * p)
{
  printf("%d\n", *((short *) p));
}

int main()
{
  int iarray [] = {1, 2, 3, 4};
  FOR_EACH(iarray, print_int); /* for_each(iarray, sizeof(iarray)/sizeof(*iarray), sizeof(*iarray), print_int); */

  short sarray [] = {5, 6, 7, 8};
  FOR_EACH(sarray, print_short); /* for_each(sarray, sizeof(sarray)/sizeof(*sarray), sizeof(*sarray), print_short); */

  return 0;
}

Компиляция и исполнение:

pi@raspberrypi:~ $ gcc -pedantic -Wall -Wextra i.c
pi@raspberrypi:~ $ ./a.out
1
2
3
4
5
6
7
8
3 голосов
/ 01 апреля 2019

Используйте указатель функции , например:

#include <stdio.h>

void for_each_function(int* arr, size_t size, void(*fun)(int)) {
  for(size_t i = 0; i < size; ++i)
    (*fun)(arr[i]);
}
void print_number(int);

int main(void) {
  int array[] = { 1, 2, 3, 4 };
  for_each_function(array, 4, &print_number);
  return 0;
}

void print_number(int num)
{
    printf("%d\n", num);
}

Выход:

1
2
3
4

Примечания:

  • Третий параметр for_each_function, а именно void(*fun)(int), является указателем на функцию с именем fun, с типом возврата void и параметр типа int.
  • Внутри каждого тела вы вызываете указатель на функцию следующим образом (*fun)(arr[i]);, который разыменовывает указатель на функцию fun, и будет передавать arr[i] в качестве параметра. Вы также можете использовать fun(arr[i]).
  • При вызове каждой функции в основной функции вам нужно используйте адрес функции, на которую вы хотите указать, как это for_each_function(array, 4, &print_number);. Вы также можете использовать for_each_function(array, 4, print_number);.

PS: в вашем примере массива я не думаю, что вы хотели бы использовать NULL в качестве последнего элемента (вы, вероятно, получите предупреждение, например warning: initialization of 'int' from 'void *' makes integer from pointer without a cast [-Wint-conversion], если NULL не было определено как 0, как прокомментировано @EricPostpischil), поэтому я избавился от этого в своем примере.

1 голос
/ 01 апреля 2019

Вы можете определить макрос для этого.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FOR_EACH(type, target, size, body) \
                                           \
    for (size_t i = 0; i < size; i++)      \
    {                                      \
        type value = target[i];            \
        body;                              \
    }

int main(int argc, char const *argv[])
{
    int arr[4] = {0, 1, 2, 3};

    char *name = "My Name";

    FOR_EACH(int, arr, 4, {
        printf("%d\n", value);
    })

    size_t len = strlen(name);

    FOR_EACH(char, name, len, {
        printf("%c-", value);
    })

    return 0;
}

В этом случае вам даже не нужна функция, и ее можно использовать для перебора любого массива любого типа, если вы знаете егодлина.

0 голосов
/ 01 апреля 2019

Вы можете сделать что-то подобное, используя указатели на функции. Но использование макросов будет очень мощным, это позволит вашему foreach быть универсальным.

#include <stdio.h>
typedef struct int_array{
    int *data;
    int len; 
}Int_array;

void print_number(int num)
{
    printf("%d\n", num);
}

void for_each_function(Int_array a, void (*f)(int)){
    for(int i =0; i < a.len;i++){
            (*f)(a.data[i]);
    }
}

int main(){

    int data[4] = { 1, 2, 3, 4 };
    Int_array x;
    x.len = 4;
    x.data = data;
    for_each_function(x, print_number);   
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...