C, matrici, allocazione dinamica ed eleganza del codice.

di il
50 risposte

50 Risposte - Pagina 2

  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    VC++ non compila, e il primo errore che da è su
    
    void doSomething(unsigned r, unsigned c, int(*matrix)[*]); //  error C2059: syntax error: ']'
    
    Mingw 7.2.0 da più o meno gli stessi errori mostrati da IfNotFalseTrue (però io ho tolto la compatibilità C11 C99 e aggiunto quella per il C++17)

    Per il resto:
    un puntatore a int può puntare a qualsiasi cosa riconducibile a un int;
    un puntatore a funzione può puntare a qualsiasi funzione compatibile col puntatore stesso;
    se esiste una logica, un puntatore a VLA dovrebbe puntare a qualcosa che sia un VLA, ne convieni?
    Devo invece dissentire se dici che il tipo di matrix è forzato ad essere int
    
    int (*allocate(unsigned r, unsigned c))[]
    {
        return malloc(r * c * sizeof(int));
    }
    int(*matrix)[c] = allocate(r, c);
    
    Scusa, ma allora quell'int a che serve? A farci stare qualsiasi cosa di 4 byte? Non pensi sia quanto meno ambiguo?
    l'unica cosa che posso invitarti a fare è una ricerca su cosa sono i pointer-to-array.
    Grazie del consiglio, ma non ne ho bisogno. Tra l'altro gli esempi che trovo nel draft dello standard C++ n4750 usano tutti costanti numeriche, e non variabili il cui valore può essere determinabile dall'utente (e non mi aspettavo niente di diverso).
    Tra l'altro ho anche una mezza idea del perché il C++ non ammetta i VLA e riguarda un possibile inlining delle funzioni (con problemi di stack overflow).
    Per concludere: non tutto il codice C può essere compilato da un compilatore C++ aderente allo standard.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    Nel frattempo ho eseguito anche io altre prove con diversi compilatori impostando diversi parametri per bloccare i VLA e ottengo alcuni dei vostri stessi errori, proprio perché la sintassi di questo tipo di puntatori si appoggia sulla sintassi dei VLA.
    Certo che secondo la logica un puntatore a VLA è fatto per puntare ad un VLA, ma come dicevo è possibile sfruttarlo per alcuni syntatic sugar come quello nel mio codice oppure questo:
    
    float (*first_row(unsigned n, unsigned m, float (*matrix)[n][m]))[] 
    {
        return *matrix; 
    }
    //...
    float matrix[3][4];
    float (*row)[m] = first_row(3, 4, &matrix); 
    /...
    
    In merito alle altre tue domande immagino che, non avendo alcun bisogno di riguardare questi aspetti, non ci sia bisogno che io risponda.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    IfNotFalseTrue ha scritto:


    cat prova.cpp
    #include <stdio.h>
    #include <stdlib.h>
    
    void getDimensions(unsigned *, unsigned *);
    int (*allocate(unsigned, unsigned))[];
    void doSomething(unsigned r, unsigned c, int(*matrix)[*]);
    
    
    int main()
    {
       unsigned r, c;
       
       getDimensions(&r, &c);
    
        int(*matrix)[c] = allocate(r, c);
       
       doSomething(r, c, matrix);
       
       free(matrix);
       
        return 0;
    }
    
    
    
    void getDimensions(unsigned *r, unsigned *c) {
       do {
        scanf("%u", r);
        scanf("%u", c);
       } while(*r == 0 || *c == 0); // unsigned -> negative values in input will be considered positive
       return;
    }
    
    
    int (*allocate(unsigned r, unsigned c))[]
    {
        return malloc(r * c * sizeof(int));
    }
    
    
    void doSomething(unsigned r, unsigned c, int(*matrix)[c]) {
       
       unsigned i = 0;
       unsigned j;
       
       for(; i < r; i++)
          for(j = 0; j < c; j++) 
             matrix[i][j] = j * i;
    
       for(i = 0; i < r; printf("%c", '\n'), i++)
          for(j = 0; j < c; j++)
             printf("%u ", matrix[i][j]);
       
       return;
    }
    
    Compilando con clang:
    
    clang -Weverything prova.cpp
    prova.cpp:6:54: warning: variable length arrays are a C99 feature
          [-Wvla-extension]
    void doSomething(unsigned r, unsigned c, int(*matrix)[*]);
                                                         ^
    prova.cpp:6:54: warning: variable length array used [-Wvla]
    prova.cpp:15:17: warning: variable length arrays are a C99 feature
          [-Wvla-extension]
        int(*matrix)[c] = allocate(r, c);
                    ^
    prova.cpp:15:17: warning: variable length array used [-Wvla]
    prova.cpp:15:10: error: cannot initialize a variable of type 'int (*)[c]' with
          an rvalue of type 'int (*)[]'
        int(*matrix)[c] = allocate(r, c);
             ^            ~~~~~~~~~~~~~~
    prova.cpp:37:12: error: cannot initialize return object of type 'int (*)[]' with
          an rvalue of type 'void *'
        return malloc(r * c * sizeof(int));
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~
    prova.cpp:41:54: warning: variable length arrays are a C99 feature
          [-Wvla-extension]
    void doSomething(unsigned r, unsigned c, int(*matrix)[c]) {
                                                         ^
    prova.cpp:41:54: warning: variable length array used [-Wvla]
    prova.cpp:48:27: warning: implicit conversion changes signedness: 'unsigned int'
          to 'int' [-Wsign-conversion]
             matrix[i][j] = j * i;
                          ~ ~~^~~
    prova.cpp:41:6: warning: no previous prototype for function 'doSomething'
          [-Wmissing-prototypes]
    void doSomething(unsigned r, unsigned c, int(*matrix)[c]) {
         ^
    8 warnings and 2 errors generated.
    
    Questo per quanto riguarda il C++, visto che se n'è parlato, mentre per il C:
    
    clang -Weverything prova.c -std=c90
    prova.c:6:54: warning: variable length arrays are a C99 feature
          [-Wvla-extension]
    void doSomething(unsigned r, unsigned c, int(*matrix)[*]);
                                                         ^
    prova.c:6:54: warning: variable length array used [-Wvla]
    prova.c:15:17: warning: variable length arrays are a C99 feature
          [-Wvla-extension]
        int(*matrix)[c] = allocate(r, c);
                    ^
    prova.c:15:17: warning: variable length array used [-Wvla]
    prova.c:15:10: warning: ISO C90 forbids mixing declarations and code
          [-Wdeclaration-after-statement]
        int(*matrix)[c] = allocate(r, c);
             ^
    prova.c:30:33: warning: // comments are not allowed in this language [-Wcomment]
       } while(*r == 0 || *c == 0); // unsigned -> negative values in input ...
                                    ^
    prova.c:41:54: warning: variable length arrays are a C99 feature
          [-Wvla-extension]
    void doSomething(unsigned r, unsigned c, int(*matrix)[c]) {
                                                         ^
    prova.c:41:54: warning: variable length array used [-Wvla]
    prova.c:48:27: warning: implicit conversion changes signedness: 'unsigned int'
          to 'int' [-Wsign-conversion]
             matrix[i][j] = j * i;
                          ~ ~~^~~
    prova.c:50:40: warning: possible misuse of comma operator here [-Wcomma]
       for(i = 0; i < r; printf("%c", '\n'), i++)
                                           ^
    prova.c:50:22: note: cast expression to void to silence warning
       for(i = 0; i < r; printf("%c", '\n'), i++)
                         ^~~~~~~~~~~~~~~~~~
                         (void)(           )
    10 warnings generated.
    
    
    clang -Weverything prova.c -std=c11
    prova.c:6:54: warning: variable length array used [-Wvla]
    void doSomething(unsigned r, unsigned c, int(*matrix)[*]);
                                                         ^
    prova.c:15:17: warning: variable length array used [-Wvla]
        int(*matrix)[c] = allocate(r, c);
                    ^
    prova.c:41:54: warning: variable length array used [-Wvla]
    void doSomething(unsigned r, unsigned c, int(*matrix)[c]) {
                                                         ^
    prova.c:48:27: warning: implicit conversion changes signedness: 'unsigned int'
          to 'int' [-Wsign-conversion]
             matrix[i][j] = j * i;
                          ~ ~~^~~
    4 warnings generated.
    
    Che venga compilato senza errori in C non mi stupisce e non l'ho mai messo in dubbio, il fatto è che non è detto che tutti i compilatori supportino tale idioma

    Dimenticavo che in C++ è necessario un cast quando la malloc restituisce il puntatore...quindi nel caso del mio codice un cast a (int(*)[c]) e questo dovrebbe eliminarti almeno gli errori riguardanti il void*.
    Per quanto riguarda il C alcuni warning sono scatenati dal ISO90 e non ho idea di quanto sia valido al momento...poi erroneamente ho creato una matrice di int anziché di unsigned e si lamenta giustamente anche se esegue la conversione (correggo subito il codice postato). Il resto dei warning è possibile tramutarli in errore usando -Werror=vla, proprio perché quel tipo di sintassi, anche se non genera alcun VLA, si appoggia alla sintassi di questi ultimi.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    shodan ha scritto:


    Tra l'altro ho anche una mezza idea del perché il C++ non ammetta i VLA e riguarda un possibile inlining delle funzioni (con problemi di stack overflow).
    Infatti, la VLA è allocata sulla stack e a causa della sua dimensione variabile può dar luogo a stack overflow in runtime. L'alternativa sarebbe limitare la dimensione dell'array. Ma a questo punto, non sarebbe più semplice e sicuro creare la stessa array con dimensione fissa al suo massimo previsto? Inoltre la VLA da sé non può fornire informazioni se l'allocazione fallisce.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    jfet ha scritto:


    Ispirato da un post di qualche ora fa dove era necessario allocare dinamicamente una matrice ho rispolverato qualche vecchio amico in unione con alcuni aspetti meno conosciuti del C.
    Mi chiedo se è possibile creare/se conoscete una soluzione più elegante della seguente, sia per allocare una matrice dinamicamente, sia per utilizzarla in una funzione:
    A parte le questioni sulla correttezza del codice, quale vantaggio si avrebbe dall'utilizzare un puntatore a VLA su una porzione di heap allocata con malloc, piuttosto che utilizzare un puntatore dichiarato molto più semplicemente?
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    Unqualunque ha scritto:


    jfet ha scritto:


    Ispirato da un post di qualche ora fa dove era necessario allocare dinamicamente una matrice ho rispolverato qualche vecchio amico in unione con alcuni aspetti meno conosciuti del C.
    Mi chiedo se è possibile creare/se conoscete una soluzione più elegante della seguente, sia per allocare una matrice dinamicamente, sia per utilizzarla in una funzione:
    A parte le questioni sulla correttezza del codice, quale vantaggio si avrebbe dall'utilizzare un puntatore a VLA su una porzione di heap allocata con malloc, piuttosto che utilizzare un puntatore dichiarato molto più semplicemente?
    È un syntatic sugar, permette di mantenere la sintassi utilizzata per accedere agli elementi di array, matrici, ecc statici allcando comunque un unico, contiguo blocco di memoria dinamico.
    Dal mio punto di vista aggiunge un tocco di eleganza al codice.
    Non creando nulla sullo stack se non il puntatore stesso, a differenza dei VLA, non peggiora il codice aggiungendo i pericoli da te menzionati nel precedente post.
    Le alternative sono:
    1 Un puntatore di n-esimo livello che sarà l'array n-dimensionale e un numero mano mano crescente di puntatori di livello via via inferiore per ogni dimensione dell'array n-dimensionale, in modo da mantenere la sintassi degli array n-dimensionali statici. Purtroppo il blocco di memoria totale allocato non sarà contiguo e si "spreca" memoria per ogni puntatore allocato per reggere il sistema. Inoltre, più grande è la dimensione n dell'array n-dimensionale più è facile commettere errori in fase di deallocazione.
    2 Utilizzare un puntatore semplice allocando un unico blocco di memoria spazioso per l'intero array n-dimensionale, così facendo però è necessario ogni volta inserire un calcolo usando la sintassi dell'array monodimensionale per accedere a un dato elemento scelto. Maggiore è la dimensione n dell'array n-dimensionale allocato più è facile "pasticciare". Ovviamente questa soluzione non permette l'uso della sintassi di matrici, tensori, ecc statici.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    Unqualunque ha scritto:


    L'alternativa sarebbe limitare la dimensione dell'array. Ma a questo punto, non sarebbe più semplice e sicuro creare la stessa array con dimensione fissa al suo massimo previsto?
    Ci sono casi in cui servono buffer piccoli non stimabili a priori e in cui l'allocazione dinamica ha costi alti.
    In casi simili è possibile usare la funzione posix alloca() che riserva memoria sullo stack, poi liberata quando lo scope esce.
    E' da usare con cautela in quanto è possibile domandare quantità enormi.
    In VC++ è fortemente consigliato usare la coppia malloca/freea che solo in modalità release, consente di allocare nello stack fino a 1024 bytes, mentre quantità superiori le alloca nell'heap (in pratica malloca/freea è poco più di un wrapper per alloca()).
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    Dal mio punto di vista aggiunge un tocco di eleganza al codice.
    Peccato che poi tutto vada in malora se il compilatore non supporta i VLA.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    shodan ha scritto:


    Dal mio punto di vista aggiunge un tocco di eleganza al codice.
    Peccato che poi tutto vada in malora se il compilatore non supporta i VLA.
    Alla fine penso che sia il problema minore. Non ne sono sicuro, ma penso di poter verificare la possibilità di usarli tramite il preprocessore e in caso ripiegare su altro codice.
    È possibile verificare la presenza o meno di alcuni flag inseriti nella chiamata al compilatore nella fase di preprocessamento? Mi devo documentare.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    Immagino che qualcosa di questo tipo possa andare:
    
    #ifdef __STDC_NO_VLA__
    /*Gestisci il caso in cui i VLA non siano disponibili
    #endif
    
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    IfNotFalseTrue ha scritto:


    Immagino che qualcosa di questo tipo possa andare:
    
    #ifdef __STDC_NO_VLA__
    /*Gestisci il caso in cui i VLA non siano disponibili
    #endif
    
    Perfetto, grazie x la dritta. Provo il prima possibile.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    IfNotFalseTrue ha scritto:


    Immagino che qualcosa di questo tipo possa andare:
    
    #ifdef __STDC_NO_VLA__
    /*Gestisci il caso in cui i VLA non siano disponibili
    #endif
    
    Ho fatto qualche prova: questo check è valido per C11 ma non per lo standard C90; se uso il flag -Werror=vla ottengo errori che si rifanno proprio a quest'ultimo. Vedo cosa altro trovo online da aggiungere al tuo suggerimento.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    jfet ha scritto:


    IfNotFalseTrue ha scritto:


    Immagino che qualcosa di questo tipo possa andare:
    
    #ifdef __STDC_NO_VLA__
    /*Gestisci il caso in cui i VLA non siano disponibili
    #endif
    
    Ho fatto qualche prova: questo check è valido per C11 ma non per lo standard C90; se uso il flag -Werror=vla ottengo errori che si rifanno proprio a quest'ultimo. Vedo cosa altro trovo online da aggiungere al tuo suggerimento.
    Ha senso che non funzioni col C90/ANSI C, perché all'epoca i VLA non esistevano proprio, penso che dovresti controllare che tu stia usando effettivamente tale standard con la macro __ANSI__
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    IfNotFalseTrue ha scritto:


    jfet ha scritto:


    IfNotFalseTrue ha scritto:


    Immagino che qualcosa di questo tipo possa andare:
    
    #ifdef __STDC_NO_VLA__
    /*Gestisci il caso in cui i VLA non siano disponibili
    #endif
    
    Ho fatto qualche prova: questo check è valido per C11 ma non per lo standard C90; se uso il flag -Werror=vla ottengo errori che si rifanno proprio a quest'ultimo. Vedo cosa altro trovo online da aggiungere al tuo suggerimento.
    Ha senso che non funzioni col C90/ANSI C, perché all'epoca i VLA non esistevano proprio, penso che dovresti controllare che tu stia usando effettivamente tale standard con la macro __ANSI__
    Si si comprendo che non abbia alcun senso per il C90, avevo pensato appunto all'uso di un check sulla versione del C ma ho provato anche a compilare in C11 aggiungendo il flag -Werror=vla e ottengo errore, errore che si rifà allo standard C90. Perciò il controllo sulla versione C penso che sia necessario ma non sufficiente se appunto compilo in C11 con il flag -Werror=vla. Probabilmente devo trovare il modo, se esiste, di effettuare un controllo degli argomenti presenti in *argv[] con il preprocessore.
  • Re: C, matrici, allocazione dinamica ed eleganza del codice.

    Non so che dirti, usare __STDC_VERSION__ probabilmente non è una buona idea, per l'ANSI C perché lo standard non dice nulla al riguardo quindi potrebbe esistere con un valore diverso da quello che ti aspetti.
    In pratica tu vorresti controllare gli argomenti che uno utilizza per compilare il suddetto codice? Puoi spiegare meglio cosa intendi?
Devi accedere o registrarti per scrivere nel forum
50 risposte