diff --git a/exercises/higher-oder-func/README.md b/exercises/higher-oder-func/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ea7405e6256f06f31791fdf3c5331f60ffe9a67b
--- /dev/null
+++ b/exercises/higher-oder-func/README.md
@@ -0,0 +1,33 @@
+# Higher Order Functions
+
+Here is a small exercise that helps you understand function pointers and void pointers in C.
+
+## Instructions
+
+Implement some higher order functions for arrays.
+
+You will implement the following functions:
+
+* `map`: map a given array with a transform function to a new array. 
+* `filter`: filter a given array with a predicate function to a new array. 
+* `reduce`: "reduce" the array with a given initial accumulator and a reducer function to a final value.
+
+## Tips
+
+TBD
+
+## Testing
+
+The included makefile can be used to create and run the tests using the following commands:
+
+```bash
+# run unit tests
+make test
+
+# check memory leaks
+make memcheck
+
+# clean all compilation files
+make clean
+```
+
diff --git a/exercises/higher-oder-func/higher_order_func.c b/exercises/higher-oder-func/higher_order_func.c
new file mode 100644
index 0000000000000000000000000000000000000000..973f971f658d57d5d44dfe53582e130fed044506
--- /dev/null
+++ b/exercises/higher-oder-func/higher_order_func.c
@@ -0,0 +1,17 @@
+#include "higher_order_func.h"
+#include <stdlib.h>
+#include <string.h>
+
+void *map(void *array, size_t n, size_t size, void (*fn)(void *elem)) {
+  // TODO: implement me!
+}
+
+void *filter(void *array, size_t n, size_t size, size_t *new_size,
+             bool (*pred)(void *elem)) {
+  // TODO: implement me!
+}
+
+void *reduce(void *array, size_t n, size_t size, void *initial_value,
+             void *(*reducer)(void *acc, void *elem)) {
+  // TODO: implement me!
+}
diff --git a/exercises/higher-oder-func/higher_order_func.h b/exercises/higher-oder-func/higher_order_func.h
new file mode 100644
index 0000000000000000000000000000000000000000..c146a8f97f867cdce2e5e9d56ccb259b558c78e6
--- /dev/null
+++ b/exercises/higher-oder-func/higher_order_func.h
@@ -0,0 +1,12 @@
+#ifndef HIGHER_ORDER_FUNC_H
+#define HIGHER_ORDER_FUNC_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+void* map(void* array, size_t n, size_t size, void (*fn)(void* elem));
+void* filter(void* array, size_t n, size_t size, size_t* new_size, bool (*pred)(void* elem));
+void* reduce(void* array, size_t n, size_t size, void* initial_value, void* (*reducer)(void* acc, void * elem));
+
+#endif
+
diff --git a/exercises/higher-oder-func/makefile b/exercises/higher-oder-func/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..765a4227c672256d2a7f1dead1ddd1ef637d394d
--- /dev/null
+++ b/exercises/higher-oder-func/makefile
@@ -0,0 +1,36 @@
+### If you wish to use extra libraries (math.h for instance),
+### add their flags here (-lm in our case) in the "LIBS" variable.
+LIBS = -lm
+
+###
+CFLAGS  = -std=c99
+CFLAGS += -g
+CFLAGS += -Wall
+CFLAGS += -Wextra
+CFLAGS += -pedantic
+CFLAGS += -Wmissing-declarations
+CFLAGS += -DUNITY_SUPPORT_64 -DUNITY_OUTPUT_COLOR
+
+ASANFLAGS  = -fsanitize=address
+ASANFLAGS += -fno-common
+ASANFLAGS += -fno-omit-frame-pointer
+
+.PHONY: test
+test: tests.out
+	@./tests.out
+
+.PHONY: memcheck
+memcheck: ./*.c ./*.h
+	@echo Compiling $@
+	@$(CC) $(ASANFLAGS) $(CFLAGS) ../unity/unity.c ./*.c -o memcheck.out $(LIBS)
+	@./memcheck.out
+	@echo "Memory check passed"
+
+.PHONY: clean
+clean:
+	rm -rf *.o *.out *.out.dSYM
+
+tests.out: ./*.c ./*.h
+	@echo Compiling $@
+	@$(CC) $(CFLAGS) ../unity/unity.c ./*.c -o tests.out $(LIBS)
+
diff --git a/exercises/higher-oder-func/test_higher_order_func.c b/exercises/higher-oder-func/test_higher_order_func.c
new file mode 100644
index 0000000000000000000000000000000000000000..175925df43fd7fb8c4b74154e2a58934e8cdf77c
--- /dev/null
+++ b/exercises/higher-oder-func/test_higher_order_func.c
@@ -0,0 +1,222 @@
+#include "../unity/unity.h"
+#include "higher_order_func.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#define INT_ARRAY_SIZE 100
+
+static int int_arr[INT_ARRAY_SIZE];
+static int int_arr_copy[INT_ARRAY_SIZE];
+
+void setUp(void) {
+  for (int i = 0; i < INT_ARRAY_SIZE; i++) {
+    int_arr[i] = i;
+    int_arr_copy[i] = i;
+  }
+}
+
+void tearDown(void) {}
+
+static void id(void *element) { *(int *)element = *(int *)element; }
+
+static bool tautology(void *element) {
+  int *element_ptr = (int *)element;
+  return *element_ptr >= 0;
+}
+
+static bool always_false(void *element) { return *(int *)element < 0; }
+
+static void double_int(void *element) { *((int *)element) *= 2; }
+
+static void plus_2(void *element) { *((int *)element) += 2; }
+
+static void *sum_int(void *acc, void *element) {
+  int *acc_ptr = (int *)acc;
+  int *element_ptr = (int *)element;
+  *acc_ptr += *element_ptr;
+  return acc_ptr;
+}
+
+static void *product_int(void *acc, void *element) {
+  int *acc_ptr = (int *)acc;
+  if (*acc_ptr == 0) {
+    return acc_ptr;
+  }
+  int *element_ptr = (int *)element;
+  *acc_ptr *= *element_ptr;
+  return acc_ptr;
+}
+
+static bool is_prime(void *element) {
+  int *element_ptr = (int *)element;
+
+  if (*element_ptr < 2) {
+    return false;
+  }
+  for (int i = 2; i < *element_ptr; i++) {
+    if (*element_ptr % i == 0) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static bool is_even(void *element) {
+  int *element_ptr = (int *)element;
+  return *element_ptr % 2 == 0;
+}
+
+static void test_map_empty_array(void) {
+  int *result = map(int_arr, 0, sizeof(int), NULL);
+  TEST_ASSERT_NULL(result);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+}
+
+static void test_filter_empty_array(void) {
+  size_t new_size;
+  int *result = filter(int_arr, 0, sizeof(int), &new_size, NULL);
+  TEST_ASSERT_NULL(result);
+  TEST_ASSERT_EQUAL_INT(new_size, 0);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+}
+
+static void test_reduce_empty_array(void) {
+  int inital_value = 0;
+  int *result = reduce(int_arr, 0, sizeof(int), &inital_value, NULL);
+  TEST_ASSERT_EQUAL_INT(*result, inital_value);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+}
+
+static void test_map_id_array(void) {
+  int *result = map(int_arr, INT_ARRAY_SIZE, sizeof(int), id);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, result, INT_ARRAY_SIZE);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+  free(result);
+}
+
+static void test_filter_totology_array(void) {
+  size_t new_size;
+  int *result =
+      filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, tautology);
+  TEST_ASSERT_EQUAL_INT(new_size, INT_ARRAY_SIZE);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, result, INT_ARRAY_SIZE);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+  free(result);
+}
+
+static void test_map_double_array(void) {
+  int *result = map(int_arr, INT_ARRAY_SIZE, sizeof(int), double_int);
+  for (int i = 0; i < INT_ARRAY_SIZE; i++) {
+    TEST_ASSERT_EQUAL_INT(int_arr[i] * 2, result[i]);
+  }
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+  free(result);
+}
+
+static void test_map_plus_2_array(void) {
+  int *result = map(int_arr, INT_ARRAY_SIZE, sizeof(int), plus_2);
+  for (int i = 0; i < INT_ARRAY_SIZE; i++) {
+    TEST_ASSERT_EQUAL_INT(int_arr[i] + 2, result[i]);
+  }
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+  free(result);
+}
+
+static void test_reduce_sum_array(void) {
+  int inital_value = 0;
+  int *result =
+      reduce(int_arr, INT_ARRAY_SIZE, sizeof(int), &inital_value, sum_int);
+  int expected = 0;
+  for (int i = 0; i < INT_ARRAY_SIZE; i++) {
+    expected += int_arr[i];
+  }
+  TEST_ASSERT_EQUAL_INT(expected, *result);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+}
+
+static void test_product_sum_array(void) {
+  int inital_value = 1;
+  int *result =
+      reduce(int_arr, INT_ARRAY_SIZE, sizeof(int), &inital_value, product_int);
+  int expected = 1;
+  for (int i = 1; i < INT_ARRAY_SIZE; i++) {
+    expected *= int_arr[i];
+  }
+  TEST_ASSERT_EQUAL_INT(expected, *result);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+}
+
+static void test_filter_is_prime_array(void) {
+  size_t new_size;
+
+  int *result =
+      filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, is_prime);
+  int expected[] = {2,  3,  5,  7,  11, 13, 17, 19, 23, 29, 31, 37, 41,
+                    43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97};
+  TEST_ASSERT_EQUAL_INT(new_size, sizeof(expected) / sizeof(int));
+  TEST_ASSERT_EQUAL_INT_ARRAY(expected, result, sizeof(expected) / sizeof(int));
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+  free(result);
+}
+
+static void test_filter_always_false_array(void) {
+  size_t new_size;
+  int *result =
+      filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, always_false);
+  TEST_ASSERT_EQUAL_INT(new_size, 0);
+  TEST_ASSERT_NULL(result);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+}
+
+static void test_filter_is_even_array(void) {
+  size_t new_size;
+  int *result =
+      filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, is_even);
+  int expected[50];
+  for (int i = 0; i < 50; i++) {
+    expected[i] = i * 2;
+  }
+  TEST_ASSERT_EQUAL_INT(new_size, 50);
+  TEST_ASSERT_EQUAL_INT_ARRAY(expected, result, sizeof(expected) / sizeof(int));
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+  free(result);
+}
+
+static void test_is_even_and_sum(void) {
+  size_t new_size;
+  int *result =
+      filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, is_even);
+  int inital_value = 0;
+  int *sum = reduce(result, new_size, sizeof(int), &inital_value, sum_int);
+  int expected = 0;
+  for (int i = 0; i < INT_ARRAY_SIZE; i++) {
+    if (int_arr[i] % 2 == 0) {
+      expected += int_arr[i];
+    }
+  }
+  TEST_ASSERT_EQUAL_INT(new_size, 50);
+  TEST_ASSERT_EQUAL_INT(expected, *sum);
+  TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE);
+  free(result);
+}
+
+int main(void) {
+  UnityBegin("test_higher_order_func.c");
+
+  RUN_TEST(test_map_empty_array);
+  RUN_TEST(test_filter_empty_array);
+  RUN_TEST(test_reduce_empty_array);
+  RUN_TEST(test_map_id_array);
+  RUN_TEST(test_filter_totology_array);
+  RUN_TEST(test_map_double_array);
+  RUN_TEST(test_reduce_sum_array);
+  RUN_TEST(test_filter_is_prime_array);
+  RUN_TEST(test_filter_always_false_array);
+  RUN_TEST(test_filter_is_even_array);
+  RUN_TEST(test_map_plus_2_array);
+  RUN_TEST(test_product_sum_array);
+  RUN_TEST(test_is_even_and_sum);
+
+  return UnityEnd();
+}