Need some advice on my Implementation of strsplit() using C

I write a strsplit function which converts a string separated by a delimeter to a sub-string array.

If anyone can give some advice to optimize it, or raise some new thought about this implementation? Tkx.

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

char * const *strsplit(char *p, char sep)
{

        if (p == NULL)
                return NULL;

        int num = 0;
        int i = 0;
        char **arry = NULL;
        char **newarrybuff;
        char *strcur;
        char *tmpstr = malloc(strlen(p) + 1);

        if (!tmpstr)
                return NULL;
        strcur = tmpstr;
        strcpy(tmpstr, p);

        while (strcur) {
                if (*strcur == sep) {
                        strcur++;
                        continue;
                }
                newarrybuff = realloc(arry, sizeof(char*) * (i+1));
                if (!newarrybuff) {
                        free(arry);
                        return NULL;
                }
                arry = newarrybuff;
                arry[i++] = strcur;
             
                strcur = strchr(strcur, sep);
                if(strcur)
                        *strcur++ = '\0';
        }
         newarrybuff = realloc(arry, sizeof(char*) * (i+1));
         if (!newarrybuff) {
                 free(arry);
                 return NULL;
         }
         arry = newarrybuff;
         arry[i++] = NULL;

         newarrybuff = realloc(arry, sizeof(char*) * (i+1));
         if (!newarrybuff) {
                 free(arry);
                 return NULL;
         }
         arry = newarrybuff;
         arry[i++] = tmpstr;

         return arry;
}

void strsplitfree(char * const *p)
{
        if (!p) return;

        char * const *tmp = p;
        while (*tmp++);
        free(*tmp);
        free((void*)p);
}

int main(int argc, char **argv)
{
        char str[] = "::::hello::wold:";
        char * const *s = strsplit(str, ':');
        int j=0;
        if (s == NULL)
                return 1;
        for (j = 0; s[j] != NULL; j++) {
                printf("%s\n", s[j]);
        }

        strsplitfree(s);
        return 0;
}


First, use strdup() to duplicate a string on the heap.

Second, don't duplicate each and every substring - duplicate the entire string and just split it by replacing the separators with NULs, filling in your pointer array as you go. (Yes, it's spelled "NUL". That's the '\0' character.)

Something like this:

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

char **strsplit( const char *str, char sep )
{
    char *pp;
    char *qq;
    unsigned int sepCount;

    char *newStr = strdup( str );

    // count the separators
    for ( pp = newStr, sepCount = 0; *pp; pp++ )
    {
         if ( sep == *pp ) sepCount++;
    }

    // N separators means N + 1 strings, plus we need
    // a NULL terminator at the end of the pointer array
    char **result = calloc( 2 + sepCount, sizeof( *result ) );

    // first result is the start of the original string
    result[ 0 ] = newStr;

    // now split the string by inserting NUL characters
    // in place of separators, and filling in the pointer
    // array as we go
    pp = newStr;
    sepCount = 1;
    for ( ;; )
    {
        // find the next separator
        qq = strchr( pp, sep );
        if ( NULL == qq ) break;

        // replace it to split the string
        *qq = '\0';

        // point at the start of the next sting
        pp = qq + 1;

        // fill in the array with the next string
        result[ sepCount ] = pp;

        sepCount++;
    }

    return( result );
}

void freesplit( char **split )
{
    free( split[ 0 ] );
    free( split );
}

int main( int argc, char **argv )
{
    char **split;

    int ii;
    int jj;

    for ( ii = 1; ii < argc; ii++ )
    {
        split = strsplit( argv[ ii ], ':' );
        for ( jj = 0; split[ jj ]; jj++ )
        {
            printf( "string[ %d ] = '%s'\n", jj, split[ jj ] );
        }

        freesplit( split );
    }

    return( 0 );
}

Here is an old utility. It splits string from the command line, it also has extra stuff you should probably ignore: parsing options, formatting output.
There are two files:

ssplit.c
classes.c

make:

[g]cc classes.c ssplit.c -o ssplit

1, Why must use strdup but malloc and strcpy?
2, Indeed I duplicate the entire string.
3, Is calloc better than malloc?