Προς το περιεχόμενο

C: Περίεργη συμπεριφορά της free()?


geomagas

Προτεινόμενες αναρτήσεις

Πολύ πιθανόν να έχω κολλήσει σε κάτι προφανές, αλλά μου έχει σπάσει τα νεύρα! :mad:

 

Έστω το παρακάτω:


char *getlexeme(lexer *dfa,char **srcpos)
    {
    char *token_start=*srcpos;
    // ...
    // do stuff
    // ...
    // *srcpos is guaranteed >=token_start
    char *str=malloc(*srcpos-token_start+1);
    strncpy(str,token_start,*srcpos-token_start);
    return str;
    }
    
token *gettoken(lexer *dfa,char **srcpos)
    {
    char *l;
    symbol *s;
    while((l=getlexeme(dfa,srcpos))[0]) // l>""
        {
        s=dfa->current->symbol;
        if(s&&(/*some other conds*/))
            {
            printf("i[%s]",l);
            free(l);
            }
        else
            {
            printf("r[%s]\n",l);
            return tokenize(s,l,NULL);
            }
        }
    printf("EOS!\n");
    return tokenize(dfa->eos,"<EOS>",NULL);
    }

char *txt="\n";
token *t=gettoken(somedfa,&t);

Έτσι όπως είναι, δεν εκτυπώνει ποτέ "EOS!\n".

Αν βγάλω την free(l); δουλεύει κανονικά, αλλά προφανώς δεν ελευθερώνεται η μνήμη, όπως θα ήθελα... :wacko:

 

Τι χάνω;

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Απαντ. 59
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Συχνή συμμετοχή στο θέμα

Το μήνυμά μου δεν θα βοηθήσει πολύ (έως καθόλου) αλλά θέλω να ρωτήσω αν ο κώδικας που δεν δουλεύει είναι ακριβώς έτσι ? Το ρωτάω γιατί μερικές εκφράσεις είναι παράξενες.

 

π.χ Στο printf γιατί έχεις δύο l ορίσματα ενώ έχεις ορίσει μόνο ένα %s

 

Στην getlexeme, εφόσον το token_start δείχνει στο *srcpos, η έκφραση "*srcpos-token_start" δεν είναι μηδενική οπότε η malloc δεσμεύει 1 θέση και η strncpy δεν αντιγράφει τίποτα ?

 

Άκυρο. Ό,τι να ναι διαβάζω. Στα σχόλια λες ότι υπάρχει και άλλος κώδικας που αυξάνει το srcpos. Μες τη χαζομάρα είμαι.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

π.χ Στο printf γιατί έχεις δύο l ορίσματα ενώ έχεις ορίσει μόνο ένα %s

 

Διότι είχε ξεμείνει έτσι... Το διόρθωσα.

 

Στη συγκεκριμένη συνάρτηση, αυτός είναι ο κώδικας αυτούσιος*.

Δεν ξέρω αν θα βοηθήσει ή θα μπερδέψει περισσότερο, αλλά ολόκληρη η getlexeme() είναι αυτή:

char *getlexeme(lexer *dfa,char **srcpos)
    {
    char *token_start=*srcpos,*t=*srcpos,c;
    dfa->current=dfa->start;
    int i,nedges;
    printf("%c(%d)",**srcpos,**srcpos);
    while(c=**srcpos)
        {
        nedges=dfa->current->nedges;
        for(i=0;i<nedges;i++)
            if(strchr(dfa->current->edge[i].charset,c))
                {
                dfa->current=dfa->current->edge[i].target;
                (*srcpos)++;
                break;
                }
        if(i==nedges) break; // no shift, dead end!
        }
    char *str=malloc(*srcpos-token_start+1);
    strncpy(str,token_start,*srcpos-token_start);
    return str;
    }

* EDIT: Εχμ... εκτός από το if, που είνα αυτό:

if(s&&((s->kind==2)||(s->kind==4)||(s->kind==5)||(s->kind==6)))
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Έτσι όπως είναι, δεν εκτυπώνει ποτέ "EOS!\n".

Αν βγάλω την free(l); δουλεύει κανονικά, αλλά προφανώς δεν ελευθερώνεται η μνήμη, όπως θα ήθελα... :wacko:

 

Τι χάνω;

Δεν δίνεις πολλά info για να το δω πιο διεξοδικά, αλλά από ότι δείχνεις φαίνεται πως η συνθήκη: if ( s && /* someother stuff */ ) είναι πάντα TRUE;

 

ΥΓ. Άσχετο, μετά το char *str=malloc(*srcpos-token_start+1); βάλε check για NULL πριν πας στην strncpy()

 

EDIT:

 

Α, έβαλες νέο κώδικα; Κάτσε να το δω μήπως μπορέσω να βοηθήσω.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Στη περίπτωση που δεν εμφανίζει το EOS παίρνεις κάποιο μήνυμα λάθους ή το while παίζει επ αόριστον ?

 

