Newsgroup: it.comp.lang.c
Titolo: Const quando serve...

Ho un bel po' di strutture (S1), molte delle quali non vengono
modificate
durante l'esecuzione del programma e, quindi, posso dichiararle const
(cosa che mi fa MOLTO comodo su un'architettura limitata).
Alcune strutture, però, possono essere modificate a run-time e,
quindi,
non posso dichiararle const.
Queste struct sono indirizzate in un'altra struct (S2) insieme ad
altri campi.
Le S2 non sono const perchè gli altri campi possono essere modificati.
Il problema è: come dichiaro la struct S1 all'interno di S1?
Se facessi:
---
  struct S2 {
    const struct *S1;
    ...
  };
---
non riuscirei a modificare quelle poche strutture, cioè non riuscirei
a fare:
---
  S1 myS1;
  S2 myS2 = {&myS1, ...};
  ...
  myS2.S1->member_S1 = ciccio;
---
Se invece facessi:
---
  struct S2 {
    struct *S1;
    ...
  };
---
mi darebbe warning il compilatore assegnando al membro S1 di S2
un puntatore ad una struttura S1 di tipo const.
Che si può fare?

Ti vorrei aiutare, ma non mi e' chiaro il problema.
E' possibile portare un esempio concreto?

 un puntatore ad una struttura S1 di tipo const.
Perch� dovrebbe darti un warning? Come � dichiarata S1?
E.

 E' possibile portare un esempio concreto?
Certo. Uno dei tanti esempi che mi capita, molto semplificato giusto
per dare l'idea.
Voglio creare una struttura a menu gerarchica. Ogni menu è formato
da un elenco di voci che, a run-time, potrebbero essere nascoste.
---
struct menuitem_s {
    const char *name;
    int visible;
};
struct menu_s {
    const char *name;
    struct menuitem_s *itmlist;
    int num_item;
};
---
Normalmente gli array di menuitem_s sono tutti costanti, nel senso
che visible rimane sempre ad 1. In alcuni e pochi casi, però, ho la
necessità di mettere visible a zero a run-time.
Stando alle definizioni sopra, se definisco un array di item di tipo
const, non potrei assegnarlo al campo itmlist di un menu_s che non
è di tipo const (la conversione da const T * a T * non sembra essere,
giustamente, permessa).
Se, però, cambio la definizione di menu_s in:
---
struct menu_s {
    const char *name;
    const struct menuitem_s *itmlist;
    int num_item;
};
---
Non potrei andare, attraverso struct menu_s, a modificare il campo
visible
di un elemento dell'array itmlist.
Questa è la soluzione che adotto ora, con l'accortezza di andare a
modificare
il campo visible non attraverso menu_s, ma direttamente attraverso
l'array:
---
struct menuitem_s {
    const char *name;
    int visible;
};
struct menu_s {
    const char *name;
    const struct menuitem_s *itmlist;
    const int num_item;
};
/* Menu fisso */
const struct menuitem_s itmlist1[] = {
    { "Item11", 1 },
    { "Item12", 1 },
    { "Item13", 1 },
};
const struct menu_s menu1 = { "Menu1", itmlist1, 3 };
/* Menu con un item variabile a run-time */
struct menuitem_s itmlist2[] = {
    { "Item21", 1 },
    { "Item22", 1 },
    { "Item23", 1 },
};
const struct menu_s menu2 = { "Menu2", itmlist2, 3 };
void
print_menu (void)
{
    int i;
    printf ("MENU1\n");
    for (i = 0; i < menu1.num_item; i++)
        if (menu1.itmlist[i].visible)
            printf ("%s\n", menu1.itmlist[i].name);
    printf ("MENU2\n");
    for (i = 0; i < menu2.num_item; i++)
        if (menu2.itmlist[i].visible)
            printf ("%s\n", menu2.itmlist[i].name);
}
int
main (int argc, char *argv[])
{
    print_menu();
    putchar ('\n');
    itmlist2[1].visible = 0;
    print_menu();
}
---
Nel main(), non posso fare menu2.itmlist[1].visible = 0, ma devo per
forza fare
itmlist2[1].visible = 0.
Ripeto, l'obiettivo è quello di definire const la maggior parte delle
cose (che naturalmente
non vengono mai modificate) per evitare di riempire la RAM inutilmente
(il mio compilatore
mette le costanti in una zona di memoria read-only).

Due cose mi vengono in mente in questo momento.
1) Hai misurato la differenza in uso di memoria non-readonly tra la
soluzione con `const struct menuitem_s *itmlist;` e `struct menuitem_s
*itmlist;`?
Questo potrebbe darti una mano
2) In questi casi, ovvero dove hai bisogno di un piu' grande controllo
di dove le cose sono, ecco che devi secondo me usare il C come un
assembler glorificato.
Prima di tutto, potresti dichiarare il testo delle voci a menu come un
array statico (costante e preallocato in memoria readonly), cosi' che
va direttamente in memoria readonly e viene allineato in maniera il
piu' compatta possibile.
Poi puoi calcolarti l'offset delle stringhe a mano, e nelle strutture,
modificabili (non-const) puoi mettere un indice (const) che ti dice la
posizione della voce di menu, e l'altro (non-const) l'attributo se e'
visibile o meno.
Insomma, invece di usare struct, usi degli array tutti indipendenti, e
usi delle funzioni che esprimono la relazione tra gli array e li
manipolano attraverso un indice.
Dopotutto, mi sembra di capire che e' la quantita' di memoria read/
write il tuo problema, non le prestazioni.

 write il tuo problema, non le prestazioni.
