hilpers


  hilpers > comp.lang.* > comp.lang.c

 #1  
26.01.2008, 19:22
pozz
Nelle mie applicazioni mi ritrovo spesso a dover passare ad una funzione il
puntatore ad un buffer in memoria. Come ho visto fare nelle varie funzioni di
libreria (vedi memcpy e simili), ho pensato di dichiarare il buffer come (void *).

In questo modo, il chiamante può passare un qualsiasi puntatore, senza fare cast.

All'interno della funzione, però, devo accedere al buffer scrivendolo byte a
byte e quindi mi serve un (unsigned char *).
Normalmente faccio così:
void dummy( void *bufp ) {
unsigned char *buf = bufp;
*bufp++ = 0xAA;
*bufp++ = 0xAB;
..
Ma così perdo spazio sullo stack per bufp e per buf e mi sembra abbastanza inutile.
 #2  
26.01.2008, 22:40
?manu*
pozz wrote:
> Nelle mie applicazioni mi ritrovo spesso a dover passare ad una funzione
> il puntatore ad un buffer in memoria. Come ho visto fare nelle varie
> funzioni di libreria (vedi memcpy e simili), ho pensato di dichiarare il
> buffer come (void *).
>
> In questo modo, il chiamante può passare un qualsiasi puntatore, senza
> fare cast.
>
> All'interno della funzione, però, devo accedere al buffer scrivendolo
> byte a byte e quindi mi serve un (unsigned char *).
> Normalmente faccio così:
> void dummy( void *bufp ) {
> unsigned char *buf = bufp;
> *bufp++ = 0xAA;
> *bufp++ = 0xAB;
> ..
> Ma così perdo spazio sullo stack per bufp e per buf e mi sembra
> abbastanza inutile.


Probabilmente il compilatore se ne accorge e non ti fa perdere spazio
per niente... In C++ userei invece un riferimento, in questo caso...

E.
 #3  
27.01.2008, 08:42
Giovanni
On 01/26/08 20:22, pozz wrote:
> Nelle mie applicazioni mi ritrovo spesso a dover passare ad una funzione
> il puntatore ad un buffer in memoria. Come ho visto fare nelle varie
> funzioni di libreria (vedi memcpy e simili), ho pensato di dichiarare il
> buffer come (void *).
>
> In questo modo, il chiamante può passare un qualsiasi puntatore, senza
> fare cast.
>
> All'interno della funzione, però, devo accedere al buffer scrivendolo
> byte a byte e quindi mi serve un (unsigned char *).
> Normalmente faccio così:
> void dummy( void *bufp ) {
> unsigned char *buf = bufp;
> *bufp++ = 0xAA;
> *bufp++ = 0xAB;
> ..
> Ma così perdo spazio sullo stack per bufp e per buf e mi sembra
> abbastanza inutile.


Forse volevi scrivere:
void dummy( void *bufp ) {
unsigned char *buf = bufp;
*buf++ = 0xAA;
*buf++ = 0xAB;
..

In ogni caso se vuoi evitare di allocare una variabile nello stack
puoi usare il cast:
void dummy(void *bufp)
{

*((char *)bufp++) = 0xAA;
*((char *)bufp++) = 0xAB;
....
}

Ciao
Giovanni
 #4  
27.01.2008, 09:14
pozz
?manu* ha scritto:
> pozz wrote:


In realtà l'esempio che volevo scrivere è sbagliato. Lì sopra bisogna leggere
(*buf++ = ...) e non (*bufp++ = ...)

>> Ma così perdo spazio sullo stack per bufp e per buf e mi sembra
>> abbastanza inutile.

>
> Probabilmente il compilatore se ne accorge e non ti fa perdere spazio
> per niente... In C++ userei invece un riferimento, in questo caso...


Sì, ci avevo pensato. Però ammettendo che il compilatore se ne accorga, non mi
sembra uno stile di programmazione molto pulito... o sbaglio?
Come farebbe una memcpy(), per esempio?
 #5  
27.01.2008, 10:37
crxor 666
pozz <pNoOzSzPuAgMno> wrote:

> Ma così perdo spazio sullo stack per bufp e per buf e mi sembra
> abbastanza inutile.


Perdi 4 o 8 byte per un puntatore in più nella peggiore delle ipotesi.
Sicuro che sia inaccettabile?

Aggiungo altre note... se conosci giù quello che ci devi scrivere,
perchè non ne fai un array di letterali ({0xAA, 0xAB, ...}) e lo copi
proprio con memcpy?

Se non usi questa strada, perchè non fattibile, io considererei
l'alternativa di una macro (da poi fare sparire) al posto di

*((char *)bufp++) = 0xAB;

#define SIGNIFICATIVE_NAME(pvoid, value) \
*((char *)(pvoid)++) = (value);

E a questo punto scavalli il tutto. Ovviamente a patto di trovare un
nome significativo che renda *effettivamente* il codice più leggibile.
 #6  
27.01.2008, 12:09
Giorgio Silvestri
"Giovanni" <lsodgf0> ha scritto nel messaggio
news:jag1
> On 01/26/08 20:22, pozz wrote:
>
> Forse volevi scrivere:
> void dummy( void *bufp ) {
> unsigned char *buf = bufp;
> *buf++ = 0xAA;
> *buf++ = 0xAB;
> ..
>
> In ogni caso se vuoi evitare di allocare una variabile nello stack
> puoi usare il cast:
> void dummy(void *bufp)
> {
>
> *((char *)bufp++) = 0xAA;
> *((char *)bufp++) = 0xAB;
> ....
> }


Attenzione che non compila!

Il ++ viene associato direttamente a bufp non a (char *)bufp
Io preferisco la versione con la temporanea.
Più pulito.
 #7  
27.01.2008, 12:11
Giorgio Silvestri
"crxor 666" <riko> ha scritto nel messaggio
news:riko
> pozz <pNoOzSzPuAgMno> wrote:
>
> > Ma così perdo spazio sullo stack per bufp e per buf e mi sembra
> > abbastanza inutile.

>
> Perdi 4 o 8 byte per un puntatore in più nella peggiore delle ipotesi.
> Sicuro che sia inaccettabile?
>
> Aggiungo altre note... se conosci giù quello che ci devi scrivere,
> perchè non ne fai un array di letterali ({0xAA, 0xAB, ...}) e lo copi
> proprio con memcpy?


Questa è un'idea.

>
> Se non usi questa strada, perchè non fattibile, io considererei
> l'alternativa di una macro (da poi fare sparire) al posto di
>
> *((char *)bufp++) = 0xAB;


Stesso problema del post di Giovanni.

>
> #define SIGNIFICATIVE_NAME(pvoid, value) \
> *((char *)(pvoid)++) = (value);
>


Idem.
 #8  
27.01.2008, 13:08
Giovanni
On 01/27/08 13:09, Giorgio Silvestri wrote:

>> In ogni caso se vuoi evitare di allocare una variabile nello stack
>> puoi usare il cast:
>> void dummy(void *bufp)
>> {
>>
>> *((char *)bufp++) = 0xAA;
>> *((char *)bufp++) = 0xAB;
>> ....
>> }

>
> Attenzione che non compila!


Puoi sempre riscriverlo con un ulteriore livello di parentesi:
*(((char *)bufp)++) = ....
ma compila (e funziona) perfettamente con il gcc anche senza le
parentesi. Ma ti posso assicurare che funziona tutti i compilatori.

> Il ++ viene associato direttamente a bufp non a (char *)bufp


No. Il ++ viene associato al lvalue che lo precede e che è bufp ma con
il cast, cioè: (char *)bufp

> Io preferisco la versione con la temporanea.
> Più pulito.


Su questo sono d'accordo.

Ciao
Giovanni
 #9  
27.01.2008, 14:39
crxor 666
Giorgio Silvestri <giorgiosilvestri> wrote:

> > Se non usi questa strada, perchè non fattibile, io considererei
> > l'alternativa di una macro (da poi fare sparire) al posto di
> >
> > *((char *)bufp++) = 0xAB;

>
> Stesso problema del post di Giovanni.


Vero. Non ci avevo pensato (ne lo avevo provato). Il fix è banale. Ma
non piace neppure a me. Io personalmente userei proprio il memcpy. Si sa
mica mai che se lo siano ottimizzato per bene.
 #10  
27.01.2008, 14:42
pozz
Giovanni ha scritto:
> On 01/26/08 20:22, pozz wrote:
>
> Forse volevi scrivere:
> void dummy( void *bufp ) {
> unsigned char *buf = bufp;
> *buf++ = 0xAA;
> *buf++ = 0xAB;
> ..


Sì sì, hai ragione.


> In ogni caso se vuoi evitare di allocare una variabile nello stack puoi
> usare il cast:
> void dummy(void *bufp)
> {
>
> *((char *)bufp++) = 0xAA;
> *((char *)bufp++) = 0xAB;
> ....
> }


Giusto, però così diventa difficile da leggere e complicato da scrivere.
Poi quelle istruzioni sono solo un esempio. In alcuni casi devo scrivere dei
byte con valore fisso, in altri casi devo copiare (mediante memcpy) delle
variabili, in altri casi delle struct, ecc.
 #11  
27.01.2008, 14:43
pozz
crxor 666 ha scritto:
> pozz <pNoOzSzPuAgMno> wrote:
>
>> Ma così perdo spazio sullo stack per bufp e per buf e mi sembra
>> abbastanza inutile.

>
> Perdi 4 o 8 byte per un puntatore in più nella peggiore delle ipotesi.
> Sicuro che sia inaccettabile?


Su microcontrollori con 100 byte di RAM è inaccettabile.


> Aggiungo altre note... se conosci giù quello che ci devi scrivere,
> perchè non ne fai un array di letterali ({0xAA, 0xAB, ...}) e lo copi
> proprio con memcpy?


Non è sempre così. In alcuni casi devo scrivere dei byte letterali, in altri
casi delle variabili, in altri devo copiare delle struct e in altri ancora ho un
mix di queste operazioni da fare sul buffer.


> Se non usi questa strada, perchè non fattibile, io considererei
> l'alternativa di una macro (da poi fare sparire) al posto di
>
> *((char *)bufp++) = 0xAB;
>
> #define SIGNIFICATIVE_NAME(pvoid, value) \
> *((char *)(pvoid)++) = (value);
>
> E a questo punto scavalli il tutto. Ovviamente a patto di trovare un
> nome significativo che renda *effettivamente* il codice più leggibile.


E' un'idea...
 #12  
27.01.2008, 15:17
Giovanni
On 01/27/08 15:42, pozz wrote:

> Giusto, però così diventa difficile da leggere e complicato da
> scrivere. Poi quelle istruzioni sono solo un esempio. In alcuni
> casi devo scrivere dei byte con valore fisso, in altri casi devo
> copiare (mediante memcpy) delle variabili, in altri casi delle
> struct, ecc.


Sul fatto che sia più complicato da scrivere e difficile da leggere
concordo, ma se ti preoccupi di risparmiare anche il singolo byte di
memoria è conveniente. Qualsiasi operazione tu debba fare il cast la
può risolvere. Per semplificare la scritture puoi usare delle macro:

#define bufp_c ((char *)bufp) /* puntatore a char */
#define bufp_i ((int *)bufp) /* puntatore a int */
#define bufp_s ((struct s *)bufp) /* puntatore a struct s */

*(bufp_c)++ = 0x04
*(bufp_i)++ = 123456;
*(bufp_s)->membro_struttura = ....

#undef bufp_s
#undef bufp_i
#undef bufp_c

L'unico side effect che vedo è il fatto che in questi esempi vai a
modificare il valore del parametro di ingresso. In certe situazioni
ed accessi multipli potrebbe servire il valore originale.

Tieni anche presente che usando l'ottimizzazione molti compilatori
mettono la variabile in un registro ed anche tu lo puoi fare con la
keyword register nella dichiarazione della puntatore d'appoggio.
Attenzione però che a volte se la funzione è molto complessa, il
compilatore ha bisogno dei registri ed usa lo stack per salvare
temporaneamente il registro e non risparmia nulla.

Ciao
Giovanni
 #13  
27.01.2008, 16:22
crxor 666
Giovanni <lsodgf0> wrote:

> Tieni anche presente che usando l'ottimizzazione molti compilatori
> mettono la variabile in un registro ed anche tu lo puoi fare con la
> keyword register nella dichiarazione della puntatore d'appoggio.


Tipicamente non farci conto: il compilatore fa un po' quello che vuole.
Non essendo tenuto a rispettare la direttiva, in molti i casi la ignora
e punto. :)
 #14  
27.01.2008, 16:45
Giovanni
On 01/27/08 17:22, crxor 666 wrote:

>
>> Tieni anche presente che usando l'ottimizzazione molti
>> compilatori mettono la variabile in un registro ed anche tu lo
>> puoi fare con la keyword register nella dichiarazione della
>> puntatore d'appoggio.

>
> Tipicamente non farci conto: il compilatore fa un po' quello che
> vuole. Non essendo tenuto a rispettare la direttiva, in molti i
> casi la ignora e punto. :)
>


Difatti avevo scritto anche:
>> Attenzione però che a volte se la funzione è molto complessa, il
>> compilatore ha bisogno dei registri ed usa lo stack per salvare
>> temporaneamente il registro e non risparmia nulla.


Ciao
Giovanni
 #15  
27.01.2008, 17:01
fnegroni
La soluzione piu' elegante, secondo il mio modesto parere, e' che
invece di dichiarare la tua funzione come se accettasse dei void *,
dovresti dichiararla con char *.

Motivo: la tua funzione e' effettivamente una funzione che opera con
l'aritmetica dei puntatori. Per cui la tua funzione ha bisogno di
accedere ai byte.

Il chiamante della tua funzione e' quello che si deve preoccupare di
fare il cast.

Dal punto di vista del compilatore e' piu' facile ottimizzare
l'allineamento dei byte sul chiamante che sul chiamato.

Perche' ricordati, con la dichiarazione void * stai chiedendo al
compilatore di accettare *sempre* un puntatore con allineamento
generico.

Siccome accedi ai singoli byte, se non ti fidi dell'ottimizzazione del
compilatore, effettivamente tu stai chiedendo al compilatore una cosa
non vera.
Tu hai bisogno, nella tua funzione, di avere un puntatore con massima
efficienza ad accesso di byte sequenziale.

Il compilatore ha piu' facilita' a capire la cosa nel codice
chiamante, in quanto il cast viene fatto una sola volta, e il
chiamante puo' usare ottimizzazioni piu' idonee per farlo.

Le implementazioni di memcpy che vedo in giro infatti contano sul
fatto che il processore possa fare delle copie _in blocco_, senza
quindi accedere ai singoli byte.
Le funzioni che operano su byte singoli di regola dichiarano i loro
puntatori come char *, e sta al chiamante decidere se la chiamata e'
opportuna o c'e' bisogno di un cast.

Dal punto di vista dell'interfaccia, che strcpy sia dichiarato con
char * mi fa capire che si sta parlando di byte signoli.
Void * mi fa capire che sia generico e quindi non ottimizzato per la
mia particolare struttura dati.

Questo e' il mio EURO 0.02

Discussioni simili
Da una stringa di byte ad array di byte

Salve a tutti, con byte[] IV = Encoding.ASCII.GetBytes("arcobaleno"); ottengo una serie di numeri "97,114,99,111,98,97,108,101,110,11" che conservo nel web.config per...

come posso spostare un byte all'interno di un'altro byte?

mi spiego,ho 2 byte già definiti(tipo:11110000 e 10001000)e voglio che il secondo byte entri parzialmente nel primo occupandone i primi 3 bit ,quindi con un byte risultante...

Integer=byte*byte [OVERFLOW?]

Ciao a tutti il seguente codice Dim nRighe As Byte Dim nColonne As Byte Dim nElementi As Integer Dim i As Byte Dim ii As Byte Dim index As Integer

Oggetto Buffer as Byte: LENGHT???

Scusate, forse è una domanda troppo idiota... Se io dichiaro dim buffer (1024) as byte quanti caratteri potrò metterci dentro? Secondo quello che ho scoperto sarebbe 3...

prendere i byte in parole di 2 byte

Salve a tutti. Ho un problema che non so risolvere. Ho bisogno di codificare una sequenza numerica in un'altra sequenza numerica. Mi spiego meglio. Supponiamo di aver...


Tutti gli orari sono GMT. Attualmente sono le 01:43. | Privacy Policy