3

I want to create a function that takes a array and a function and calls that function on each element in the array

I've looked around for a solution but they all seem to use macros whereas I would prefer a function if at all possible.

I'm looking for something that would work something like the following

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

Outputting:

1
2
3
4

Edit: It needs to be generic so it will probably require a macro

gsamaras
  • 71,951
  • 46
  • 188
  • 305
txk2048
  • 281
  • 3
  • 15

6 Answers6

5

You should probably use function pointers. One complete implementation using function pointers is like this.

#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;
}
davidlowryduda
  • 2,404
  • 1
  • 25
  • 29
3

Use a function pointer, like this:

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

Output:

1
2
3
4

Remarks:

  • The third parameter of for_each_function, namely void(*fun)(int), is a function pointer named fun, with a return type void, and a parameter of type int.
  • Inside the for each body, you call the function pointer like this (*fun)(arr[i]);, which dereferences the function pointer fun, and would pass arr[i] as its parameter. You could also use fun(arr[i]).
  • In the call of the for each function in main function, you need to use the address of the function you want to point to, like this for_each_function(array, 4, &print_number);. You could also use for_each_function(array, 4, print_number);.

PS: In your example array, I don't think you wanted to use NULL as the last element (you would probably get a warning like warning: initialization of 'int' from 'void *' makes integer from pointer without a cast [-Wint-conversion], unless NULL was defined as 0, as commented by @EricPostpischil), so I got rid of it in my example.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
3

A modification of the answer of davidlowryduda / gsamaras to be compatible with any type of array, not just int, however that impacts the called function which gets a pointer rather than a value :

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

Compilation and execution:

pi@raspberrypi:~ $ gcc -pedantic -Wall -Wextra i.c
pi@raspberrypi:~ $ ./a.out
1
2
3
4
5
6
7
8
bruno
  • 32,421
  • 7
  • 25
  • 37
1

You can define a macro for this.

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

In this case you don't even need a function and it can be used to iterate over any array of any type as long as you know its length.

LeoVen
  • 632
  • 8
  • 18
  • *I've looked around for a solution but they all seem to use macros whereas I would prefer a function if at all possible.* (c) The OP – Eugene Sh. Apr 01 '19 at 17:58
  • @EugeneSh. _It needs to be generic so it will probably require a macro_ - The OP – LeoVen Apr 01 '19 at 20:10
0

You could do something like that using function pointers. But using macros would be very more powerful, it will allow your foreach to be generic.

#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);   
}
Cryckx
  • 659
  • 6
  • 18
  • 1
    it is more much practical to give the array and length directly as parameters rather than to fill a struct then to give that struct in parameter, your proposal is complicated for nothing, sorry ;-) – bruno Apr 01 '19 at 16:48
0

I took a solution's friend on forum and I increased. I got the idea that foreach doesn't have to have a value to stop the loop.

#include <iostream>
#include <stdio.h>

void for_each(int * arr, void (*fun)(int)){

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

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

int main(){
int array [] = {1, 2, 3, 4, 5, 6};
for_each(array, print_num);
}
J. Murray
  • 1,460
  • 11
  • 19