Newsgroup: it.comp.lang.c
Titolo: Puntatore a locazione array

Ciao a tutti,
 
ho una function semplicissima che "funziona" ma non sono convinto che sia corretto implementarla così.
È corretto secondo voi?
 
Quello che deve fare la funzione è ritornare un puntatore all'elemento dell'array myarray[] indicato dall'indice index. Così che da altri moduli tipo main.c (che non vedono ovviamente index e myarray[]) sia possibile fare qualcosa tipo memcpy(mybuff, foo(), 5); tralasciando per ora i vari controlli sulla dimensione di mybuff, quanti caratteri copiare prima di arrivare alla fine di myarray[], ...
Il codice gira su un microcontrollore arm 32-bit
 
myfile1.c:
uint8_t index=10;
uint8_t myarray[128];
uint8_t *foo() {
 return &myarray[index];
}
 
Grazie

 È corretto secondo voi?
 
Si
 
 
-- 
I'm not anti-social;
I'm just not user friendly


E corretta come hai scritto
Ma mi chiedo a che serve una funzione siffatta,non e' neanche inline
 

Permette ai vari moduli (main.c, ...) di accedere a myarray[] senza necessariamente avere globali l'array e il suo indice

 Permette ai vari moduli (main.c, ...) di accedere a myarray[] senza necessariamente avere globali l'array e il suo indice
 
quindi myarray e index li dichiari static. E la funzione inline, cosi il compilatore ottimizza un po' meglio.
 
Ciao Jack

 }
 
Funzionare funziona: restituisci un puntatore al decimo elemento
dell'array.
 
Attento però che stai mandando in giro un puntatore ad una
variabile globale privata, e dai ai vari moduli che usano
myfile1.c la capacità di modificare il comportamento.

Sono già statici anche senza dichiararli esplicitamente statici, o sbaglio?

 
Si' sbagli, se non metti static, l'area di memoria della variabile 
potrebbe essere riassegnata, quindi il comportamento della tua funzione 
foo() sarebbe unpredictable, ovvero puo' andare di culo oppure no.

> statici, o sbaglio?
 
Se non vengono dichiarati statici, quei simboli saranno
indicizzati nel codice oggetto risultante dalla compilazione.
Ma senza un header (o una extern) che li dichiari, nessun altro
modulo sarà a conoscenza del nome di quelle variabili, dunque
qualsiasi referenza ad esse sarà segnalata come errore in fase di
compilazione, ma non di linking.

Un po' di spiegazione sul come funzionano static ed inline, tanto
per fugare dubbi e leggende.  Inizio con il far vedere una
'static' su variabile globale… poi più tardi (quando e se ho
tempo) faccio vedere un po' di altra roba.
 
Consideriamo un semplice file sorgente, test.c:
 
    $ more test.c 
    // Variabile globale
    int ciao = 3;
 
    int funzione(int x)
    {
        return x + ciao;
    }
 
    int funzione2(int x)
    {
        int barba = 3;
        int risultato = barba + x;
        barba = x;
        return risultato;
    }
 
Compiliamolo e vediamo quali simboli esporta il compilato
 
    $ gcc test.c -c
    $ objdump --syms test.o 
 
    test.o:     file format elf64-x86-64
 
    SYMBOL TABLE:
    0000000000000000 l    df *ABS*	0000000000000000 test.c
    0000000000000000 l    d  .text	0000000000000000 .text
    0000000000000000 l    d  .data	0000000000000000 .data
    0000000000000000 l    d  .bss	0000000000000000 .bss
    0000000000000000 l    d  .note.GNU-stack	0000000000000000 .note.GNU-stack
    0000000000000000 l    d  .eh_frame	0000000000000000 .eh_frame
    0000000000000000 l    d  .comment	0000000000000000 .comment
    0000000000000000 g     O .data	0000000000000004 ciao
    0000000000000000 g     F .text	000000000000000f funzione
    000000000000000f g     F .text	0000000000000024 funzione2
 
