Newsgroup: it.comp.lang.c
Titolo: reimplementazione di sizeof

Ho un algoritmo in cui ho bisogno di una funzione simile alla sizeof()
la mia funzione deve accettare come parametri gli stessi di sizeof,
perchè poi glieli deve passare.
int newsizeof(parametro){
   int dimensione;
   dimensione=sizeof(parametro);
   return dimensionze;
}
int main(){
   char x;
   printf("%d %d",newsizeof(char),newsizeof(x));
   return 0;
}
se funzionasse correttamente, mi aspetterei di vedere su schermo "1 1"
invece newsize(x) mi restituisce 4 mentre newsize(char) mi da syntax
error in fase di compilazione.
chiaramente così com'è (seppure funzionasse) l'algoritmo è inutile
perchè newsizeof sarebbe sizeof, ma dentro la newsizeof poi ci metterò
anche altre cose.
C'è un modo?
se proprio non si può fare newsizeof(char) mi basterebbe che
newsizeof(x) funzionasse correttamente.
Matteo

Ho trovato questo... non è che sia molto bello però.
int returnsizeof(int size){
    return size;
}
#define newsizeof(param) returnsizeof(sizeof(param))
int main(){
    char x;
    printf("%d %d",newsizeof(x),newsizeof(char));
    return 0;
}
che ne pensate? c'è di meglio?
Matteo

