[MISTERO] C corruzione della memoria

Area di discussione libera.

Moderatore: Staff

Regole del forum
1) Rispettare le idee altrui.
2) Evitare le offese dirette.
3) Leggere attentamente le risposte ricevute
4) Scrivere i messaggi con il colore di default, evitare altri colori.
5) Scrivere in Italiano o in Inglese, se possibile grammaticalmente corretto, evitate stili di scrittura poco chiari, quindi nessuna abbreviazione tipo telegramma o scrittura stile SMS o CHAT.
6) Appena registrati è consigliato presentarsi nel forum dedicato.

La non osservanza delle regole porta a provvedimenti di vari tipo da parte dello staff, in particolare la non osservanza della regola 5 porta alla cancellazione del post e alla segnalazione dell'utente. In caso di recidività l'utente rischia il ban temporaneo.
Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun set 03, 2007 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Messaggioda Mario Vanoni » mar dic 11, 2007 16:29

puzuma ha scritto:
ildiama ha scritto:1) se uno programma per piacere, forse troverà piacere anche a debuggare errori non voluti che ti fanno "divertire", non "perdere" un pomeriggio. Si impara e ci si diverte. Se uno si diverte col tennis, vada pure.


personalmente non trovo divertente ne un gran chè creativo debuggare il fatto che in una riga su 100.000 ho scritto 8 invece che 7, però questo è soggettivo.

ildiama ha scritto:2) se uno programma in C per lavoro, sa che questi controlli sono a suo carico e che se non li fa rischia. Io direi.. il lavoro :badgrin:


ok, allora io non so programmare, che controllo dovrei mettere se in un'indice per sbaglio ho scritto un numero invece che un altro? sono daccordo controllare l'input dell'utente o di un programmatore che usa una mia funzione ma controllare anche l'input dello stesso programmatore che scrive il codice mi sembra difficile ;)


Esatto, non sai programmare in C, "you must know what you do",
ed ignoro/dubito se con una Ferrari da Cuasso al Monte arrivi alla Valganna!

Mario Vanoni

Avatar utente
puzuma
Linux 2.x
Linux 2.x
Messaggi: 482
Iscritto il: mar lug 04, 2006 17:14
Nome Cognome: Stefano Salvador
Slackware: current
Kernel: 2.6.32.2
Desktop: KDE 4.4.0
Località: Udine
Contatta:

Messaggioda puzuma » mar dic 11, 2007 16:37

Mario Vanoni ha scritto:Esatto, non sai programmare in C, "you must know what you do",
ed ignoro/dubito se con una Ferrari da Cuasso al Monte arrivi alla Valganna!


Senti, cerchiamo di rispettare un po' di netiquette, io non ho affermato niente di personale, non vedo perchè dovresti farlo tu. Semplicemente si ponevano a confronto diversi punti di visti valutando le diverse esperienze, rispondere con arroganza e sufficienza non serve a niente.

Avatar utente
Paoletta
Staff
Staff
Messaggi: 3974
Iscritto il: lun apr 25, 2005 0:00
Slackware: 14.2 - 64 bit
Desktop: fluxbox
Località: Varese

Messaggioda Paoletta » mar dic 11, 2007 17:10

ragazzi calma...

Simone_R
Linux 2.x
Linux 2.x
Messaggi: 218
Iscritto il: mar apr 12, 2005 0:00
Contatta:

Messaggioda Simone_R » mar dic 11, 2007 17:29

ildiama ha scritto:Forse neanche io mi sono spiegato tanto bene.. io prima cercavo di spiegare perché gcc non alza quello warning.
Però sta cosa di tennis e ferrari proprio non la capisco.

    1) se uno programma per piacere, forse troverà piacere anche a debuggare errori non voluti che ti fanno "divertire", non "perdere" un pomeriggio. Si impara e ci si diverte. Se uno si diverte col tennis, vada pure.
    2) se uno programma in C per lavoro, sa che questi controlli sono a suo carico e che se non li fa rischia. Io direi.. il lavoro :badgrin:

tutto qui.

PS: adesso mi preparo per il calcetto.. :lol:


icc di default è più pedante di gcc.

