Comunque sia, io farei così (con tutti i miei limiti)...
Definita una struttura tipo questa:
typedef struct RICORRENZA {
int valore;
size_t quantita;
} RICORRENZA;
Potresti implementare una funzione tipo rileva_ricorrenze...
RICORRENZA *rileva_ricorrenze( int *v, size_t dim_v );
La funzione potrebbe verificare quanti rilevamenti sono necessari, quindi allocare un vettore di elementi di tipo RICORRENZA e "compilarlo".
Per rilevare quanti elementi sono necessari, potresti scorrere il vettore v e contare la quantità di valori diversi in esso presenti.
Ad esempio, usando una variabile ausiliaria aux e due contatori qvu (quantità valori unici) e n (numero di ricorrenze) potresti eseguire un ciclo annidato del tipo...
for( qvu=0, i=0; i<dim_v; ++qvu )
for( aux=v[i], n=0; v[i]==aux&&i<dim_v; ++i, ++n );
...che, partendo dal presupposto che il vettore v sia ordinato in senso crescente, "osservi" uno ad uno gli elementi del vettore v stesso e conti in qvu quanti sono i valori unici.
Una volta scoperta la quantità dei valori unici occorrerebbe predisporre un vettore di elementi di tipo RICORRENZA dove immettere osservazioni più specifiche. Siccome non si potrebbe sapere quanti elementi dovrebbero trovare posto in quel vettore, non si potrebbe fare altro che allocarlo dinamicamente.
r = calloc( qvu+1, sizeof(*r) );
if( !r ) return r; // l'allocazione potrebbe fallire
Scopiazzando il metodo che usa il C per indicare la fine degli array di caratteri nelle stringhe, potresti usare un valore arbitrario (ad esempio potresti usare (size_t)-1) per indicare la fine della serie degli elementi RICORRENZA.In questo caso, l'allocazione riguarderebbe qvu+1 elementi.
A questo punto, potresti ri-"osservare" il vettore v e memorizzare in r i risultati delle "osservazioni".
for( i=0, j=0; i<dim_v; ++j ) {
for( aux=v[i], n=0; v[i]==aux&&i<dim_v; ++i, ++n );
r[j].valore = aux;
r[j].quantita = n;
}
Occorrerebbe non dimenticare di mettere il "terminatore" convenzionale in coda alla serie degli elementi RICORRENZA rilevati.
r[j].quantita = (size_t)-1; // (size_t)-1 è un valore "terminatore" arbitrario.
Alla fine della fiera, si dovrebbe passare come valore di ritorno il puntatore al vettore di elementi RICORRENZA allocato e "compilato", che sarebbe NULL in caso di errore.
Ovviamente, come sempre, potrei aver scritto delle scempiaggini poco "ortodosse". Tienilo ben presente (sono un hobbista).