Newsgroup: it.comp.lang.c
Titolo: Puntatori a dati costanti

Nel linguaggio C i puntatori a dati costanti sono compatibili con puntatori a 
dati variabili?
Immaginiamo di definire:
   int *var;
   const int *cst;
E' sempre vero che sizeof(var) è uguale a sizeof(cst)?
Su architetture x86 penso di sì poichè le costanti si trovano sempre in RAM ad 
un determinato indirizzo.
Su altre architetture (sto pensando a microcontrollori dove le costanti 
risiedono in Flash o EEPROM per non sprecare spazio in RAM) penso possano essere 
diversi.
I due puntatori, quindi, non dovrebbero essere compatibili tra loro e non 
potrebbero essere usati l'uno al posto dell'altra.
Immaginiamo di avere questa funzione:
   int somma( int *a, int *b ) {
     return (*a)+(*b);
   }
Posso richiamarla in questo modo?
     int a=3, b=4;
     const int *acst = &a;
     const int *bcst = &b;
     int x = somma(acst,bcst);
Con Dev-Cpp ottengo due Warning (passing arg 1 of `somma' discards qualifiers 
from pointer target type), ma il programma sembra funzionare. Probabilmente 
perchè su architettura x86 i due puntatori sono compatibili.
Ma è così su qualsiasi architettura?
Stavo allora pensando di ridefinire la funzione somma in questo modo:
   int somma( const int *a, const int *b ) {
     return (*a)+(*b);
   }
che è forse più corretto visto che (*a) e (*b) non vengono modificati dalla 
funzione.
In questo caso le istruzioni
     int a=3, b=4;
     const int *acst = &a;
     const int *bcst = &b;
     int x = somma(acst,bcst);
non mi danno più warning poichè i qualificatori sono tutti coerenti.
Ma cosa succede se devo passare alla funzione dei puntatori che non sono stati 
definiti come costanti?
     int a=3, b=4;
     const int *acst = &a;
     const int *bcst = &b;
     int *avar = &a;
     int *bvar = &b;
     int x = somma(acst,bcst);
     int y = somma(avar,bvar);
Queste istruzioni non danno problemi, però è così su tutte le architetture?
In particolare, se la funzione somma si aspetta puntatori costanti, come 
potrebbe funzionare passando puntatori non dichiarati costanti su architetture 
dove i due tipi di puntatori non sono tra loro compatibili?
Che dice lo standard a tal proposito, visto che mi stanno cadendo le poche 
certezze che avevo sul C?

 In particolare, se la funzione somma si aspetta *puntatori costanti*, ...
prima di tutto nota la differenza tra le dichiarazioni sotto:
const int *acst; /* e' un puntatore ad una costante di tipo int */
int * const acst; /* e' un puntatore costante ad int */
const int * const acst; /* e' un puntatore costante ad una costante di tipo 
int */
ciao
-- 
Cybertoldo
Per rispondermi via email, sostituisci moc.liamg con 
gmail.com

 const int *acst; /* e' un puntatore ad una costante di tipo int */
Sì sì, a me interessano questi...

pozz ha scritto:

Il puntatore ad una variabile è una costante, ossia quando dichiari "int 
a" l'indirizzo di "a" sarà lo stesso per tutta l'esecuzione del blocco 
d'istruzioni in cui si trova. Const nei parametri delle funzioni indica 
che quel valore non può venire modificato.
int somma( const int *a, const int *b ) {
return (*a)+(*b);
}
Con questa funzione dici al compilatore che il valore puntato da "a" e 
"b" non può essere modificato (non si può fare *a=3), permetti però di 
modificare i valori dei puntatori (si può fare a=b).
I parametri di una funzione sono sempre dei valori allocati in memoria.
Senza tanti giri potresti scrivere:
int a=3, b=4;
int x = somma(&a,&b);
ed accedere agli indirizzi di "a" e "b" tramite l'operatore &, evitando 
confusione nel capire a cosa si riferiscono.
e se vuoi essere proprio preciso somma la puoi dichiarare:
int somma( const int * const a, const int * const b ) {
return (*a)+(*b);
}
Indicando così che non modifichi ne le variabili puntate e ne i 
puntatori "a" e "b". Non si può fare ne *a=3 e ne a=b.

  poche certezze che avevo sul C?
Sono abbastanza sicuro del fatto che puoi sempre convertire da (int *) a 
(const int *).
E.

 puntatori "a" e "b". Non si può fare ne *a=3 e ne a=b.
Mi sembra giusto.

 (const int *).
Ok, grazie mille.

 certezze che avevo sul C?
Perche' mai? lo standard non (mi pare) dica nulla a questo proposito, ma
parlando in generale, non mi sembra che questo problema sia reale.
Magari esiste una CPU tanto arzigogolata, ma in questo caso il compilatore
dovrebbe essere del tutto particolare e probabilmente pieno di costrutti
assolutamente non standard.........
brix

 prevista dallo standard, giusto?
Sì. Sarebbe più ragionevole che desse un errore di compilazione, non un
semplice warning, giacché si tratta di una violazione delle regole del
linguaggio.
Max

 modo.
In questi casi accedi alla Flash che delle funzioni apposite, non 
direttamente dal linguaggio.
E.

 linguaggio.
Ma in realtà risulta spesso comodo poter violare la const-ness, 
soprattutto se si sta adattando del codice senza volerlo riscrivere 
tutto... (mi è capitato oggi stesso!)
Un altro caso tipico è:
void useHead(const char *s) {
	char *p;
	char c;
	for (p=(char *) s; *p && !isspace(*p);p++);
	c=*p;
	*p=0;
	use(s); /* in s c'e' ora solo la prima parola */
	*p=c;	/* rimetto a posto s com'era prima */
}
In questo caso si suppone che il const significhi solamente che la 
stringa non verrà modificata. Se la stringa fosse memorizzata in una 
memoria ReadOnly questo codice, però, non funzionerebbe.
Immagino che questa pratica potrebbe dare problemi anche in altre 
situazioni (multi-threading?).
E.

 linguaggio.
Infatti è la cosa che mi lascia perplesso e da lì mi veniva il dubbio che fosse 
un problema di architettura: su x86 funziona anche con il warning, su un'altra 
architettura magari non funzionava.
Se avesse dato un errore di compilazione avrei capito subito che era una 
questione di regole generali del linguaggio C violate.

 forzi l'operazione (cast) gli effetti sono indefiniti.
Però mi viene ora in mente una cosa. Supponiamo esista un processore che accede 
in modo diverso alla Flash e alla RAM, con due spazi di indirizzamento diversi.
Il compilatore potrebbe tradurre nel modo corretto gli accessi alla memoria 
supponendo che le variabili dichiarate const siano in Flash.
Penso che una architettura di questo tipo non sia C-compatibile. Infatti se la 
funzione somma accetta parametri (const int *), essa viene tradotta per accedere 
alla Flash.
Se passo alla funzione un (int *), cosa prevista dallo standard C, il risultato 
non è corretto perchè la funzione andrebbe a pescare dalla Flash e non dalla RAM.
In questi casi penso bisogna fare due funzioni, una somma() che accede in RAM 
dichiarando i puntatori (int *), ed una somma_cst() che accede in Flash 
dichiarando i puntatori (const int *).
Cosa ne pensate?

 memoria ReadOnly questo codice, però, non funzionerebbe.
Infatti: ti scoppierebbe tutto quanto in faccia con un seg-fault o
qualcosa di analogamente brutto.
% cat blowup.c
#include <ctype.h>
const char* use(const char *s) {return s;}
void useHead(const char *s) {
        char *p;
        char c;
        for (p=(char *) s; *p && !isspace(*p);p++);
        c=*p;
        *p=0;
        use(s); /* in s c'e' ora solo la prima parola */
        *p=c; /* rimetto a posto s com'era prima */
}
int main() {
  useHead("BLOW!");
  return 0;
}
% ./blowup    
[1]    4616 bus error  ./blowup
Tipicamente io starei *molto* attento a fare cose del genere. Nota che
tutti gli errori sono silenziosi:
% CFLAGS="-pedantic -Wall -W" make blowup
cc -pedantic -Wall -W    blowup.c   -o blowup
-- 
cc = callcc {|cc| cc.call cc}; cc.call cc

 Penso che una architettura di questo tipo non sia C-compatibile.
Se un compilatore lavora su un'architettura del genere, si premurerà di
generare codice corretto. Metterà le costanti da qualche parte. O magari
potrebbe esserci qualche mmu che si piglia la briga di fare il lavoro.
Ad ogni modo, fai conto che dipende dall'abi. Per dire su un processore
MIPS, se ricordo bene, è probabile che gli indirizzi delle costanti
siano tenuti tutti come offset da un registro (tenuto fissato con
l'inizio della sessione data). Per dire. In questi casi l'architettura
si premurerà di fare le cose giuste. 
Inoltre, per favore, di al tuo newsreader di wrappare correttamente
prima dell'80 carattere. Un buon numero è al 72-esimo. In modo che
quando ti si risponde (per un po' di risposte) si stia sotto
l'ottantesimo.
