Vă mulțumim pentru susținere

De ce primesc o eroare dublă gratuită cu realloc ()?

Am încercat să scriu o funcție de înlocuire a șirului în C, care funcționează pe un char * , care a fost alocat folosind malloc () . Este puțin diferit în sensul că va găsi și înlocui șiruri, mai degrabă decât caractere din șirul de pornire.

Este inutil să faceți dacă căsuțele de căutare și înlocuire au aceeași lungime (sau șirul de înlocuire este mai scurt decât șirul de căutare), deoarece am suficient spațiu alocat. Dacă încerc să folosesc realloc () , primesc o eroare care îmi spune că fac un dublu gratuit - pe care nu îl văd cum sunt, deoarece folosesc doar realloc ) .

Poate un mic cod vă va ajuta:

void strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;

    while (find = strstr(find, search)) {

        if (delta > 0) {
            realloc(input, strlen(input) + delta);
            find = strstr(input, search);            
        }

        memmove(find + replaceLen, find + searchLen, strlen(input) - (find - input));
        memmove(find, replace, replaceLen);
    }
}

Programul funcționează, până când încerc să realloc () într-o instanță în care șirul înlocuit va fi mai lung decât șirul inițial. (Este încă un fel de lucrări, doar scuipă erori, precum și rezultatul).

Dacă vă ajută, codul de apelare arată astfel:

#include 
#include 
#include 

void strrep(char *input, char *search, char *replace);

int main(void) {
    char *input = malloc(81);

    while ((fgets(input, 81, stdin)) != NULL) {
        strrep(input, "Noel", "Christmas");
    }
}
0
adăugat editat

12 răspunsuri

Poți să cumperi module WiFi cu o interfață serială , dar sunt un pic pricy. Practic, le controlați prin UART și le trimiteți comenzi AT similare cu modul în care ați controlat modemurile dial-up în zilele vechi.

WiFi module

5
adăugat
Aceasta este o idee grozavă și chiar am găsit o bordură cu acest lucru pentru a face integrarea chiar mai ușoară. Singurele mele rezervări au fost cerințele și dimensiunile de putere. Vă mulțumim pentru sugestia dvs.! O sa-mi amintesc cu siguranta acest lucru pentru proiectele viitoare :-)
adăugat autor Mario Marinato

Extinde un pic mai mult.

Davr a sugerat și ceea ce voi sugera. Roving Networks face, de asemenea, un set bun de module BT.

Pentru a extinde. ZigBee este un protocol complet separat de Wi-Fi. ZigBee este similar cu BT în puterea radiată, care se referă la o cantitate mare de gamă, dar este diferită în sensul că este proiectat pentru a permite mai multor noduri să creeze o rețea, fiecare nod extinzând în mod efectiv rețeaua în afara intervalului său. Sper că acest lucru clarifică faptul că sunt similare, dar nu sunt legate.

3
adăugat
Multumesc pentru informatii. A ajutat să clarific câteva întrebări pe care le-am avut :)
adăugat autor Mario Marinato

Doar vroiam să împărtășesc soluția curentă cu voi toți:

După ce am discutat cu Marcus și Madeleine la LittleBirdElectronics, am ajuns la următoarea soluție posibilă:

1) Xbee pe The Lilypad,

2) USB Arduino, Ethernet Sheild și combo Xbee pentru a transfera mesaje pe WWW.

Aceasta este doar una dintre multele soluții posibile, dar pentru mine acest lucru pare să ofere avantajele cerințelor de putere și dimensiuni reduse la sfârșitul lilypad-ului prin utilizarea lui xbee în loc să utilizeze 802.11 direct pe lilypad.

În plus, am avut deja un scut Arduino și Ethernet de rezervă care nu se mai folosesc și dorea oricum o scuză să se joace cu xbee! ;-)

Vă mulțumim pentru contribuția dvs.! A fost foarte util în elaborarea unei soluții.

3
adăugat
Am analizat amândouă, dar modulul pe care l-am găsit a fost scump și în străinătate. Dat fiind că am deja un scut Arduino și un eternet, acest lucru a devenit puțin mai simplu :-) Mulțumesc mult pentru sugestia ta!
adăugat autor Mario Marinato
Cred că soluția sugerată de mine ar fi mai ieftină. Dar poate că este mai solidă, mai compactă și mai ușor de construit. Și tu deja tați hardware-ul.
adăugat autor Chris Bunch

