PROBLEMA Crivello di Eratostene

Pubblicità

Hero467

Utente Attivo
Messaggi
692
Reazioni
404
Punteggio
74
In seguito ad una discussione sempre qui sul forum su un generatore di numeri di Armstrong mi accingo ora a provare a realizzare un crivello di Eratostene, fatto in C.
Il problema di suddetto programma è che non manda in output nessun numero e io, essendo impossibilitato di fare debug, non riesco a capire cosa non va
C:
#include <stdio.h>

void genPrimes(int *nums) {
    int lenght = sizeof nums / sizeof nums[0];
  
    nums[0] = 0;
  
    for(int i = 0; i < lenght; i++) {     // lo so che qui potrei fare tutto con un unico for, ma era un test
        if(nums[i] % 2 == 0) {
            nums[i] = 0;
        }
    }
  
    for(int i = 0; i < lenght; i++) {
        if(nums[i] % 3 == 0) {
            nums[i] = 0;
        }
    }
  
    for(int i = 0; i < lenght; i++) {
        if(nums[i] % 5 == 0) {
            nums[i] = 0;
        }
    }
  
    for(int i = 0; i < lenght; i++) {
        if(nums[i] % 7 == 0) {
            nums[i] = 0;
        }
    }
  
    for(int i = 0; i < lenght; i++) {
        if(nums[i] != 0) {
            printf("%d\n", nums[i]);
        }
    }
}

int main() {
    int n;
  
    printf("Inserisci fino a che numero devo generare numeri primi: ");
    scanf("%d", &n);
  
    int nums[n];
  
    for(int i = 1; i < n; i++) {
        nums[i - 1] = i;
    }
  
    genPrimes(nums);
}
 
Un programmatore che non possa fare debug è un lavoratore che non possa fare il suo lavoro.
Non continuare a scrivere codice, focalizzati sul installare e fare funzionare un debugger decente, che può essere sia il caro vecchio gdb che quello integrato in un semplice IDE.
 
Io l’ho configurato gdb per il mio ide, ma comunque non me lo fa fare. Mi esegue il codice come se nulla fosse, fermandosi se c’è un errore e basta, senza rispettare i breakpoint
 
Io l’ho configurato gdb per il mio ide, ma comunque non me lo fa fare. Mi esegue il codice come se nulla fosse, fermandosi se c’è un errore e basta, senza rispettare i breakpoint

Utilizza Ghidra, che sotto usa comunque GDB. Ha una buona documentazione, ed è sia un disassembler che un debugger. Non è forse "user friendly" appena ti ci avvicini (è scritto in Java, e la grafica è realizzata in Java dopotutto...), ma è ottimo (open source, è di NSA).
Altrimenti puoi utilizzare Radare2.

Personalmente non mi piacciono molto gli strumenti a disposizione sotto Linux; sotto Windows è decisamente più comodo e c'è l'imbarazzo della scelta (Ollydbg, x64dbg, Ghidra, WinDBG, ImmunityDebugger...).

Venendo al tuo problema, questo è sbagliato: int lenght = sizeof nums / sizeof nums[0];. La cosa migliore che puoi fare e che spesso viene fatta è passare la lunghezza della memoria allocata; quindi:

C:
// Chiamata
genPrimes(nums,n);

// Firma
void genPrimes(int *nums, int lenght);

Faccio notare che "lunghezza" è "length" e non "lenght" come hai scritto tu nel codice. 😉
 
Io l’ho configurato gdb per il mio ide, ma comunque non me lo fa fare. Mi esegue il codice come se nulla fosse, fermandosi se c’è un errore e basta, senza rispettare i breakpoint
E qui torniamo ai parametri da passare al compilatore: quando decidi di fare debug, prima ricompili con l'opzione -g ?
(quando usi -g le opzioni di ottimizzazione non vanno inserite)

Magari lo hai gia' scritto altrove, e mi e' sfuggito... ma che ambiente (IDE) stai usando per la programmazione?
 
prima di tutto, c'e' un errore concettuale.
Dichiari un vettore nel main(), lo passi a una funzione che lo "usa e getta", e nel main() non viene mai utilizzato.
Oggetti (come variabili, vettori, classi) devono essere dichiarati e usati solo dove se ne abbia bisogno. Nel tuo caso, il vettore nums[] viene usato solo nella funzione genPrimes(), per cui deve essere presente solo li'. Potresti ribattere sia una piccola cosa, ma ti assicuro che occorre abituarcisi e rispettarle fin dall'inizio, ti salverà da molti mal di testa quando i programmi diventano più complicati.

