C string parser criticism I have written a program to parse a string depending on the string the user provides.
Any advise/criticism is appreciated.
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdint.h&gt;

#define DEFAULT_SIZE 10
#define RESIZE_MULTI 2

/*compares value to see if it exists in parse. return 0 if exists*/
static uint8_t compare(const char value, const char *parse)
{
   size_t i;

   i = 0;
   while(*(parse + i) != '\0')
   {
      if(*(parse + i) == value)
         return 0;
      i++;
   }
   return 1;
}

/*string must be null terminated. the string that needs to be split*/
/*parse must be null terminated. contains value that the string will be split at*/
/*matrix contains all the string that was split based on parse. the function return how how many string there are*/
size_t parse_string(const char *string, const char *parse, char *** const user_matrix)
{
   /*how many spots there are*/
   size_t temp_matrix_size;
   /*how many acctually string there are*/
   size_t temp_matrix_length;
   char **temp_matrix;
   size_t i;
   size_t counter;

   /*allocate initial memory. array of pointers*/
   temp_matrix = malloc(sizeof(*temp_matrix) * DEFAULT_SIZE);
   if(temp_matrix == NULL)
      goto FAIL0;
   temp_matrix_size = DEFAULT_SIZE;
   /*since right now there 0 string aka pointers to strings*/
   temp_matrix_length = 0;


   i = counter = 0;
   while(*(string + i) != '\0')
   {
      /*resize array of pointers*/
      if(temp_matrix_length &gt;= temp_matrix_size)
      {
         char **temp = realloc(temp_matrix, sizeof(*temp_matrix) * temp_matrix_size * RESIZE_MULTI);
         if(temp == NULL)
            goto FAIL0;
         temp_matrix = temp;
         temp_matrix_size = temp_matrix_size * RESIZE_MULTI;
      }

      if(compare(*(string + i), parse) == 0)
      {
         char *temp;
         if(counter &gt; 0)
         {
            /*need the plus 1 for terminating char*/
            temp = malloc(sizeof(*temp) * (counter + 1));
            if(temp == NULL)
               goto FAIL0;

            /*setting terminating char*/
            *(temp + counter) = '\0';
            /*copy over the string before the parsing char*/
            memcpy(temp, string + (i - counter), counter);

            /*store the string*/
            *(temp_matrix + temp_matrix_length) = temp;
            temp_matrix_length++;
         }

         /*store only the parsing char with null terminated char*/
         temp = malloc(sizeof(*temp) * 2);
         if(temp == NULL)
            goto FAIL0;
         *(temp + 0) = *(string + i);
         *(temp + 1) = '\0';

         *(temp_matrix + temp_matrix_length) = temp;
         temp_matrix_length++;

         i++;
         counter = 0;
      }
      else
      {
         i++;
         counter++;
      }
   }

   /*special case*/
   if(counter != 0)
   {
      char *temp = malloc(sizeof(*temp) * (counter + 1));
      if(temp == NULL)
         goto FAIL0;

      /*setting terminating char*/
      *(temp + counter) = '\0';
      /*copy over the string before the parsing char*/
      memcpy(temp, string + (i - counter), counter);

      /*store the string*/
      *(temp_matrix + temp_matrix_length) = temp;
      temp_matrix_length++;
   }

   *user_matrix = temp_matrix;

   return temp_matrix_length;

   FAIL0:
   if(temp_matrix != NULL)
   {
      /*free all the strings*/
      for(i = 0; i &lt; temp_matrix_length; i++)
      {
         free(*(temp_matrix + i));
      }
      /*free the array of pointers*/
      free(temp_matrix);
   }
   *user_matrix = NULL;
   return 0;
}

int main(void)
{
   char **matrix;
   size_t size;

   /*can have anything you want*/
   char *str = &quot;Hello Word!!!&quot;;
   /*can have any char you want*/
   char *parse = &quot;! &quot;;

   size = parse_string(str, parse, &amp;matrix);

   for(size_t i = 0; i &lt; size; i++)
   {
      printf(&quot;%s\n&quot;, *(matrix + i));
   }

   for(size_t i = 0; i &lt; size; i++)
   {
      free(*(matrix + i));
   }
   free(matrix);

   return 0;
}
Major remark of the code as whole: for some reason you write the unreadable version of array access *(arr+i) all over the place. Don't do that! This makes your code look needlessly obscure and hard to read. Instead use the much more readable arr[i].

Your compare is just a naive implementation of strchr. It would be much more efficient to use strchr.

Don't write while loops when you can write for loops, the latter is easier to read and often optimizes better too.

There is no point in const qualifying parameters passed by value - it only confuses the caller. Though this is a bit subjective and coding style.

The &quot;correct naive&quot; implementation of strchr would rather look like this:
  static const char* compare (char value, const char *parse)
  {
    for(size_t i=0; parse[i] != '\0'; i++)
    {
      if(parse[i] == value)
      {
        return &amp;parse[i];
      }
    }
    return NULL;
  }


Since you don't use the above version but just return 1 or 0, you end up calling compare over and over on the same data, which is inefficient. If you had a version returning a pointer or index, you could continue searching from that location at next iteration, instead of starting over.

You can get rid of the &quot;on error goto&quot; pattern and leave all clean-up to a wrapper function. It's a bit more readable and you don't have to suffer the old &quot;goto considered harmful&quot; debate yet again:
  size_t parse_string (/* params */)  // wrapper function
  {
    // declare all resources potentially needing clean-up here:
    char **temp_matrix = NULL;  
    ...

    bool ok;
    ok = actual_function (/* params */); // the algorithm, private static inline

    if(!ok)
    {
      if(temp_matrix != NULL)  // the clean-up code from your FAIL0 label here
      ...
    }
  }

Upon failure, &quot;actual_function&quot; just return false; and then the wrapper function can worry about clean-up.
(For more intricate scenarios like some opaque struct allocated by your lib, you can move the whole clean-up code into yet another public &quot;destructor&quot; function.)

Generally, more than 2 levels of indirection is bad practice, but your scenario here is one of the very few cases where three *** is actually motivated. However, char *** const means that the outer-most pointer is const-qualified, which is just weird. The caller doesn't care if you change that local variable internally or not, it just serves to confuse the user. Again, just drop const of parameters passed by value. In this case the char*** passed by value.

Don't name your variables with identifiers used by C++ keywords, functions, libraries etc. Like string. Even if you never intend to use the code with C++, many IDE use same code formatting and color coding for C and C++, like this site for example.