Newsgroup: it.comp.lang.c
Titolo: problema puntatore

Ciao a tutti.
Volevo chiedere se  qualcuno di voi ha una risposta  a questo "problema"
che mi e` capitato sotto mano. Se io ho un programma tipo questo:
========================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef MY_BUF_SIZE
# define MY_BUF_SIZE            500
#endif
struct prova
{
  void *ptr;
  int a;
  char b;
};
int main(void)
{
  void *tmp;
  struct prova *why;
  if (NULL != (tmp = malloc(MY_BUF_SIZE)))
  {
    why = (struct prova *)tmp;
    why->ptr = (tmp + sizeof(struct prova));
    (void)memset(why->ptr, 0, MY_BUF_SIZE - sizeof(struct prova));
    why->a = 0;
    why->b = 0;
    free(why);
  }
  return 0;
}
========================================================================
E  provo a  farlo  girare sotto  la supervisione  di  un memory  checker
(valgrind) mi restituisce:
== ERROR SUMMARY: 0 errors from 0 contexts
== malloc/free: in use at exit: 0 bytes in 0 blocks.
== malloc/free: 1 allocs, 1 frees, 500 bytes allocated.
== All heap blocks were freed -- no leaks are possible.
Che e` esattamente  quello che mi aspetto (fare la  free di "why" invece
che "tmp", per via del codice, dovrebbe essere la stessa cosa).
Ora, se invece provo questo main  (praticamente uguale ma senza usare la
variabile temporanea):
========================================================================
int main(void)
{
  struct prova *why;
  if (NULL != (why = malloc(MY_BUF_SIZE)))
  {
    why->ptr = (why + sizeof(struct prova));
    (void)memset(why->ptr, 0, MY_BUF_SIZE - sizeof(struct prova));
    why->a = 0;
    why->b = 0;
    free(why);
  }
  return 0;
}
========================================================================
ottengo una cosa che non riesco a capire:
== ERROR SUMMARY: 132 errors from 1 contexts
== malloc/free: in use at exit: 0 bytes in 0 blocks.
== malloc/free: 1 allocs, 1 frees, 500 bytes allocated.
== All heap blocks were freed -- no leaks are possible.
L'errore nello specifico e`:
== 132 errors in context 1 of 1:
== Invalid write of size 1
== at 0x401D8D1: memset (mac_replace_strmem.c:464)
== by 0x804843E: main (prova.c:47)
== Address 0x415521C is 0 bytes after a block of size 500 alloc'd
== at 0x401B477: malloc (vg_replace_malloc.c:149)
== by 0x8048410: main (prova.c:43)
In sostanza, al lato pratico (non vuole essere una spiegazione teorica),
il buffer  allocato risulta 132  bytes piu` piccolo  dell'aspettato (se,
infatti, sottraggo 132 alla memset *pare* andare tutto liscio)  _ma_  il
memory checker,  comunque, asserisce  che i  bytes  allocati sono sempre
500.
Qualcuno sa  spiegarmi il perche`? (non  e` uno dei quiz  che girano qui
ogni tanto;  io non conosco la  risposta e sono realmente  interessato a
capire il perche` di questo comportamento).
Se, in alternativa (o aggiunta), avete  dei link a documenti (ad esempio
ad una  sezione dello  standard) dove posso  leggere perche`  quello che
cerco di fare, evidentemente (dati i risultati), e` sbagliato li accetto
molto volentieri.
Alcune info riguardo al mio sistema (se possono servire):
- Slackware GNU/Linux 10.2
- gcc (GCC) 3.3.6
- Linux 2.6.17.11 SMP PREEMPT i686
- Intel(R) Pentium(R) 4 CPU 3.00GHz HT
Grazie dell'attenzione.
tester
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 }
Secondo me qui c'è un buffer overrun. Supponiamo di accettare la riga 
incriminata sopra, a questo punto ti sei spostato dall'indirizzo di 
partenza di (sizeof(struct prova))^2 byte (sul mio sistema prova è di 12 
byte, quindi in totale ti sposti di 144) e hai assegnato il nuovo 
indirizzo come valore di ptr. Sotto dici di mettere a 0 500-12=488 byte 
del buvver a partire da un offset di 144, il che implica che puoi 
settarne al più 500-144=356. Questo spiega i 132 byte "mancanti".
Nel primo esempio che hai dato, invece, stai facendo un errore forse più 
grave: ovvero stai usando l'aritmetica dei puntatori con un puntatore void:
why->ptr = (tmp + sizeof(struct prova));
Ciao.

 molto volentieri.
Dovrebbe essere sufficiente un ripassino al capitolo sull'aritmetica dei 
puntatori! ;)
ciao
Giacomo

byte all'indirizzo contenuto in why.
L'aritmetica dei puntatori entra in gioco (come al solito) ma nel caso
specifico why è un puntatore void, quindi al suo indirizzo è sommata
esattamente la misura in byte del tipo di struttura specifico.

    why->ptr = (why + sizeof(struct prova));
Nun ce provà :-)
Occhio che questo codice non è equivalente al precedente.
Il puntatore "why" in questo caso è un puntatore tipizzato, non un
puntatore void, cosa che invece prima era tmp.
Se a un puntatore void sommi n, allora la locazione effettivamente
puntata sarà quella iniziale del puntatore più n byte.
Se a un puntatore a un tipo T sommi n, allora la locazione
effettivamente puntata sarà quella iniziale del puntatore più
n*sizeof(T).
In memory checker ha ragione da vendere ;-)

Come non detto :-) hai ragione.
Errata corrige nell'altro messaggio ;-)
(in realtà, nello scrivere ciò che ho scritto, mi riferivo al codice
in cui la somma era con tmp e non con why).

puntata sarà quella iniziale del puntatore più n byte.
Acc... anche qui ho detto una cagata (è l'ora, pardon :-).
L'aritmetica sui puntatori void non è ovviamente consentita, benché
molti compilatori li trattino come char* e quindi la accettino.

il compilatore che usi si comporta come se tmp fosse un (char*)
Esatto.  Il gcc se non erro implementa quella logica come estensione
dello standard, forse per agevolare i programmatori, ma allontanandosi
dalla conformità.  Teoricamente, dovrebbe bloccare la compilazione con
un messaggio d'errore.

   if (NULL != (tmp = malloc(MY_BUF_SIZE)))
Vorrei fare una semplice nota sulla portabilità.
Sebbene nel tuo sistema non ci sono problemi in questo senso,
non è detto in linea generale che il valore MY_BUF_SIZE
sia maggiore di sizeof(struct prova).
Anche se lo trovo piuttosto improbabile ...
Consiglio, come nota stilistica, di parametrizzare il programma
in questo modo:
#define BUFFER_RESIDUAL 300 /* o altro */
#ifndef MY_BUF_SIZE
# define MY_BUF_SIZE   (sizeof(struct prova) + BUFFER_RESIDUAL)
#endif
La modifica non è poi così stupida o pedante.
In programmi complessi dove il valore delle define possono variare
ci si può trovare in una situazione in cui si perde il contatto
tra la struttura e il valore della define stessa.
E' meglio tenere "agganciate" le due cose.
[...]
--
Giorgio Silvestri
DSP/Embedded/Real Time OS   Software Engineer