seconda cosa, abituati prima di scrivere del codice a scrivere l'algoritmo. Lo puoi fare con strumenti appositi (tipo UML), semplicemente a parole (usando il linguaggio naturale), o usando un meta-linguaggio (ossia un linguaggio che sta a meta' tra il linguaggio naturale e quello di programmazione). Dipende dalla complessità del problema. Se trovi un algoritmo già presente in rete (e sono sicuro lo trovi per problemi ben noti come questo) puoi semplicemente dire "ho seguito l'algoritmo proposto qui'" (e includi il link)

Scrivere l'algoritmo e' la prima cosa che si fa. Se tu lo avessi fatto, avresti visto subito perche' quello che hai scritto non funziona (anche prima di iniziare a fare il debug). Un'altra cosa fondamentale dell'algoritmo e' che non dipende dal linguaggio di programmazione, per cui lo potrai poi implementare nel tuo linguaggio di scelta, evitando di "tradurre" codice da un linguaggio di programmazione ad un altro (come hai fatto con i numeri di Armstrong). Ci sono anche casi in cui dall'algoritmo si vede subito quale sia/siano i migliori linguaggi di programmazione per implementarlo, e anche quali siano quelli per cui sia perfino impossibile.

Comunque ammiro molto la tua volontà :) e non scherzo.
 
Probabilmente quello che sto per dire non andrà a mio favore, ma per il debugging non c'è per forza bisogna di un debugger, nel senso che con un po' di ingegno, e perdendo un po' di tempo in più, si può anche fare a meno di software appositi.
Per esempio io nel mio piccolo non li ho mai utilizzati e sono sempre andato di printf() "strategiche"! 😅
 
Probabilmente quello che sto per dire non andrà a mio favore, ma per il debugging non c'è per forza bisogna di un debugger, nel senso che con un po' di ingegno, e perdendo un po' di tempo in più, si può anche fare a meno di software appositi.
Per esempio io nel mio piccolo non li ho mai utilizzati e sono sempre andato di printf() "strategiche"! 😅
Anche nello sviluppo web si finisce per usare in molti casi l'equivalente di printf.
Comunque al giorno d'oggi con IDE come quelli di Jetbrains o VisualStudio il debug, con qualsiasi linguaggio, risulta estremamente semplice in quanto viene fatto direttamente dalla GUI dell'ide. Si perde meno tempo così rispetto allo scrivere un "print", una volta che è stato configurato correttamente un debugger (per altro mostrano il contenuto delle variabili e altro ancora, quindi hai uno sguardo più completo).

A lavoro dipende da molti fattori... XD

Ci sono casi dove l'utilizzo di software come un debugger o un address sanitizer ti salva 😁
A me poi è sempre piaciuto leggere direttamente l'assembly, quindi spesse volte invece del debugger dell'ide uso uno degli strumenti sopra citati. :P
 
Utilizza Ghidra, che sotto usa comunque GDB. Ha una buona documentazione, ed è sia un disassembler che un debugger. Non è forse "user friendly" appena ti ci avvicini (è scritto in Java, e la grafica è realizzata in Java dopotutto...), ma è ottimo (open source, è di NSA).
Altrimenti puoi utilizzare Radare2.
Me lo segno

Faccio notare che "lunghezza" è "length" e non "lenght" come hai scritto tu nel codice. 😉
Ooops, mispell colossale

E qui torniamo ai parametri da passare al compilatore: quando decidi di fare debug, prima ricompili con l'opzione -g ?
(quando usi -g le opzioni di ottimizzazione non vanno inserite)

Magari lo hai gia' scritto altrove, e mi e' sfuggito... ma che ambiente (IDE) stai usando per la programmazione?
Di solito faccio fare all'ide la compilazione, solo se serve la faccio io. Uso vs code comunque

prima di tutto, c'e' un errore concettuale.
Dichiari un vettore nel main(), lo passi a una funzione che lo "usa e getta", e nel main() non viene mai utilizzato.
Oggetti (come variabili, vettori, classi) devono essere dichiarati e usati solo dove se ne abbia bisogno. Nel tuo caso, il vettore nums[] viene usato solo nella funzione genPrimes(), per cui deve essere presente solo li'. Potresti ribattere sia una piccola cosa, ma ti assicuro che occorre abituarcisi e rispettarle fin dall'inizio, ti salverà da molti mal di testa quando i programmi diventano più complicati.
Allora da questo punto di vista la funzione genPrimes la posso eliminare, e trasportare tutto nel main. Nel main l'unica cosa che c'era da fare era creare l'array, da passare poi all'altra funzione