> int newsizeof(parametro){
Di che tipo e' 'parametro'? Se non lo dici credo che defaulta a 'int', e quindi ottieni sempre 4.
Pleg

perchč poi glieli deve passare.
sizeof č un operatore (restituisce un size_t, non un int), non una
funzione. Il suo funzionamento č stabilito a tempo di compilazione,
non a run-time. 

Se non lo dici credo che defaulta a 'int', e quindi ottieni sempre 4.
Ma neanche per sogno. Non c'è un tipo di default per i parametri.
p.s.: un int non occupa necessariamente 4 byte.

 non a run-time.
ah ok, grazie.
avevo risolto col #define di cui sopra, ma la gestione risultava
comunque complicata (anche perchè dovevo comunque gestire il fatto che
l'argomento potesse essere un puntatore ad una variabile di un qualche
tipo non specificato)
Alla fine ho deciso che al momento mi posso accontentare di utilizzare
solamente int, il che elimina il problema.
Ora ho un altro problema. Determinare il numero di elementi di un
array, ovvero il numero col quale è stato inizializzato, se con int
v[10]; o, equivalentemente, l'area di memoria assegnata con una calloc
(dal momento che è così che lo creo).
Non ho ancora provato quindi non so se è fattibile (ma mi sembrerebbe
strano vedere che non si può tenere sottocontrolla la dimensione della
memoria che assegno).
Matteo

 (dal momento che è così che lo creo).
Nel caso di un array, il numero degli elementi è sizeof(array)/sizeof(tipo).
Nel caso di un'area di memoria allocata dinamicamente, le dimensioni sono
l'argomento passato alla funzione malloc (o calloc). Il programma deve
tenersi traccia di quel valore, giacché non c'è alcun modo di ricavarlo
conoscendo soltanto l'indirizzo dell'area allocata.
Max

(dal momento che è così che lo creo).
Attento a distinguere array e puntatori, perché non sono la stessa
cosa. L'array, per definizione, è SOLO quello allocato staticamente e
la cui dimensione, quindi, è conosciuta a tempo di compilazione. In
questo caso vale il consiglio di Max, ovvero se hai:
T v[10];
con al posto di T un tipo qualsiasi, il numero di elementi è
ottenibile con: sizeof v / sizeof v[0]
Nel caso di memoria allocata a run-time ed assegnata ad un puntatore,
non c'è modo di ricavare la lunghezza. Devi conoscerla a priori.
Stai però molto attento al caso dei parametri formali di una funzione.
Se scrivi:
#include <stdio.h>
#include <stdlib.h>
void writeSizeOfArray(int v[10]) {
    printf("size is: %d\n", sizeof v / sizeof v[0]);
}
int main()
{
    int v[10];
    printf("size is: %d\n", sizeof v / sizeof v[0]);
    writeSizeOfArray(v);
    return 0;
}
sulla mia macchina ottieni come output:
size is: 10
size is: 1
L'argomento di una funzione, anche se indicato con la notazione di un
array, è comunque un puntatore, cioè equivale a scrivere: 
void writeSizeOfArray(int *v) { ... }
La printf() presente nel main() stampa quel valore perché calcola
"sizeof v" che è evidentemente "10 * sizeof(int)" e la divide per
"sizeof v[0]" che è uguale a "sizeof(int)". Risultato: 10.
La seconda printf(), invece, stampa 1 perché il primo "sizeof v"
equivale a un "sizeof(int*)" che sulla mia macchina è 4. Divide quindi
per "sizeof(int)" che sulla mia macchina è 4. Risultato: 4/4=1.

 conoscendo soltanto l'indirizzo dell'area allocata.
acc.
avevo bisogno di questo.
vabbé.. troverò un'altra soluzione
Matteo

Matteo Rossini ha scritto:

Sfortunatamente gli estensori delle librerie non hanno previsto una funzione
ad oc standard per tornare la dimensione di un oggetto allocato 
dinamicamente....
(che mi ricordi, almeno... potrei sbagliarmi perche' in alcuni sistemi 
mistici
go trovato una a getmemsize(...), ma  non e' standard per nulla ).
Pero' una soluzione *non* standard, se hai a disposizione i sorgenti
delle librerie, e' possibile: guarda l' implementazione della free e 
vedi come
ottiene la dimensione del blocco di memoria da rilasciare....poi 
scriviti una
funzione ad oc per tornare il size... certo, nob sarebbe per nulla portabile
e legata ad una specifica versione della libreria....
Se, no devi farti un set di funzioni ad oc (tipo overloading :-) che 
tengano conto
delle info che vuoi tenere, che so, una lista linkata (dinamica, oppure 
semplicemente
statica) dove tieni il puntatore al blocco di memoria e il suo size....certo
e' uno spreco...
Un suggerimento, piuo' o meno, pseudocodice C al seguito...
typedef struct minfo{
    struct minfo* pNext;   //
    struct minfo* pBack;  //
    void*  pMem;
    size_t  len;
}MINFO_T;
MINFO_T*  new_info(void)
{
   // Crea nuovo elemento nella lista informazioni
}
void  free_info(MINFO_T*  pInfo)  // Cancella oggetto dalla lista
{
    //...........
}
MINFO_T*   search_info(void*  pInfo)  // Cancella oggetto dalla lista
{
   MINFO_T*  pStart;
   MINFO_T*  pFound=NULL;
    //...........
   if(NULL  != (pStart=....... //First member of list .......))
    {
        while((pInfo->pMem != pMem)  && (NULL != pInfo->pNext))
        {
              pInfo= pInfo->pNext;
        }
    }
    return(pFound);
}
void  store_info(MINFO_T*  pInfo, void* pMem,  size_t len)
{
    // Salva info nella lista
      pInfo->pMem=pMem;
      pInfo->len      =len;
    //
}
void* my_malloc(size_t len)
{
    void*             pMem;
    MINFO_T*   pInfo;
   if(NULL== ( pInfo= new_info())
       return(NULL); // Lista piena o essore di sistema
     if(NULL != ( pMem= malloc(len))
    {
            // Salva info in lista linkata o struttura
           store_info( pInfo, pMem, len);
    }
   return(pMem);
}
void my_free(void* pMem)
{
    MINFO_T*   pInfo;
     if(NULL!= ( pInfo= search_info(pMem))
    {
         free_info(pInfo);  // Cancella oggetto dalla lista
         free(pMem);
    }
}
In C++ verrebbe meglio ;-)
Bye
B.

..oppure, una idea piu' semplice, ma un poco meno pulita....
typedef struct {
    size_t   size;
}MH_T;
typedef struct {
      MH_T    info;
      char       mem[2];
}MM_T;
#define offset   (&(MM_T*(0))->mem[0])  // Offset al campo mem
void* my_malloc(size_t len)
{
   void*    pMem;
   if(NULL == ( pMem= malloc( len+sizeof(MM_T) ) )
     return(NULL);
    (MM_T*)pMem->info.size=len;
    return((void*) &(((MM_T*)pMem)->mem[0])  ).
}
void my_free(void* pMem)
{
       void*    pInfo;
 
       pInfo= ((char*)pMem)-offset;   // Punta  a len
        free(pInfo);
}
size_t my_size(void* pMem)
{
       void*    pInfo;
 
       pInfo= ((char*)pMem)-offset;   // Punta  a len
    return( ((MM_T*)pInfo)->info.size);
}

credo che per ora mi terrò il passaggio della dimensione del vettore
per parametro :-D
cmq hai colto più o meno quello che avrei voluto fare.
in particolare una serie di funzioni personalizzate per la gestione
dei vettori.
Per esempio (inventando da zero la sintassi):
definiscioggetto {
  vett[]
  size
  inizializza(int numelementi,char tipo)
  distruggi()
  riempi()
  stampa()
} VECT
uso:
VECT vettore;
vettore->inizializza(10,'i'); // 'i'=int
vettore->riempi;
vettore->stampa;
vettore->distruggi;
già come sola definizione presenta una cifra di bug.
credo che si possa fare in c++
ma lo scopo iniziale era quello di non usare oggetti e lasciare alle
funzioni la determinazione della dimensione e della dimensione e del
tipo.
void vect_init(void* vettore,int numelem);
char *vettore;
vect_init(&vettore,10);
per creare un vettore di 10 elementi; ci avrebbe pensato la funzione a
capire il tipo (char) e quindi la dimensione da allocare. Così come
vect_print(&vettore) avrebbe determinato autonomamente la dimensione,
e l'utente non avrebbe dovuto occuparsi di trattare una struct ma un
semplice vettore.
Ora che ci penso per la prima funzione ci posso pure riuscire, forse;
devo fare un po' di prove.
mmmm
mi è appena venuta in mente una idea... stasera la provo e vi faccio
sapere.
Matteo

 capire il tipo (char) e quindi la dimensione da allocare.
Questo non è evidentemente possibile perché una funzione di quel genere va
definita in qualche modo e la definzione implica la conoscenza del tipo
del puntatore. Nel momento in cui c'è un cast (che in C è automatico) da
char* a void* si perde qualsiasi informazione sul tipo di partenza. In
ogni caso, a meno di cose molto particolari, l'introspezione del tipo a
run-time non è quasi mai una buona idea.
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 tipo.
Scusa, ma che tipo di esigenze hai? Non si capisce. C e C++ sono linguaggi
diversi. Se scrivi qui, si presume che tu stia programmando in C. Quindi il
riferimento al C++ è fuori luogo.
La gestione di array instanziati dinamicamente è una necessità che emerge
più o meno in qualsiasi programma in C che non sia banale. Visto che sei
alle prime armi, non vedo perché cercare di inventarsi soluzioni. Molto
meglio studiarsi il codice di qualche programma o libreria esistente. Ad
esempio, la GLib ha una gamma di funzioni/macro apposite (g_array_*) per
istanziare, popolare e ridimensionare array di dimensioni arbitrarie.
Potresti prendere spunto da lì. Difficilmente potrai inventarti soluzioni
più efficaci.
Max

 char* a void* si perde qualsiasi informazione sul tipo di partenza.
vero, ma foooooorse (un bel po' foooorse, per la verità) sono riuscito
a trovare una soluzione per aggirare il problema.
Ma prima di dirla la voglio elaborare (anche perchè non ho un
compilatore davanti ora)
Matteo

a trovare una soluzione per aggirare il problema.
Non si capisce bene quale sia questo problema...

Beh, in questo 3ad c'è stata molta confusione perchè da subito non ho
spiegato le mie intenzioni iniziali.
Parte le ho spiegate dopo; in parte ho cambiato idee 10 volte in base
anche a quello che ho appreso sia fattibile o non lo sia, dai post o
dagli esperimenti successivi.
L'idea iniziale è nata dall'esigenza di voler fare una serie di
programmi sulla gestione dei vettori (algoritmi di ordinamento,
ricerca, esperimenti, ecc ecc).
Il problema dei vettori è sempre quello che si perde più tempo a fare
il riempimento e la stampa piuttosto che il vero algoritmo da testare.
E' venuta allora l'idea di fare un vettori.h (ovvio con relativo .c)
da includere al programma che dovrà utilizzare questi vettori.
Avrei voluto farlo il più generico possibile, ovvero, poter utilizzare
sempre le stesse funzioni indipendentemente dal tipo; rimane chiaro
che l'ideale (inesistente né possibile) sarebbe stato:
void vect_create(autotype *vect,int numelementi)
e
void vect_stampa(autotype *vect)
senza necessità di dover specificare il numero di elementi.
ovviamente "autotype" NON esiste.
allora ho cercato di fare una cosa che forse non è completamente
infattibile che avrebbe dovuto fare:
int *vettore=vect_create(int, 10);
traducendo int grazie ad una define
#define vect_create(tipo,nelem) calloc(size_of(tipo),nelem)
che viene tradotto in fase di compilazione.
Questa è una soluzione, ma costringe a specificare ogni volta il tipo
e la dimensione.
#define vect_create(tipo,nelem) calloc(sizeof(tipo),nelem)
#define vect_print(tipo,vect,nelem) vect_print_do(#tipo,vect,nelem)
void vect_print_do(char *tipo,void *vect,int nelem){
        char form[10];
        if(strcmp(tipo,"int")==0){
                int *vecti=vect;
                printf("%s-%i\n",tipo,vecti[0]);
        }else if(strcmp(tipo,"char")==0){
                char *vectc=vect;
                printf("%s-%c\n",tipo,vectc[0]);
        }
}
int main(){
        char *vetchar=vect_create(char,10);
        vetchar[0]='x';
        vect_print(char,vetchar,10);
        int *vettore=vect_create(int,10);
        vettore[0]=1;
        vect_print(int,vettore,10);
        free(vetchar);
        free(vettore);
        return 0;
}
Certo, mi dispiace che non è calcolabile la dimensione e il tipo
autonomamente (ho visto il codice di 'free' delle glibc; complessità a
parte mi sembra non applicabile... ma non lo avrei applicato
comunque).
Se si fosse potuto fare sarebbe stato tutto trasparentissimo.
cmq è ora che comincio a farci qualcosa, con questi array ;-)
@Max M.
per il c++; non intendevo che voglio usare il c++, ma che questo
genere di cose si fanno in c++; la frase incompleta dava come continuo
inplicito "quindi niente da fare"
Ciao,
Matteo

 Se si fosse potuto fare sarebbe stato tutto trasparentissimo.
Quello che vuoi fare sarebbe un'ottima motivazione per passare al C++.
E.

 senza necessità di dover specificare il numero di elementi.
metti l'array in una struct
struct{
int num_elementi;
void* v;
}
e alle varie funzioni passi solo quella.
Ciao Jack
-- 
Eroi non si nasce, ti incastrano
- Jim Belushi


E' una delle varie soluzioni uscita nel 3ad.
L'ho applicata.. ma in parte.
Comunque nella struct ho aggiunto anche
char *type[20];
per soluzione simile intendo il fatto che comunque voglio che l'utente
utilizzi il vettore come vettore e non come struct, ma le funzioni
vect_init() vect_stampa() ecc, si memorizzano le relative struct in
una loro area riservata (una lista) e ad ogni uso del vettore va a lì
cercarne le caratteristiche... così mi impratichisco anche con i
vettori, visto che sto un po arrugginito (l'ultima volta li ho fatti
in pascal oltre 15 anni fa).
Non è la soluzione più efficiente, ma così per l'utente è
completamente trasparente.
Matteo

 in pascal oltre 15 anni fa).
Non credo che quella che stai adottando sia una soluzione sensata.
Stai affaticando di parecchio il codice in Runtime per risparmiarti
(forse!) qualche battuta durante la fase di scrittura del codice.
Quando scrivi "utente" a chi ti riferisci? A chi usa il programma?
Oppure stai scrivendo una libreria che useranno altri programmatori?
E.