Pot folosi un modul XBee și o interfață cumva cu routerul meu de acasă?

Da, dar trebuie să conectați modulul XBee la routerul dvs. Puteți încerca să găsiți portul serial pe placa de rutare sau prin USB la adaptorul serial dacă routerul dvs. are port USB. De asemenea, routerul dvs. ar trebui să funcționeze sub Linux (probabil openWRT).

1
adăugat

Ca regulă generală, trebuie niciodată să faceți o reîncărcare gratuită sau realocată pe un tampon furnizat de utilizator. Nu știți unde utilizatorul a alocat spațiul (în modulul dvs., într-un alt DLL), astfel încât să nu puteți utiliza niciuna dintre funcțiile de alocare pe un buffer utilizator.

Cu condiția ca acum să nu puteți face nicio realocare în cadrul funcției dvs., trebuie să vă schimbați puțin comportamentul, ca și cum ați face doar un singur înlocuitor, astfel încât utilizatorul va putea calcula lungimea maximă a șirului rezultat și vă va oferi un tampon suficient de lung pentru acesta înlocuire să apară.

Apoi, puteți crea o altă funcție pentru a face înlocuirile multiple, dar va trebui să alocați întregul spațiu pentru șirul rezultat și să copiați șirul de intrare al utilizatorului. Apoi trebuie să oferiți o modalitate de a șterge șirul pe care l-ați alocat.

Rezultând:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
0
adăugat

Sugestiile mele rapide.

Instead of:
void strrep(char *input, char *search, char *replace)
try:
void strrep(char *&input, char *search, char *replace)

and than in the body:
input = realloc(input, strlen(input) + delta);

Citiți în general despre argumentarea funcțiilor de trecere ca valori / referință și realloc () descriere :).

0
adăugat
Notația void strrep (char * & input, char * search, char * replace) nu este validă în C? deși este valabil în C ++. Întrebarea nu este, și AFAICT nu a fost niciodată, etichetat cu C ++. La cel mai bun, codul ar trebui să fie void strrep (char ** de intrare, char * căutare, char * înlocui) , deși este ușor să susțină că char * strrep (const char * intrare, const char * search, const char * replace) este o interfață funcțională (intrările nu sunt modificate, șirul modificat este alocat și returnat).
adăugat autor Jonathan Leffler

Rețineți că încercați să modificați codul pentru a scăpa de codurile de evacuare html.

Ei bine, deși a fost un timp de când am folosit C / C ++, realloc că creste doar refolosesc valoarea indicatorului de memorie dacă există loc în memorie după blocul original.

De exemplu, ia în considerare acest lucru:

(Xxxxxxxxxx ..........)

Dacă indicatorul dvs. indică spre primul x, și. înseamnă locație de memorie liberă și veți crește dimensiunea memoriei indicată de variabila dvs. cu 5 octeți, va reuși. Acesta este, desigur, un exemplu simplificat, deoarece blocurile sunt rotunjite până la o anumită dimensiune pentru aliniere, dar oricum.

Cu toate acestea, dacă încercați ulterior să-l crească cu încă 10 octeți și există doar 5 disponibile, va trebui să mutați blocul în memorie și să vă actualizați indicatorul.

Cu toate acestea, în exemplul dvs. treceți funcția un pointer la caracter, nu un indicator pentru variabila dvs. și astfel, în timp ce funcția strrep intern poate fi capabilă să ajusteze variabila în uz, este o variabilă locală față de funcția strrep și codul dvs. de apelare va rămâne la valoarea variabilă a indicatorului inițial.

Această valoare a indicatorului, totuși, a fost eliberată.

În cazul tău, intrarea este vinovatul.

Cu toate acestea, aș face o altă sugestie. În cazul tău, se pare că variabila introducere este într-adevăr o intrare și, dacă este, nu ar trebui să fie modificată deloc.

Prin urmare, aș încerca să găsesc un alt mod de a face ceea ce doriți să faceți, fără a schimba introducerea , deoarece efecte secundare ca aceasta pot fi greu de urmărit.

0
adăugat

Acest lucru pare să funcționeze;