seconda cosa, abituati prima di scrivere del codice a scrivere l'algoritmo. Lo puoi fare con strumenti appositi (tipo UML), semplicemente a parole (usando il linguaggio naturale), o usando un meta-linguaggio (ossia un linguaggio che sta a meta' tra il linguaggio naturale e quello di programmazione). Dipende dalla complessità del problema. Se trovi un algoritmo già presente in rete (e sono sicuro lo trovi per problemi ben noti come questo) puoi semplicemente dire "ho seguito l'algoritmo proposto qui'" (e includi il link)
Sarà cosa buona farlo allora. Sono una persona molto mentale, abituato a pensare e fare subito, senza pianificare. Però capisco che in casi non è molto utile

Scrivere l'algoritmo e' la prima cosa che si fa. Se tu lo avessi fatto, avresti visto subito perche' quello che hai scritto non funziona (anche prima di iniziare a fare il debug). Un'altra cosa fondamentale dell'algoritmo e' che non dipende dal linguaggio di programmazione, per cui lo potrai poi implementare nel tuo linguaggio di scelta, evitando di "tradurre" codice da un linguaggio di programmazione ad un altro (come hai fatto con i numeri di Armstrong). Ci sono anche casi in cui dall'algoritmo si vede subito quale sia/siano i migliori linguaggi di programmazione per implementarlo, e anche quali siano quelli per cui sia perfino impossibile.
In realtà ho scelto di tradurlo più che sfida personale che altro: sono partito in Python che conosco già per capire come impostarlo, poi in C per vedere se riuscivo a farlo in un linguaggio che conoscevo poco e che sto imparando, poi in Java "perché no, tanto quelle 2-3 cose che dovrebbero servirmi qui dovrei saperle" (prima del C avevo provato Java, ma mi era risultato antipatico)

Comunque ammiro molto la tua volontà :) e non scherzo.
Grazie mille, mi fa molto piacere sentirlo da una persona che reputo molto esperta. Spero di trasformare questa passione in un lavoro prima o poi

Probabilmente quello che sto per dire non andrà a mio favore, ma per il debugging non c'è per forza bisogna di un debugger, nel senso che con un po' di ingegno, e perdendo un po' di tempo in più, si può anche fare a meno di software appositi.
Per esempio io nel mio piccolo non li ho mai utilizzati e sono sempre andato di printf() "strategiche"! 😅
Potrei farlo, ma siccome sono una persona molto pigra, e poiché ho visto che vsc offre questa possibilità integrata mi son detto "che senso ha se posso farlo premendo solo un pulsante"
 
Allora da questo punto di vista la funzione genPrimes la posso eliminare, e trasportare tutto nel main.
No, non farlo mai.
Il main() (in qualsiasi linguaggio di programmazione) non dovrebbe mai fare nulla se non chiamare qualche funzione.
Questo perche' il corpo di un programma e' formato da funzioni, una per ogni "operazione" che si deve fare. Idealmente il corpo di una funzione dovrebbe essere limitato a una dozzina di linee di codice, ma qui finiamo in un altro soggetto.
Ti faccio un altro esempio: supponi che domani devi scrivere un programma che deve, tra mille altre cose, trovare i numeri primi. Invece che riscrivere tutto il codice, potresti semplicemente "togliere" il codice dal main di questo programma e metterlo in una funzione. Quindi, tanto vale avere una funzione fin dal principio.
Dividere un programma in tante funzioni e' uno dei principi della programmazione, e ti dico subito non e' che sia cosa facile.

Una cosa che puoi fare (e qui ti punzecchio ;) ) e' darle un nome più opportuno, più esplicativo. Prima di tutto "Primes" non e' una parola (nemmeno in inglese, e' un aggettivo e quindi non ha plurale) e "gen" non ha un significato univoco . In inglese potrebbe essere "PrimeNumbersGenerator" (inutile lesinare la lunghezza a scapito della chiarita') in italiano "ListaNumeriPrimi". Altrimenti si e' costretti a mettere un commento per spiegare cosa faccia la funzione, e questo e' uno dei tanti casi in cui il commento va evitato (come la peste).

Sarà cosa buona farlo allora. Sono una persona molto mentale, abituato a pensare e fare subito, senza pianificare. Però capisco che in casi non è molto utile
In casi come la programmazione, non solo e' utile, ma indispensabile. Capisco benissimo che tu stia imparando e quindi lavori su programmi molto semplici, ma parte dello studiare significa anche come pianificare il lavoro. Dove lavoro io, non abbiamo permesso di scrivere nessuna linea di codice se non abbiamo prima discusso l'argomento, documentato, rivisitato con colleghi, e approvato dai manager.
 