Concentriamoci su 'ciao': è dichiarato globale, ma non static,
è esportato nel codice oggetto, quindi posso farci del linking
anche se non è dichiarato nell'header.
 
Per esempio con questo main:
 
    $ more main.c 
    #include <stdio.h>
 
    extern int ciao;
 
    int main(int argc, char** argv)
    {
        fprintf(stderr, "%d\n", ciao);
    }
 
linking del main senza unirci test.o ottengo un errore in fase di
linking:
 
    $ gcc main.c
    /tmp/ccAOEBX9.o: In function `main':
    main.c:(.text+0x11): undefined reference to `ciao'
    collect2: error: ld returned 1 exit status
 
Se invece ci aggiungo il .o:
 
    $ gcc main.c test.o
    $ ./a.out
    3
 
Ora proviamo a mettere lo static in test.c:
 
    static int ciao = 3;
 
Ricompiliamo...
 
    $ gcc -c test.c 
    $ objdump --syms test.o 
 
    test.o:     file format elf64-x86-64
 
    SYMBOL TABLE:
    0000000000000000 l    df *ABS*	0000000000000000 test.c
    0000000000000000 l    d  .text	0000000000000000 .text
    0000000000000000 l    d  .data	0000000000000000 .data
    0000000000000000 l    d  .bss	0000000000000000 .bss
    0000000000000000 l     O .data	0000000000000004 ciao
    0000000000000000 l    d  .note.GNU-stack	0000000000000000 .note.GNU-stack
    0000000000000000 l    d  .eh_frame	0000000000000000 .eh_frame
    0000000000000000 l    d  .comment	0000000000000000 .comment
    0000000000000000 g     F .text	0000000000000014 funzione
    0000000000000014 g     F .text	0000000000000024 funzione2
 
Come si vede, ora `ciao` è un 'local' e non più un 'global'.
Ne risulta che la compilazione con il codice di main, che
referenzia un 'ciao' globale non funziona più:
 
    $ gcc main.c test.o
    /tmp/ccsp9pGr.o: In function `main':
    main.c:(.text+0x11): undefined reference to `ciao'
    collect2: error: ld returned 1 exit status
 
Questo è il significato di static per le variabili globali, che è
ben diverso dal significato di static per le variabili dichiarate
nello scope di una funzione.

 nello scope di una funzione.
 
+1
 

     0000000000000000 l     O .data	0000000000000004 ciao
 
Solo che questa volta il simbolo ha un'ulteriore `.1795`.
Non sono sicuro, ma credo serva ad impedire il linking da parte
di oggetti esterni (come ho fatto nel precedente episodio, in cui
ho dichiarato `extern int ciao`).
 
Ora togliamo lo static...
 
     int funzione(int x)
     {
         static int ciao = 3;
         return x + ciao;
     }
 
E la variabile semplicemente sparisce dalla lista dei simboli
esportati:
 
    test.o:     file format elf64-x86-64
 
    SYMBOL TABLE:
    0000000000000000 l    df *ABS*	0000000000000000 test.c
    0000000000000000 l    d  .text	0000000000000000 .text
    0000000000000000 l    d  .data	0000000000000000 .data
    0000000000000000 l    d  .bss	0000000000000000 .bss
    0000000000000000 l     O .data	0000000000000004 ciao.1795
    0000000000000000 l    d  .note.GNU-stack	0000000000000000 .note.GNU-stack
    0000000000000000 l    d  .eh_frame	0000000000000000 .eh_frame
    0000000000000000 l    d  .comment	0000000000000000 .comment
    0000000000000000 g     F .text	0000000000000014 funzione
 
Questo perché senza lo static la variabile viene invece allocata
sullo stack della funzione `funzione`.
 