char *strrep(char *string, const char *search, const char *replace) {
    char *p = strstr(string, search);

    if (p) {
        int occurrence = p - string;
        int stringlength = strlen(string);
        int searchlength = strlen(search);
        int replacelength = strlen(replace);

        if (replacelength > searchlength) {
            string = (char *) realloc(string, strlen(string) 
                + replacelength - searchlength + 1);
        }

        if (replacelength != searchlength) {
            memmove(string + occurrence + replacelength, 
                        string + occurrence + searchlength, 
                        stringlength - occurrence - searchlength + 1);
        }

        strncpy(string + occurrence, replace, replacelength);
    }

    return string;
}

Sigh, este oricum sa postezi codul fara a suge?

0
adăugat
Adăugarea unui comentariu, deoarece comentariul a fost scris ca răspuns, înainte ca comentariul să fie disponibil: Aceasta pare să schimbe doar prima apariție. Ceea ce este probabil rezonabil, din moment ce nu am afirmat cu adevărat că trebuie să le schimbați pe toți!
adăugat autor Matthew Schinckel

Cineva a cerut scuze pentru întârzierea petrecerii - acum două luni și jumătate. Ei bine, eu petrec destul de mult timp făcând arheologie software.

Sunt interesat de faptul că nimeni nu a comentat în mod explicit despre scurgeri de memorie în designul original sau despre eroarea off-by-one. Și a fost observarea scurgerii de memorie care îmi spune exact de ce obțineți eroarea de dublu-liberă (deoarece, pentru a fi exact, eliberați aceeași memorie de mai multe ori - și asta faceți după ce ați călcat peste memoria deja eliberată).

Înainte de a efectua analiza, voi fi de acord cu cei care spun că interfața dvs. este mai mică decât stelară; cu toate acestea, dacă ați rezolvat problemele legate de scurgeri de memorie și ați documentat cerința "trebuie să fie alocată memoriei", ar putea fi "OK".

Care sunt problemele? Puneți un tampon la realloc (), iar realloc () vă întoarce un nou indicator în zona pe care ar trebui să o utilizați - și ignorați valoarea returnată. În consecință, realloc () a eliberat, probabil, memoria originală, iar apoi îi dați din nou același indicator și vă plânge că eliberați aceeași memorie de două ori deoarece ați trecut din nou valoarea inițială. Acest lucru nu numai că scurgeri de memorie, dar înseamnă că continuați să utilizați spațiul original - și împușcatul lui John Downey în întuneric arată că folosiți în mod greșit realloc (), dar nu subliniază cât de grav faceți acest lucru. Există, de asemenea, o eroare off-by-one deoarece nu alocați spațiu suficient pentru NUL '\ 0' care termină șirul.

Scurgerea memoriei apare deoarece nu furnizați un mecanism care să-i spună apelantului despre ultima valoare a șirului. Deoarece ați păstrat în călcare peste șirul original, plus spațiul după el, se pare că codul a funcționat, dar dacă codul dvs. de apelare a eliberat spațiul, acesta ar primi o eroare dublu-liberă, sau s-ar putea obține un dump de bază sau un echivalent informațiile de control al memoriei sunt complet codate.

De asemenea, codul dvs. nu protejează împotriva creșterii nedefinite - luați în considerare înlocuirea lui "Noel" cu "Joyeux Noel". De fiecare dată, adăugați 7 caractere, dar ați găsit un alt Noel în textul înlocuit și lărgiți-l și așa mai departe și așa mai departe. Remedierea mea (de mai jos) nu abordează această problemă - soluția simplă este probabil să verificați dacă șirul de căutare apare în șirul de înlocuire; o alternativă este să săriți peste șirul de înlocuire și să continuați căutarea după el. Cel de-al doilea are câteva probleme non-triviale de codare pentru a le adresa.

Deci, revizuirea sugerată a funcției dvs. de chemare este:

char *strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;

    while ((find = strstr(find, search)) != 0) {
        if (delta > 0) {
            input = realloc(input, strlen(input) + delta + 1);
            find = strstr(input, search);            
        }

        memmove(find + replaceLen, find + searchLen, strlen(input) + 1 - (find - input));
        memmove(find, replace, replaceLen);
    }

    return(input);
}

Acest cod nu detectează erorile de alocare a memoriei - și probabil că se blochează (dar dacă nu, scurgeri de memorie) dacă realloc () nu reușește. Consultați cartea lui Steve Maguire, "Writing Code Solid", pentru o discuție amplă despre problemele de gestionare a memoriei.