Però mi sembra complicato e non so se risolve in mio problema.

 mette le costanti in una zona di memoria read-only).
Se tu dichiari una struttura con dei campi const e dei campi non const, 
sar� difficile che il compilatore la metta in ROM, visto che la 
struttura DEVE essere costituita da memoria contigua.
Dunque se vuoi ottenere lo spostamento dei dati in ROM devi fare come ti 
ha suggerito fnegroni, ovvero separare i dati const da quelli non const. 
Non capisco perch� dici che � una soluzione complicata.
Se invece non vuoi separare i dati, non tentare di mantenere const 
quello che const non �. Tanto andr� comunque tutto in RAM.
E.

La tua terza soluzione sembra quella giusta.
Questo e' cio' che mi e' venuto in mente riguardo l'uso di array:
----
#include <stdio.h>
const char open[] = "open";
const char close[] = "close";
const char cut[] = "cut";
const char *names[] = {open, close, cut};
int visible[] = {1, 1, 1};
const int file_menu[] = {0, 1};
const int edit_menu[] = {2} ;
int
main(void)
{
    visible[file_menu[0]] = 0;
    printf("file menu: %s %s\n", names[file_menu[0]], names[file_menu
[1]]);
    return 0;
}
----

 quello che const non è. Tanto andrà comunque tutto in RAM.
Il mio approccio iniziale era di definire le strutture che non
cambiano mai
const (tanto sono il 99%), e quelle due o tre strutture che cambiano
non-const.
Però a questo punto mi viene in mente un quesito più generale
sull'utilizzo di
const. Quando dichiaro una funzione che ha come parametro un
puntatore,
è meglio definirlo const (se ovviamente non vado a modificare il
valore da lui
indirizzato)?
Per esempio, guardiamo il prototipo della printf:
  int printf (const char *template, ...);
Perchè const? Funzionerebbe tranquillamente anche senza const. L'unico
motivo
che mi sono dato è che, se qualcuno dovesse utilizzare una stringa
template
costante, darebbe errore.
Allora come conclusione, è bene dichiarare const tutti i puntatori (le
zone puntate dai
puntatori) se effettivamente non vengono modificati.
Ma la stessa conclusione vale anche per le struct? Cioè, se in una
struct ho un
puntatore ad una zona che la _mia libreria_ non va a toccare in
scrittura, ma solo
in lettura, è meglio definirla const?
Tornando all'esempio dei menu, mi sembra più corretto definire le
strutture dei menu:
---
struct menuitem_s {
    const char *name;
    int visible;
};
struct menu_s {
    const char *name;
    const struct menuitem_s *itmlist;
    int num_item;
};
---
Così l'utilizzatore della libreria menu.c (sono sempre io, ma
chissà...) potrebbe anche
dichiarare un menuitem costante senza problemi.