RISOLTO Strano comportamento semplice codice C++

Stato
Discussione chiusa ad ulteriori risposte.

BrutPitt

Utente Attivo
1,166
1,262
Permettimi di chiarire meglio questo:
Il tuo problema è che la tua struttura ha una lunghezza variabile, che è il numero degli elementi del vettore

La sua struttura ha una dimensione fissa, cosi' come la sua "matrice di strutture", in quanto gli elementi (che inserisce) vengono allocati "altrove".
La classe vector contiene solo un puntatore agli elementi, quindi il sizeof di "A" o di "m" e' sempre costante, indipendentemente dagli elementi che inserisce.

Oltretutto non accede agli elementi (nel ciclo for) ma solo ad una variabile membro (attraverso size()), quindi (in questo caso) anche l'ottimizzazione dell'allocazione non dovrebbe importare (forse manda solo "in confusione" l'ottimizzatore, leggasi dopo): in pratica sarebbe come utilizzare una struttura del genere:
C++:
struct A
{
    bool flag;
    int size;
};

Che poi il codice sia poco leggibile e che sia da riscrivere in ottica produttiva, sono assolutamente d'accordo. 😉

@M1n021 oltretutto i compilatori moderni riescono ad ottimizzare meglio il codice attraverso gli indici di array/matrici/etc: hanno piu' difficolta' ad "interpretare" l'aritmetica dei puntatori, o meglio, non riescono a "manipolarlo" in maniera arbitrale piu' di tanto.
(ricordo che non era cosi'... quando le CPU erano molto piu' "semplici")
Quindi alla fine è corretto dire si tratta di un "bug", o comunque di un compilatore un po' troppo "intraprendente"? 😅
Secondo me e' un bug... 😉

Non so se hai visto il semplice codice che ho postato nel precedente messaggio:
Non l'avevo visto... o meglio rispondevo a quello che avevo letto ieri sera (ma vista l'ora tarda, temevo fossi io quello in errore)
Quindi questo manda all'aria (in un certo senso) anche la mia ipotesi che fosse l'allocazione (e relativa ottimizzazione) degli elementi della classe vector che potessero aver confuso l'ottimizzatore.

Senza la keyword "volatile" o con la keyword "volatile" applicata al''indice i del ciclo for, il programma stampa fino a 2, mentre se applico la keyword "volatile" alla matrice m stampa giustamente fino a 8.
Ed invece, come scrivevo prima, l'ottimizzazione di m, non dovrebbe importare nulla: il ciclo for dovrebbe terminare comunque, anche senza conoscere cosa fa m... essendoci l'operatore logico &&.
Ed anche chi scrive il codice, non dovrebbe, in un ciclo del genere, preoccuparsi di dichiarare "volatile" la variabile "i" (di controllo ciclo) e nemmeno la matrice) .

Aggiungo...
pero' in questo caso, se utilizzi gli indici, funziona correttamente (quello di prima, anche con gli indici, non funzionava comunque)

C++:
int main()
{
    int m[3][3] = {{1, 2, 3},
                   {4, 5, 6},
                   {7, 8, 9}};

    for(unsigned int i = 0; i < 3 * 3 && m[i/3][i%3]; ++i)
    {
        cout << i << endl;
    }
}

// e funziona anche se utilizzassi questo for
    for(unsigned int i = 0; i < 3 * 3 && (*m)[i%3]; ++i)
 
Ultima modifica:

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,220
1,852
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Ah, di nulla. Mi piace portare asm nella vita delle persone @M1n021 . 🤣

Concordo sul discorso delle ottimizzazioni della struttura e sulla leggibilita' de codice... ma con che criterio il compilatore decide di rimuovere il check sulla i?

Concordo con quanto dici Secondo me si tratta di un bug come già avete evidenziato. C'è qualche comportamento anomalo nella generazione del codice.

Prova ne è che questo ad esempio funziona:

C++:
#include <iostream>
using namespace std;
int main()
{
    int m[3][3] = {{1, 2, 3},
                   {4, 5, 6},
                   {7, 8, 9}};

    for(unsigned int i = 0; i < 3 * 3 && *(*m + i); ++i)
    {
        cout << i << endl;
    }
}

Schermata del 2022-11-21 16-22-25.png

sostituendolo appunto con (*m)[i] invece non produce un codice corretto. Non so, sarà qualche assunzione del compilatore GCC/G++... impazzisce quando vede un puntatore e del sintactic sugar (parentesi quadre) "assieme". 😁