Quindi se lavori con gcc aggiungi l'opzione -Wall ti ritorna tutti i warning possibili diventando pedantissimo e se vuoi usa anche -pedantic ti obbliga ad usare un c che rispetti rigorosamente gli standard ISO.

Se usi delle costanti definiscile sempre come macro e usa sempre la macro al posto del numero ti risparmierai parecchi errori e il codice è più flessibile da manipolare.

esempio:

Codice: Seleziona tutto

#define MY_VECTOR_SIZE 100
 main() {
   int a[MY_VECTOR_SIZE] ;

   int i = xyz ;

   if ( i > MY_VECTOR_SIZE - 1 ) {
      // errore .....
   } else {
      a[i] = 23 ;
   }
}

Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun set 03, 2007 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Messaggioda Mario Vanoni » mar dic 11, 2007 17:32

puzuma ha scritto:
Mario Vanoni ha scritto:Esatto, non sai programmare in C, "you must know what you do",
ed ignoro/dubito se con una Ferrari da Cuasso al Monte arrivi alla Valganna!


Senti, cerchiamo di rispettare un po' di netiquette, io non ho affermato niente di personale, non vedo perchè dovresti farlo tu. Semplicemente si ponevano a confronto diversi punti di visti valutando le diverse esperienze, rispondere con arroganza e sufficienza non serve a niente.