No, non farlo mai.
Il main() (in qualsiasi linguaggio di programmazione) non dovrebbe mai fare nulla se non chiamare qualche funzione.
Questo perche' il corpo di un programma e' formato da funzioni, una per ogni "operazione" che si deve fare. Idealmente il corpo di una funzione dovrebbe essere limitato a una dozzina di linee di codice, ma qui finiamo in un altro soggetto.
Ti faccio un altro esempio: supponi che domani devi scrivere un programma che deve, tra mille altre cose, trovare i numeri primi. Invece che riscrivere tutto il codice, potresti semplicemente "togliere" il codice dal main di questo programma e metterlo in una funzione. Quindi, tanto vale avere una funzione fin dal principio.
Dividere un programma in tante funzioni e' uno dei principi della programmazione, e ti dico subito non e' che sia cosa facile.
Ah, allora correggo

Una cosa che puoi fare (e qui ti punzecchio ;) ) e' darle un nome più opportuno, più esplicativo. Prima di tutto "Primes" non e' una parola (nemmeno in inglese, e' un aggettivo e quindi non ha plurale) e "gen" non ha un significato univoco . In inglese potrebbe essere "PrimeNumbersGenerator" (inutile lesinare la lunghezza a scapito della chiarita') in italiano "ListaNumeriPrimi". Altrimenti si e' costretti a mettere un commento per spiegare cosa faccia la funzione, e questo e' uno dei tanti casi in cui il commento va evitato (come la peste).
Era fatto per capirmi io (come detto prima sono pigro, quindi di andare a scrivere quel nome ogni volta era un po' noioso, ed stavo programmando su una specie di notepad), avrei dovuto correggerlo prima di pubblicarlo
--- i due messaggi sono stati uniti ---
Ci rinuncio. A quanto pare il debugger di gcc non va d'accordo con le funzioni della libreria standard
 
Ultima modifica:
Era fatto per capirmi io (come detto prima sono pigro, quindi di andare a scrivere quel nome ogni volta era un po' noioso, ed stavo programmando su una specie di notepad), avrei dovuto correggerlo prima di pubblicarlo

Con il tempo e lo studio scoprirai un mondo dietro alla progettazione. Guarda in questa sezione, primo post: https://forum.tomshw.it/threads/tut...le-di-studio-manuali-tutorial-risorse.290243/
In particolare sotto lo spoiler "Refactoring e codice pulito".

Ci rinuncio. A quanto pare il debugger di gcc non va d'accordo con le funzioni della libreria standard

Che problema stai avendo di preciso?
 
Con il tempo e lo studio scoprirai un mondo dietro alla progettazione. Guarda in questa sezione, primo post: https://forum.tomshw.it/threads/tut...le-di-studio-manuali-tutorial-risorse.290243/
In particolare sotto lo spoiler "Refactoring e codice pulito".
Mi era sfuggita quella parte

Che problema stai avendo di preciso?
Eseguo le istruzioni linea per linea, per capire cosa c'è nelle variabili e nel caso correggere, ma una volta che il puntatore arriva ad un'istruzione con una funzione della libreria stdio.h (come printf o scanf, che per il momento sono le uniche che uso) si rifiuta di andare avanti, lampeggiando sempre sulla stessa linea quando premo "prossima istruzione"
 
Non so per quale arcano motivo, ma ora funziona (non il debugger, il codice che ho scritto). Per il debug ancora non capisco il problema, forse esegue il codice “live“ interpretandolo al momento e non riesce a interpretare le funzioni esterne(?)
 
Eseguo le istruzioni linea per linea, per capire cosa c'è nelle variabili e nel caso correggere, ma una volta che il puntatore arriva ad un'istruzione con una funzione della libreria stdio.h (come printf o scanf, che per il momento sono le uniche che uso) si rifiuta di andare avanti, lampeggiando sempre sulla stessa linea quando premo "prossima istruzione"
Nel tuo caso il debugger non sa dove trovare il codice delle librerie, neanche quello assembler. Dovresti avere due comandi nel tuo debugger: uno che “entra” nella funzione (“step”), un altro per eseguire la funzione senza farne il debugger (nel vecchio gdb il comando era “next” o più semplicemente “n”)

PS studio.h non è una libreria, i file .h sono file che “in genere” contengono le definizioni delle funzioni incluse in una libreria, a altre utili definizioni.
 
Pubblicità
Pubblicità
Indietro
Top