Όχι, τερματίζει κανονικά. Και μάλιστα από το 2ο return, γιατί το επόμενο l=getlexeme(...) θα δώσει "\0".

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

ΥΓ. Άσχετο, μετά το char *str=malloc(*srcpos-token_start+1); βάλε check για NULL πριν πας στην strncpy()

 

Έχεις δίκιο, το έκανα. :-)

 

Στη περίπτωση που δεν εμφανίζει το EOS παίρνεις κάποιο μήνυμα λάθους ή το while παίζει επ αόριστον ?

 

Όχι, τερματίζει κανονικά. Και μάλιστα από το 2ο return, γιατί το επόμενο l=getlexeme(...) θα δώσει "\0".

 

Τώρα πρόσθεσα στο τέλος:

    printf("%s\n",t->value);

και μου βγάζει segfault στη μέση της printf("i[%s]",l):

(10)i[
Segmentation fault (core dumped)

Βαλε ενα breakpoint να δεις τι παιζει...

 

Όντως, βάλτο στον debugger.

 

Στην τελική θα το κάνω κι έτσι, αλλά δεν είναι πολύ κουλό;

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Κάτι βρήκα νομίζω...

	char *l;
	symbol *s;
	while ( (l = getlexeme(dfa,srcpos))[0] ) // l>""
	{
μάλλον ήθελες...

	while ( (l = getlexeme(dfa,srcpos)) ) // l>""
	{
μιας και η getlexme() επιστρέφει char * (και άρα το [0] δίνει char, ενώ το l το έχεις ορίσει ως char *)

 

EDIT:

 

Μπα όχι, άκυρο! Σωστά τις έχεις τις παρενθέσεις. Debugger, debugger!

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Αφού αν βγάλεις τη free τερματίζει αυτό σημαίνει ότι η συμπεριφορά του προγράμματος εξαρτάται από τον τυχαίο παράγοντα του τι υπάρχει στη μνήμη που σου γυρνάει η malloc() -- βγαίνοντας η free ο allocator σου γυρνάει άλλα πράγματα που τυχαίνει να έχουν άλλες τιμές οπότε αλλάζει η συμπεριφορά του.

 

Μέχρι εδώ ήδη ξέρεις ότι έχεις bug γιατί το να εξαρτάσαι από το τι σκουπίδια έχει η μνήμη που σου γυρνάει η malloc δεν είναι βέβαια σωστό.

 

Η malloc γίνεται στην getlexeme οπότε εκεί κοιτάς για το bug (ενδεχομένως όχι μέσα στον κώδικα αλλά ίσως στο contract που υπόσχεται στον caller της και τι απαιτήσεις τελικά έχει αυτός).

 

Βλέπεις λοιπόν στη getlexeme ότι κάνεις αυτό:

strncpy(str,token_start,*srcpos-token_start);

Η τρίτη παράμετρος είναι η λεγόμενη "num". Τι λέει το man γι' αυτή;

 

No null-character is implicitly appended at the end of destination if source is longer than num. Thus, in this case,destination shall not be considered a null terminated C string (reading it as such would overflow).

 

 

Άρα αν ισχύει ότι *srcpos == token_start τότε η strncpy δε θα κάνει τίποτα απολύτως οπότε μένει το junk από τη malloc αναλλοίωτο να επηρρεάζει το τι γίνεται μέσα στο if εκεί που καλείς τη getlexeme.

 

Οπότε ξέρεις πού είναι το bug, φτιάξτο (δεν έκατσα να δω ποιά είναι ακριβώς η λογική στη getlexeme).

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Με αφορμή αυτό που λέει ο defacer (που είναι σωστό, και πιθανόν να είναι και το πρόβλημά σου) να προσθέσω πως εγώ σχεδόν την έχω καταργήσει την malloc() (εκτός από πολύ ειδικές περιπτώσεις) και την έχω αντικαταστήσει με την calloc().

 

Δηλαδή στο παράδειγμά σου θα το έκανα κάπως έτσι:

	...
	size_t len = *srcpos - tokstart;
	char *str = calloc( 1, 1+len );
	if ( str ) {
		strncpy(str, tokstart, len);
	}
	return str;
}
Αλλά έχει trade-off. Αν βασίζεσαι στο "immediate crash is good" μάλλον δεν θα σε βολέψει. Αν και στην προκειμένη περίπτωση που συζητάμε (και στις περισσότερες) με την malloc() δεν έχεις εγγυημένα immediate crash.
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

A, ξέχασα... αν το θυμάμαι καλά, τόσο η malloc() όσο και η calloc() δεν κάνουν fail αν τους ζητήσεις 0 bytes μνήμη, οπότε έχετε το κι αυτό υπόψη σου (ή ψάξτο αν όντως ισχύει ακόμα, γιατί εγώ πάω να δω μπαλίτσα τώρα :) )

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...