Netiquette?!? Accetta la realta` cruda:
- C e` un linguaggio 3rd generation, vicino ad assembler 2nd generation
- vedi il Linux kernel con parti compilate con as(1)
- a questi livelli e` il programmatore l'unico responsabile, non l'arnese che usa
- linguaggi della 4th (pascal/ada) e 5th generation (SQL) hanno quello che ti aspetti/speri/ti auguri che abbiano
- se Intel con icc riesce a fare salti di generazione, tanto di capello,
ma per ora non riesce a compilare un kernel Linux funzionante

Mario Vanoni

Simone_R
Linux 2.x
Linux 2.x
Messaggi: 218
Iscritto il: mar apr 12, 2005 0:00
Contatta:

Messaggioda Simone_R » mar dic 11, 2007 17:40

Mario Vanoni ha scritto:- se Intel con icc riesce a fare salti di generazione, tanto di capello,
ma per ora non riesce a compilare un kernel Linux funzionante


Il c di icc è lo stesso di gcc. Le grosse differenze stanno nel generatore di codice macchina.
Il kenel non lo compila perché è scritto con un diletto del c riconosciuto solo da gcc.

[Comunque da quello che ho trovato su internet è possibile compilare il kernel anche con icc]

Infine il dialetto gcc introduce al c molte estensioni di alto livello e il c come linguaggio non ha nulla da spartire con l'assembler.

Avatar utente
lamarozzo
Linux 3.x
Linux 3.x
Messaggi: 732
Iscritto il: gio lug 14, 2005 0:00
Desktop: xfce
Distribuzione: archlinux
Località: Roma

Messaggioda lamarozzo » mar dic 11, 2007 19:46

*puzuma: siamo sulla stessa lunghezza d'onda, quoto ogni virgola.

*Simone_R: il codice che hai postato sicuramente risolve il problema, però mettere un controllo if ogni volta che accedo all'array all'interno di un loop è micidiale per le prestazioni perchè uccide la pipeline e costringe il processore a fare branch prediction. Per quanto riguarda le flag, questa discussione origina proprio dal fatto che gcc non segnala nulla neanche con -Wall -pedantic.

*Mario_Vanoni: scusami ma a volte sembra che il tuo atteggiamento sia ostruzionistico, del tipo: "è così e se non vi va bene non sapete programmare".
Un conto è il C, codificato nei suoi standard, e un altro conto sono i compilatori. Se il compilatore mette un warning non sta violando nessuno standard, sta solo facendo un favore al programmatore, sempre che tu voglia chiamarlo ancora così perchè ha fatto un errore di distrazione.
Per quanto riguarda il tanto odiato compilatore intel che non compila il kernel, Simone_R ti ha già detto perchè, ma googlando ho trovato qualche precisazione in più:

.. the "unraliable guide to kernel hacking" sais that there are the following gnu extentions in the linux kernel:

Inline functions

Statement expressions (ie. the ({ and }) constructs).

Declaring attributes of a function / variable / type (__attribute__)

Labeled elements

typeof

Zero length arrays

Macro varargs

Arithmetic on void pointers

Non-Constant initializers

Assembler Instructions (not outside arch/ and include/asm/)

Function names as strings (__FUNCTION__)

__builtin_constant_p()



Adesso secondo il tuo ragionamento bisognerebbe dire che gli sviluppatori del kernel non sanno programmare perchè non rispettano gli standard! :roll:

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom ott 14, 2007 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Messaggioda nuitari » mer dic 12, 2007 5:49

Mah.. tornando in-topic, ho sempre trovato utile la possibilità del C di eccedere i limiti degli array.

In fondo un array in C non è altro che un puntatore ad un area di memoria inizializzata alla dimensione di N_ELEMENTI * sizeof(TIPO), così come le parentesi quandre non servono altro che a muovere l'offset del puntatore di N posizioni dall'inizio dell'area di memoria. Infatti è possibile usarle con qualsiasi puntatore ad un area di memoria, non necessariamente con gli array.
Ad esempio:

Codice: Seleziona tutto

#include "stdio.h"
#include "stdlib.h"

int main (void)
{
        int *a = (int *)malloc(sizeof(int));
        *a = 1;
        printf("%d\n", *a);

        short *b = (short *)a;
        /* usa il puntatore come fosse un array spostando l'offset di uno short, quindi di 2 byte */
        b[1] = 1;
        printf("%d\n", *a);

        free(a);
        return 0;
}


Per cui, personalmente trovo comoda l'assenza di controlli di quel tipo. Per utilizzi ad alto livello di array e similari, imho è meglio usare la STL, ed usare gli array C solo per cose a basso livello.

Avatar utente
lamarozzo
Linux 3.x
Linux 3.x
Messaggi: 732
Iscritto il: gio lug 14, 2005 0:00
Desktop: xfce
Distribuzione: archlinux
Località: Roma

Messaggioda lamarozzo » mer dic 12, 2007 22:46

nuitari ha scritto:Mah.. tornando in-topic, ho sempre trovato utile la possibilità del C di eccedere i limiti degli array.

In fondo un array in C non è altro che un puntatore ad un area di memoria inizializzata alla dimensione di N_ELEMENTI * sizeof(TIPO), così come le parentesi quandre non servono altro che a muovere l'offset del puntatore di N posizioni dall'inizio dell'area di memoria. Infatti è possibile usarle con qualsiasi puntatore ad un area di memoria, non necessariamente con gli array.
Ad esempio:

Codice: Seleziona tutto

#include "stdio.h"
#include "stdlib.h"

int main (void)
{
        int *a = (int *)malloc(sizeof(int));
        *a = 1;
        printf("%d\n", *a);

        short *b = (short *)a;
        /* usa il puntatore come fosse un array spostando l'offset di uno short, quindi di 2 byte */
        b[1] = 1;
        printf("%d\n", *a);

        free(a);
        return 0;
}


Per cui, personalmente trovo comoda l'assenza di controlli di quel tipo. Per utilizzi ad alto livello di array e similari, imho è meglio usare la STL, ed usare gli array C solo per cose a basso livello.


Nell'esempio che riporti però non hai definito b come array ma semplicemente come puntatore. Nulla di strano quindi se poi usi l'operatore parentesi per spostarti secondo l'algebra dei puntatori. (Ed infatti il compilatore intel non solleva nessun warning con il tuo codice).
Diverso è invece quando dichiari un array, che non è come dichiarare un puntatore. Infatti quando dichiari un array stai anche riservando delle zone consecutive di memoria. Ed è in questo caso che possono sorgere problemi, cioè quando vai oltre lo spazio consentito.

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom ott 14, 2007 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Messaggioda nuitari » mer dic 12, 2007 23:31

Mi permetto di dissentire.
Quello che volevo evidenziare con il mio esempio è che gli array in C non esistono, per lo meno non come tipo dati. In C le parentesi quadre sono un semplice operatore binario che permette di lavorare con i puntatori, e nient'altro. Un array in informatica è molto più di questo.

Quando dichiari una variabile con le parentesi quadre nella definizione, stai dichiarando un area di memoria divisa in ipotetici spazi contigui, è vero, ma il nome diventa un puntatore a tutti gli effetti ai fini del codice, non un ipotetico "typeof Array", la differenza è solo nella compilazione. Es.:

Codice: Seleziona tutto

#include "stdio.h"
#include "stdlib.h"

int
main (void)
{
        int a[2] = {1};
        int *b = a;
        printf("a &%p *%p = %d\nb &%p *%p = %d\n", &a, a, *a, &b, b, *b);
        return 0;
}


output:

Codice: Seleziona tutto

samuele@nuitari-laptop:~$ gcc -Wall check2.c && a.out
a &0xbfeb41bc *0xbfeb41bc = 1
b &0xbfeb41b8 *0xbfeb41bc = 1


Per cui, per quale motivo dovrebbe un compilatore C fare dei controlli sull'indice che metti all'interno delle parentesi quadre? Lui sa che vuoi dereferenziare il puntatore, nient'altro. Se poi vai a leggere o scrivere in un area di memoria inizializzata o meno, questo è affar tuo.

Possono esistere 1000 casi che invalidano il controllo sugli array. Ad esempio, potresti aver liberato una porzione di memoria dell'array, ed in runtime otterresti comunque una violazione di accesso all'area di memoria, per quanto l'indice possa essere valido. Oppure potresti voler utilizzare la parentesi quadra per accedere ai membri di una struttura o di un oggetto. E li che tipo di controllo dovrebbe essere fatto?

Il concetto di array inteso come tipo dati è ben più di alto livello, proprio di altri linguaggi di programmazione come il Basic od il Java, o di librerie apposite. E' per questo che ho detto che ha più senso usare la STL per cose del genere. Oppure, non volendo utilizzare il C++, si potrebbe semplicemente definire un set di comode funzioni (array_create, array_get, array_set, array_free, etc) ed usarle per l'accesso, all'interno delle quali fare tutti i controlli del caso (ovviamente in fase di runtime, il momento in cui va effettivamente controllato il limite di un array).

Perchè fare tutto questo per degli array? Risposta: perchè il C non implementa gli array come tipo dati, gli mancano. Se stai pensando "vorrei che il C mi controllasse i limiti sugli indici mentre uso gli array" stai facendo un errore alla base, perchè ti aspetti che il C ti faccia un controllo su un tipo dati che non c'è, che non esiste, che se lo vuoi devi programmartelo da te.

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6524
Iscritto il: gio nov 03, 2005 14:05
Nome Cognome: Emanuele Tomasi
Slackware: current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Messaggioda targzeta » gio dic 13, 2007 1:51

Scusate ragazzi,
ma si viete mai posti la "vera" domanda????

C'è davvero un errore nel seguente codice?

Codice: Seleziona tutto

int i[8];

i[8]=3;

Siete veramente sicuri che non posso eseguire quell'istruzione? Siete sicuri che darà un "segmentation fault"? E se non lo dara, perchè non lo dara?

Eheh, la questione è sottile, ma il gcc ha ragione, non è un errore, se sai quello che stai facendo!!!
Che poi potrebbe segnalarlo con un warning (su richiesta) del tipo:
"Se stai iniziando a programmare, guarda che probabilmente stai sbagliando, altrimenti, scusa per il disturbo!!!"

questo è un altro paio di maniche, io mi trovo d'accordo con Mario.

Spina
Linux Registered User #454438
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama
20/04/2013 - Io volevo Rodotà 

Avatar utente
lamarozzo
Linux 3.x
Linux 3.x
Messaggi: 732
Iscritto il: gio lug 14, 2005 0:00
Desktop: xfce
Distribuzione: archlinux
Località: Roma

Messaggioda lamarozzo » gio dic 13, 2007 9:37

x nuitari e spina:

Sono d'accordo su quello che dite e nei programmi(ni) che scrivo mi capita di fare diverse porcherie con i puntatori. Il C non prevede nessun controllo sugli indici degli array, quindi gcc non è tenuto a segnalare un bel nulla. E sono d'accordo. Il topic nasce però da un altro problema. Dato un errore come quello che ho fatto io anche i più comuni tool di debugging della memoria falliscono (vedi Valgrind). Personalmente trovo un po' inquietante il fatto di poter violare un zona di memoria senza che ci sia un strumento che mi dice che ciò sta accadendo e chi ne è il responsabile. Supponiamo che il programma sia un progetto collaborativo con molte persone che ci mettono le mani e molte righe di codice e vi succede una cosa simile. In assenza di strumenti di rilevazione dell'errore possono succedere due cose. Corrompete una zona di memoria assegnata ad un'altra variabile che magari indica la quantità di farmaco da somministrare al paziente, non ve ne accorgete perchè l'errore non fa altro che raddoppiare la dose. E tanti saluti al paziente. Oppure siete fortunati e la corruzione vi produce una quantità di farmaco negativa e vi accorgete dell'assurdo.

A me il C piace così com'è, non lo voglio imbrigliato come il Fortran dove i puntatori vengono visti come il male. Soltanto che di fronte ad un errore banale come quello che ho fatto mi sono stupito di non avere strumenti per affrontarlo. In fondo se uno dichiara un array di lunghezza 8 evidentemente non può usare quel puntatore per altro che per l'array in questione (mi sembrerebbe una cattiva abitudine di programmazione altrimenti). Se gli serve di fare giochetti definisce un nuovo puntatore e poi si sbizzarrisce.
Non mi sembra che il mio ragionamento sia così assurdo, o almeno la pensano come me anche i programmatori di icc (che non è meno standard compliant di gcc).

Vabbè forse devo passare a C# :badgrin:

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom ott 14, 2007 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Messaggioda nuitari » gio dic 13, 2007 13:50

Il punto, ripeto, è che se vuoi usare un array come tale devi programmarti delle funzioni apposite, se vuoi esser sicuro di non commettere errori (o che altri che lavorano con te non li commettano). A maggior ragione se il software diventa grande.

So che la tentazione di ripiegare sugli *pseudo array* forniti dalle parentesi quadre senza programmare altro è forte, ma si tratta solo di farti una libreria (od usarne una ad hoc) ed includerla nei tuoi progetti... niente di drammatico :)

D'altronde, come dici tu, il C è così ed è bello perchè è così, quindi è una mancanza nei termini in cui è una mancanza il fatto che gli mancano le classi e via dicendo.. se non li ha non li ha ^_^

Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun set 03, 2007 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Messaggioda Mario Vanoni » gio dic 13, 2007 19:57

cat lamarozzo.c

Codice: Seleziona tutto

#include <stdio.h>

static double R[8];

int main()
{
       R[0] = .1234;
       R[8] = 2.;
       R[9] = 999.999;

       printf("0 %f\n", R[0]);
       printf("1 %f\n", R[1]);
       printf("8 %f\n", R[8]):
       printf("9 %f\n", R[9]);
       return(fprintf(stderr, "exiting\n"));
}


gcc lamorozzo.c
a.out
0 0.123400
1 0.000000
8 2.000000
9 999.999000
exiting
echo $?
8
gcc --version
gcc (GCC) 4.1.2
gcc -fsyntax-only -Wall lamarozzo.c
splint lamarozzo.c
Splint 3.1.2 -- 01 Aug 2007
finished checking --- no warnings

Slackware -current aggiornato.

Non mi registro alla Intel solo per provare icc.

Mario Vanoni

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6524
Iscritto il: gio nov 03, 2005 14:05
Nome Cognome: Emanuele Tomasi
Slackware: current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Messaggioda targzeta » gio dic 13, 2007 21:07

Mario Vanoni ha scritto:

Codice: Seleziona tutto

....
       return(fprintf(stderr, "exiting\n"));
....



A mio avviso c'è un errore, non credi? Solo perchè in questo thread si è decantata più volte l'abilità di un programmatore e bla..bla..bla.

Cito dal man di fprintf:

Codice: Seleziona tutto

...
Return value
   Upon successful return, these functions return the number of  characters
printed
...


L'errore è sottile, ma tra l'altro in questo thread ci si è bisticciati per molto meno :)

Sono sicuro che avrai già capito a cosa mi riferisco, e sono sicuro anche che sarai d'accordo con me ;).

Spina
Linux Registered User #454438
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama
20/04/2013 - Io volevo Rodotà