Newsgroup: it.comp.programmare
Titolo: linguaggio C e puntatori. un parere

Ciao a tutti.
Dove lavoro capita che un programmatore perda parecchio tempo a causa di 
errori causati dal passaggio di puntatori nulli o errati a funzioni come 
strcpy(), strcmp() ecc.
Per cercare di limitare un po' il problema ho pensato di inserire in un ns. 
#include generale delle #define del genere
#define strcmp(x, y)  _xx_strcmp(x, y)
Poi scrivero' le funzioni _xx_strcmp(char *x, char *y) e similari che per 
prima cosa controlleranno se gli argomenti passati sono nulli e nel caso 
segnaleranno il fatto, altrimenti chiameranno la funzione reale passando a 
questa gli argomenti.
A questo punto vi chiedo che ne pensate ? Vi sembra una buona idea ?
Esiste un sistema migliore per fare cio' ?
A parte i puntatori nulli, facili da riconoscere, esiste un sistema per 
riconoscere un puntatore non nullo ma invalido ?
Grazie
--  
Stefano L.

 riconoscere un puntatore non nullo ma invalido ?
un puntatore sballato contiene una valore "casuale", che potrebbe o meno 
essere corretto. "ad occhio" direi che potresti controllare che punti ad 
un'area dati. se punta a codice, o altro è sbagliato. (in realtà il codice 
potresti leggerlo, ma di solito non possibilità si utilizza).
Il "come" fare questo controllo dipende dal sistema, e probabilmente lo dovrai 
fare in assembler
Ciao
-- 
Ranger! 
www.rossi.eu.com
aggiungi .it alla mail x rispondermi

 strcpy(), strcmp() ecc.
Vedi, in linea di principio il problema non è il fatto che passi a quelle
funzioni degli argomenti nulli, ma bensi' il problema reale è proprio il
fatto che da qualche parte nel codice assegni un valore nullo alle variabili
che dai in pasto ai metodi stessi.
Questo indica che nel tuo sistema c'e' una falla, che deve essere risolta in
modo tale che il programma nn possa arrivare a manipolare referenze non
valide.
Più che tentare una soluzione a valle (che non risolve il problema in sé, ma
ne ritarda semplicemente l'individuazione), io mi concentrerei sulla
risoluzione dei processi che stanno a monte, irrobustendo il codice dove
necessario.
Saluti
--
NinjaCross
 
www.ninjacross.it
Membro dell'Italian Agile Movement.
www.agilemovement.it
Sconnetti il chip isolineare per contattarmi
ninjacross_CH...@yahoo.it

 necessario.
Ad esempio ?
Ripeto, sono daccordo con te ma all'atto pratico... un colpo al cerchio e 
uno alla botte.
Quello che ho pensato di certo non risolve tutti i miei problemi ma credo 
possa aiutare a risolverne alcuni.
--  
Stefano L.

 Ad esempio ?
Per esempio eseguendo i test necessari dopo operazioni critiche che li
coinvolgono, come:
1- if(variabile!=null)
2- assert()
3- try-catch
Il tutto associato ad un buon sistema di logging rende le cose molto +
semplici.
Se hai problemi di efficienza, ed inserire tanti controlli non è ideale al
tuo scopo, puoi benissimi inserire i blocchi di controllo all'interno di
sezioni macro, da disabilitare in fase di release con un semplice #define.
Non avendo specificato il tuo specifico target, non so darti informazioni +
precise.
A solo titolo di esempio cmq, considera che ho lavorato su progetti di circa
30.000 righe di codice usando questi meccanismi e mi sono trovato sempre
bene :)


 sempre bene :)