0
adăugat
Mulțumesc, este o analiză foarte bună a ceea ce făceam greșit (și că dublu-liber a fost într-un sens un produs secundar al mai multor lucruri pe care le-am făcut greșit.) Cred că am avut în capul meu că realloc ) a extins doar alocarea memoriei - ceea ce nu are nici un sens, când mă gândesc la asta!
adăugat autor Matthew Schinckel

realloc este ciudat, complicat și ar trebui să fie utilizat numai atunci când se ocupă cu o mulțime de memorie de multe ori pe secundă. adică - unde face de fapt codul dvs. mai rapid.

Am văzut codul unde

realloc(bytes, smallerSize);

a fost folosit și a lucrat pentru a redimensiona tamponul, făcându-l mai mic. A lucrat aproximativ un milion de ori, apoi, dintr-un anumit motiv, realloc a decis că, chiar dacă ați scurta tamponul, v-ar da o copie nouă. Deci, vă prăbușiți într-un loc aleatoriu o jumătate de secundă după ce s-au întâmplat lucrurile rele.

Utilizați întotdeauna valoarea returnată a realloc.

0
adăugat

Doar o lovitură în întuneric pentru că nu am încercat încă, dar când realloc returnează pointerul la fel ca malloc. Deoarece realloc poate muta pointerul dacă este necesar, cel mai probabil lucrați cu un pointer nevalid dacă nu faceți următoarele:

input = realloc(input, strlen(input) + delta);
0
adăugat
Și dacă realloc eșuează, returnează NULL și lasă singur bufferul existent. Tocmai ai pierdut pointerul ... :-(
adăugat autor Roger Lipscombe

În primul rând, îmi pare rău că am întârziat la petrecere. Acesta este primul meu răspuns stackoverflow. :)

Așa cum am subliniat, atunci când se numește realloc (), poți schimba eventual pointerul în memoria care este realocată. Când se întâmplă acest lucru, argumentul "șir" devine nevalid. Chiar dacă îl reasociți, schimbarea nu mai are obiect atunci când se termină funcția.

Pentru a răspunde la OP, realloc () returnează un indicator la memoria nou-reallocated. Valoarea returnată trebuie să fie stocată undeva. În general, ați face acest lucru:

data *foo = malloc(SIZE * sizeof(data));
data *bar = realloc(foo, NEWSIZE * sizeof(data));

/* Test bar for safety before blowing away foo */
if (bar != NULL)
{
   foo = bar;
   bar = NULL;
}
else
{
   fprintf(stderr, "Crap. Memory error.\n");
   free(foo);
   exit(-1);
}

După cum subliniază TyBoer, voi nu puteți schimba valoarea indicelui care este transmis ca intrare pentru această funcție. Puteți aloca orice doriți, dar schimbarea va ieși din domeniul de aplicare la sfârșitul funcției. În următorul bloc, "intrarea" poate sau nu poate fi un indicator nevalid după finalizarea funcției:

void foobar(char *input, int newlength)
{
   /* Here, I ignore my own advice to save space. Check your return values! */
   input = realloc(input, newlength * sizeof(char));
}

Mark încearcă să rezolve această problemă returnând noul pointer ca ieșire a funcției. Dacă faceți acest lucru, responsabilitatea este ca apelantul să nu mai folosească niciodată indicatorul pe care la folosit pentru introducere. Dacă se potrivește cu valoarea returnată, atunci aveți două indicatoare în același loc și trebuie să apelați gratuit () pe unul dintre ele. Dacă acestea nu se potrivesc, indicatorul de intrare indică acum memoria care poate fi sau nu deținută de proces. Derefecționarea poate duce la o defecțiune în segmentare.

Ați putea folosi un indicator dublu pentru intrare, după cum urmează:

void foobar(char **input, int newlength)
{
   *input = realloc(*input, newlength * sizeof(char));
}

Dacă apelantul are un duplicat al indicatorului de intrare undeva, acel duplicat ar putea să nu mai fie valabil acum.

Cred că cea mai curată soluție aici este de a evita utilizarea realloc () atunci când încercați să modificați intrarea apelantului funcției. Doar malloc () un tampon nou, întoarceți-l și lăsați-l pe apelant să decidă dacă sau nu să elibereze vechiul text. Acest lucru are avantajul suplimentar de a lăsa apelantul să păstreze șirul original!

0
adăugat