E se proviamo a rendere static la funzione `funzione` otteniamo…
 
    static int funzione(int x)
    {
        int ciao = 3;
        return x + ciao;
    }
 
    $ objdump --syms test.o
 
    test.o:     file format elf64-x86-64
 
    SYMBOL TABLE:
    0000000000000000 l    df *ABS*	0000000000000000 test.c
    0000000000000000 l    d  .text	0000000000000000 .text
    0000000000000000 l    d  .data	0000000000000000 .data
    0000000000000000 l    d  .bss	0000000000000000 .bss
    0000000000000000 l     F .text	0000000000000018 funzione
    0000000000000000 l    d  .note.GNU-stack	0000000000000000 .note.GNU-stack
    0000000000000000 l    d  .eh_frame	0000000000000000 .eh_frame
    0000000000000000 l    d  .comment	0000000000000000 .comment
 
…otteniamo che `funzione` viene codificata come funzione (F)
locale (l), mentre prima era globale (g). E come conseguenza non
siamo più in grado di linkarla (provare per credere).
 
Riassumendo
===========
 
variabile globale: Malvagia
    Visibile ovunque in fase di linking. Se non dichiarata in un
    header può essere "invisibile" al compilatore, ma è una falsa
    invisibilità, in quanto non ne impedisce il linking.  È
    malvagia in quanto globale (e si, le globali sono da
    evitare).
 
variabile globale static: Malvagia
    Visibile solo all'interno del modulo, di scope comunque
    globale (quindi malvagia), e comunque esportabile manualmente
    mediante copia dell'indirizzo.
 
variabile locale static: Malvagia
    Visibile solo all'interno della funzione in cui è dichiarata,
    ma malvagia in quanto ogni chiamata alla funzione ha
    potenziali effetti collaterali sulle chiamate successive.
    Inoltre è esportabile mediante copia dell'indirizzo. In
    pratica è come una globale.
 
variabile locale: Buona
    La buona variabile. Nessun effetto collaterale, vive sullo
    stack. Può comunque essere esportata mediante copia
    dell'indirizzo, e la cosa tipicamente finisce in segfault o
    bachi orribili.
 
funzione globale: Buona
    È il modo normale per dichiarare una funzione. È sano, in
    quanto una funzione è per sua natura costante.
 
funzione static: Buona anzi ottima
    È il modo normale e sano per nascondere un simbolo
    all'interno del modulo (onde evitare linking accidentale). Il
    suo utilizzo migliora le performances del linking e riduce la
    probabilità di collisione tra simboli. Ovviamente non tutto
    va dichiarato static.
 
Nel prossimo episodio (se ho tempo): funzioni inline e static inline.


 
Occhio a fare esempi su una specifica implementazione, perché non è detto
che sia giusto in generale.
 
Dallo standard 9899:1999
- 6.2.2[3]: "If the declaration of a file scope identifier for an object or
a function contains the storage class specifier static, the identifier has
internal linkage."
- 6.2.4[3]: "An object whose identifier is declared with external or internal
linkage, or with the storage-class specifier static has static storage
duration. Its lifetime is the entire execution of the program and its
stored value is initialized only once, prior to program startup."
- 6.2.4[4]: "An object whose identifier is declared with no linkage and
without the storage-class specifier static has automatic storage duration."
 
Che direi che riassume meglio di tre post di objdump ;)
 
Ciao!

 
 
 
Peccato che index sia fuori dalla funzione,da come e' scritto
 
Magari una cosa di questo genere:
 
uint8_t myarray[128];
inline uint8_t *foo(size_t idx) {
   assert(idx < sizeof(myarray) / sizeof(myarray[0]));
   return &myarray[idx];
}
 
 
potrebbe ancora ancora andare,ma come l'hai fatta tu e' inutile
 
 
 
 

 linking.
Non sto parlando di variabili globali, quelle dell'op sono variabili 
locali che appartengono alla funzione foo(), per cui se foo() ritorna un 
puntatore non dichiarato static, non hai nessuna garanzia che l'area di 
memoria riferita dal puntatore resti immutata dopo la chiamata.

 locali che appartengono alla funzione foo(), per cui se foo() ritorna un 
 
no, guarda meglio sono dichiarate fuori dalla funzione foo().
 
Ciao Jack
 
 


Hai ragione, ho preso un abbaglio.