Purtoppo sto parlando di 2.000.000 di righe di codice create nell'arco di 10 
anni da molte persone. Non e' semplice revisionarle tutte, e in ogno caso 
non e' questa l'esigenza.
La mia intenzione era quella di dare uno strumento in piu' ai programmatori
per facilitare il debug.
Tornando all'inizio del discorso sarebbe interessante anche un sistema per 
identificare i puntatori non nulli ma sballati, certo e' impossibile 
controllare che puntino dove dovrebbero ma almeno sapere se puntano fuori 
dall'area dati del programma.
--  
Stefano L.

 non e' questa l'esigenza.
Incominciate ad eliminare le stringhe C ed usare std::string. Non è tempo 
perso. 
-- 
Luc.

 se puntano fuori dall'area dati del programma.
Non ho mai avuto occasione di provarlo, pero' c'e' Electric Fence che
potrebbe fare al caso tuo, o comunque darti qualche ispirazione (e' un
progetto open source).
Luca

 perso. 
Ora che hai finito il refactoring di due milioni di righe di C in C++, 
il progetto e` diventato obsoleto (anzi, putrescente)...
Io inizierei a metter da parte i soldini per strumenti come Purify [1] 
(se sei su Windows/Solaris/HP-UX/Irix), o a scaricarti Valgrind [2] o 
ElectricFence [3] se sei su Linux x86.
----
[1] Rational Purify
 
http://www.rational.com/products/purify_nt/index.jsp
 
http://www.rational.com/products/purify_unix/index.jsp
[2] Valgrind: 
http://developer.kde.org/~sewardj/
[3] Electric Fence: 
http://perens.com/FreeSoftware/
-- 
   |   \    \  | ___|_  |_  | ianezz a casa sua... :-)
   |  _ \  | \ | _|    /   /  Visita il LinuxTrent a
  _|_/  _\_|  _|____|___|___| 
http://www.linuxtrent.it

 non e' questa l'esigenza.
Lasciami capire: hai 2.000.000 di righe di codice vecchio di 10 anni buggato
????
Urgh... non vorrei essere al posto tuo ;)


 Urgh... non vorrei essere al posto tuo ;)
No ho un progetto in continua evoluzione.
Quando si fanno delle modifiche o si aggiungono nuovi componenti capita di 
introdurre dei bug (da te non succede ?), un apposita fase di test ne 
elimina la magior parte qualunque accorgimento per facilitare/velocizzare 
il testing e' benvenuto.
Tra l'altro tu connosci progetti di dimensioni paragonabili senza bug di 
nessun genere ? Sono curioso.
--  
Stefano L.

 nessun genere ? Sono curioso.
Il numero di righe non quantifica la dimensione del progetto, soprattutto se
sviluppi in C dove non puoi utilizzare efficacemente molti dei paradigmi
della programmazione ad oggetti.
Sinceramente mi è molto difficile anche solo creare un punto di riferimento,
non ho informazioni sufficienti :)


 Non in C (o sbaglio)
scusa, ma un debugger non usa, dalle vostre parti? nel tuo caso un try-
catch, anche se potessi usarlo, non risolverebbe. Basterebbe far girare 
il programma dentro un debugger: al primo errore avresti lo stack trace 
completo, con tanto di evidenziazione dell'esatta riga di codice
-- 
  < 
http://www.reactos.com/
 >  Open source clone of Windows NT. Current
      Don't stand, REACT!      version 0.1.1. C, C++ and ASM developers
                               and beta testers are welcome!


:Provate a guardare gli ingredienti del famoso Labello che si usa per
:le labbra screpolate: Butirrospermum Parkii (sperma di maiale).

 (e bisogna stabilire chi e dove).
Va bene, ho anche pensato a quello, pero' rimane il fatto che un puntatore 
puo' provenire non soltanro da una malloc, ma anche, ad esempio da
   char pippo[20], *Pluto="xxxx"
quindi posso usare il sistema descritto solo per testare un puntatore prima 
di una free e non prima di una strcpy o altro.
Comunque anche controllare malloc, calloc, free strdup ecc. e' una buona 
idea.
Mi chiedo se esistano compilatori che facciano in automatico tutto cio' 
passando degli argomenti.
--  
Stefano L.

 scusa, ma un debugger non usa, dalle vostre parti?
Usiamo anche il debugger, peccato che spesso, quando un programma ha un 
errore dovuto a un puntatore ciucco, ricompilandolo con il debug il 
problema sparisca in quanto il puntatore e' sempre ciucco ma non punta piu' 
a una zona dove fa danni.
 
 
--  
Stefano L.

di una free e non prima di una strcpy o altro.
Sinceramente, *Pluto = "xxxx" lo trovo un rischio. Su alcuni sistemi, per
dire,
	*Pluto = "1234";
	sprintf(Pluto, "%d", atol(Pluto)+1);
causa un segmentation fault anche per valori del Pluto che non causano l'
evidente buffer overflow.
Meglio, anche se non e' l'ottimo,
	char *Pluto;
	Pluto = strdup("1234");


 [...] ricompilandolo con il debug [...]
non č necessario. Un map file si puň produrre, ed č comunque utile, per 
qualsiasi tipo di eseguibile. Abbi solo l'accortezza di non attivare le 
ottimizzazioni che rendono piů difficile il debugging, come l'omissione 
degli stack frame dove possibile. La soluzione definitiva rimane quella 
di trovare o comprarsi una libreria o un compilatore che facciano tutti 
i controlli necessari - esempio: il compilatore del Borland C++ Builder 
puň compilare il codice in modo che *ogni singolo accesso alla memoria* 
venga controllato da una libreria chiamata CodeGuard


 Sinceramente, *Pluto = "xxxx" lo trovo un rischio. 
Adesso che mi ci fai pensare avevo notato anche io comportamenti 
"particolari" in proposito, ad esempio
   char *alfa="1234";
   char *beta;
   beta = "1234" 
A questo punto alfa puo' essere uguale uguale a beta !
Cioe' il compilatore ottimizza riusando la stessa memoria perche' si accorge 
che la costante e' la stessa.
La cosa e' molto pericolosa se ad esempio in seguito si scrive
   strcpy(beta, "4567");
Ci si ritrova alfa cambiato !
Sul mio compilatore, per fortuna, si puo' dare un parametro che impedisce 
tale ottimizzazione.
 
Qualcuno conosce un documento che descrive queste potenziali trappole in cui 
si puo' cadere ?
--  
Stefano L.

 venga controllato da una libreria chiamata CodeGuard
Grazie, ci guardero'
--  
Stefano L.

   strcpy(beta, "4567");
   A me, una cosa molto simile, con gcc su Solaris dava segmentation
fault in esecuzione.
   In effetti si trattava di un strtok, comunque il concetto era lo
stesso. :)
-- 
S.L.

 Ci si ritrova alfa cambiato !
non c'e` nulla di strano. Il tuo "1234" e` quello che lo standard chiama
"string literal": il tipo degli string literal e` 'const char*', per cui
*non* possono essere modificati. Purtroppo lo standard ammette
esplicitamente l'assegnamento di uno string literal ad un char* (non-const)
per ragioni di compatibilita`: ma in ogni caso occorre sapere che il
puntatore punta ad un'area di memoria read-only (da qui il segmentation
fault su alcuni sistemi) e quindi non va *mai* modificata, in nessun caso.
Tecnicamente si parla di "undefined behaviour", quindi potrebbe accadere di
tutto al tuo programma: dal segfault, al corretto funzionamento, alla
formattazione del disco fisso ;-).
Occhio che invece questo caso e` *ben* diverso:
        char alfa[] = "1234";
In questo caso il compilatore alloca sullo stack o nella memoria statica un
array di 5 char su cui puoi scrivere normalmente. La linea sopra e`
esattamente equivalente a questa:
        char alfa[5] = {'1', '2', '3', '4', '\0'};
ciao Anto
-- 
"Computer science is not about computers any more than astronomy
is about telescopes." -- EW Dijkstra