Faccio solo notare (ovviamente a @M1n021 ) che è sbagliata comunque la logica del check in sè: facendo così, se nella matrice al posto del numero 6, inserissi ad esempio 0, il ciclo si interromperebbe:

Schermata del 2022-11-21 16-27-15.png

@bigendian ti riferisci allo strumento in sè? Se a questo si, è davvero un ottimo strumento, anche utile. Per provare qualcosa se non si ha molto tempo, o se si è curiosi di vedere cosa genera (e ormai anche l'output dell'esecuzione) torna utile; ed è comodo anche se per vedere il codice macchina delle istruzioni (sotto a uno dei menu c'è la possibilità di mettere una spunta per attivarlo).



@M1n021 ti ho modificato il post per sistemare la formattazione: ti devo chiedere di non utilizzare in particolare il nero, ma anche altri colori sono un pò al limite (il blu), questo perchè chi non utilizza la skin di default potrebbe non vedere o aver enormi difficoltà nella lettura di quanto scrivi (scorri in basso nella pagina, troverai una barra blu con la skin che stai usando, se noti alcune sono scure).
 
  • Mi piace
Reazioni: M1n021 e BrutPitt

M1n021

Nuovo Utente
143
68
Faccio solo notare (ovviamente a @M1n021 ) che è sbagliata comunque la logica del check in sè: facendo così, se nella matrice al posto del numero 6, inserissi ad esempio 0, il ciclo si interromperebbe:
In che senso? Premesso che questi frammenti di codice che ho riportato servono solo a riprodurre il "bug" che ho riscontrato, mi sembra normale che se sostituisco il 6 con lo 0 il programma dovrebbe stampare fino a 4.

@M1n021 ti ho modificato il post per sistemare la formattazione: ti devo chiedere di non utilizzare in particolare il nero, ma anche altri colori sono un pò al limite (il blu), questo perchè chi non utilizza la skin di default potrebbe non vedere o aver enormi difficoltà nella lettura di quanto scrivi (scorri in basso nella pagina, troverai una barra blu con la skin che stai usando, se noti alcune sono scure).
Afferrato, non ci avevo pensato! Comunque la scelta del nero era per togliere il blu. 😅


In ogni caso, giusto per tirare le somme, se sostituisco il compilatore GCC/G++ con un altro, risolvo o no? E nel caso quale mi consigliate?
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,220
1,852
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
In che senso? Premesso che questi frammenti di codice che ho riportato servono solo a riprodurre il "bug" che ho riscontrato, mi sembra normale che se sostituisco il 6 con lo 0 il programma dovrebbe stampare fino a 4.
Si, è così, ho precisato la cosa. Ma è un controllo inutile quello, è sufficiente la condizione sulla variabile "i": la condizione sulla "i < 9" è quella che ti fa ciclare sui valori della matrice; se rimuovi questa e lasci, per assurdo, quella sul valore della matrice, potresti anche potenzialmente andare avanti all'infinito con quel ciclo (in quanto non è detto che al termine della matrice ci sia uno 0).

Ma è una cosa buona, abbiamo scoperto quello che è probabilmente un bug in uno dei compilatori maggiormente utilizzati.

Per altro ho controllato tramite Godbolt, e anche sulle vecchie versioni si verifica lo stesso tipo di problema.

In ogni caso, giusto per tirare le somme, se sostituisco il compilatore GCC/G++ con un altro, risolvo o no? E nel caso quale mi consigliate?
Se sei su Windows puoi usare sia Clang tramite MSYS ,sia MSVC, che lo trovo un altro bel compilatore (di solito uso questo, se scarichi Visual Studio è già incluso, e puoi anche usarlo da command line).

Avevo provato a compilare online e mi sembrava funzionasse; Brutpitt ha poi confermato la cosa in un precedente post, quindi ti direi di si, prova.
Se usi già MSYS per utilizzare GCC/G++ ti ci vorrà poco per installare clang, mi sembra sia sufficiente un pacman -S mingw-w64-x86_64-clang.
 
  • Mi piace
Reazioni: BrutPitt

M1n021

Nuovo Utente
143
68
Cmq, essendo un probabile bug, io aprirei un thread


Ci sara' una risposa degli sviluppatori, cosi' capiamo.
Lascio a voi questo onore! 😅
Scherzi a parte, magari qualcuno è già registrato al sito e sicuramente ha più esperienza di me nel riportare il tutto.


Se sei su Windows puoi usare sia Clang tramite MSYS ,sia MSVC, che lo trovo un altro bel compilatore (di solito uso questo, se scarichi Visual Studio è già incluso, e puoi anche usarlo da command line).
Premesso che sono del tutto ignorante in quest'ambito, prima di scaricare nuovi software volevo chiedere, visto che al momento uso codeblocks e mi trovo bene, se fosse possibile installare sul suddetto IDE uno dei compilatori che mi avete suggerito?
 
  • Mi piace
Reazioni: BrutPitt

BrutPitt

Utente Attivo
1,166
1,262
Premesso che sono del tutto ignorante in quest'ambito, prima di scaricare nuovi software volevo chiedere, visto che al momento uso codeblocks e mi trovo bene, se fosse possibile installare sul suddetto IDE uno dei compilatori che mi avete suggerito?

Come ha detto @DispatchCode:
Se usi già MSYS per utilizzare GCC/G++ ti ci vorrà poco per installare clang, mi sembra sia sufficiente un pacman -S mingw-w64-x86_64-clang.

Una volta installato basta che cambi i riferimenti del "toolchain" in CodeBlock (Settings->Compiler) e dal combo Selected compiler selezioni: LLVM Clang compiler

Dovrebbe essere sufficiente questo'
 
  • Mi piace
Reazioni: M1n021

bigendian

Utente Attivo
732
418
OS
Linux
questo funziona

Codice:
int main()
{
    vector<A> m {{false, {2, 8, 5}}, {true,  {}},        {false, {1}},
                 {true,  {}       }, {false, {2, 8, 5}}, {false, {11, 18}},
                 {true,  {}       }, {true,  {}},        {false, {1, 5, 2, 8, 5}}};

    for (unsigned int i = 0; i < 3 * 3 && (m[i].flag || m[i].v.size()); ++i)
    {
        cout << i << endl;
    }

    return 1;

}

Evidentemente, usando un puntatore, il compilatore ragiona diversamente.
 

pabloski

Utente Èlite
2,868
916
Non penso lo riconosceranno come errore, magari terranno conto del caso limite ed implementeranno un fix...il motivo è che questo funge

C++:
unsigned int cnt = 0;   

for (unsigned int i = 0; i < 3; i++) {       
    for (unsigned int j = 0; j < 3; j++) {           
        if (m[i][j].flag || m[i][j].v.size()) cout << cnt++ << endl;            
    }   
}

cioè dal punto di vista semantico è il codice originale che cerca di infilarsi "sotto il cofano" della gestione degli array, cosa che idiomaticamente non dovrebbe fare

voglio dire, se hanno implementato l'allocazione degli elementi degli array multidimensionali in "modo strano" nel caso -O3, ci saranno ragioni prestazionali, non è una svista
 

bigendian

Utente Attivo
732
418
OS
Linux
C'e' gia la sentenza. Non e' un bug ma undefined behavior. A quel punto il compilatore ottimizza come puo/vuole. Come detto piu volte appunto, non si accede all'array bidimensionale in quel modo.

Compilando con
g++ -o test -O3 -g main.cc -fsanitize=undefined

problema esce in runtime.
 

pabloski

Utente Èlite
2,868
916
C'e' gia la sentenza. Non e' un bug ma undefined behavior. A quel punto il compilatore ottimizza come puo/vuole. Come detto piu volte appunto, non si accede all'array bidimensionale in quel modo.

Compilando con
g++ -o test -O3 -g main.cc -fsanitize=undefined

problema esce in runtime.

credo che nei forum, sui thread dove Rust viene attaccato a tamburo battente, posterò questo esempio come una delle ragioni per la creazione di nuovi linguaggi, con modelli di programmazione ben definiti 😁

cioè capisco che esistona comportamenti indefiniti dovuti a grosse lacune negli standard di C e C++, ma pensare che il compilatore ne va a caccia nel codice ( e considerando che non stiamo parlando di un essere intelligente, un mucchio di cose possono andare storte ) e addirittura

"Sorry (*m)[3] . So if the compiler thinks it is undefined behavior it can optimize it any way it wants. In this case it removes the i < 9 ."

praticamente fa il giocoliere

e poi c'è chi lamenta il fatto che Torvalds non vuol sentir parlare di compilare il kernel con -O3
 
Stato
Discussione chiusa ad ulteriori risposte.

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!

Discussioni Simili