Přidat otázku mezi oblíbenéZasílat nové odpovědi e-mailem dynamicka alokace C, retezce

Zdravim, potreboval bych vysvetlit situaci v nasledujicim programku:

int main(void){
	char *p = NULL, *r = NULL;
	r = "no nazdarek";
	p = (char *) malloc(sizeof(char));
	strcpy(p,r);
	//p = "ahojky";
	printf("delka r: %d\ndelka p: %d\n",strlen(p),strlen(r));
	return 0;
}

Proc musim naalokovat misto pro p a pro r jsem nemusel? Je to proto, ze bez mallocku se nevi, kam p ukazuje?
A proc staci pro "p" naalokovat pouze jeden char, kdyz "no nazdarek" ma 11 znaku? a jak je to s '\0' v tomto pripade? je na 12. miste '\0' nebo ne?
Nemelo by tam byt spis

p = (char *) malloc(sizeof(char) * strlen(r));

Diky za pomoc v pochopeni teto problematiky...

Předmět Autor Datum
Smerník r ukazuje po priradení toho reťazca do pamäte, v ktorej je uložený reťazec "no nazdarek". Tá…
los 18.12.2012 19:15
los
Ok, takze vzdy, kdyz potrebuju zkopirovat retezec nekam jinam, musim nejdriv naalokovat pamet o znak…
karlos15 18.12.2012 20:17
karlos15
Funkcia strncpy tam ten ukončovací znak nevloží, ak je kopírovaný reťazec dlhší. Len zabezpečí, aby…
los 18.12.2012 21:19
los
Diky! A kdyz zavolam free(p): free uvolni pamet, ale p porad ukazuje na uvolnene misto. Musim do "p"…
karlos15 19.12.2012 10:46
karlos15
nemusis ukladat do 'p' NULL po free(). Po zavolani malloc v dalsom cykle ti uz 'p' bude ukazovat do…
nl12345 19.12.2012 13:35
nl12345
Ok. Jinak neznate nejakej program s debuggerem, ve kterym uvidim primo jak vypada retezec v pameti i…
karlos15 19.12.2012 14:10
karlos15
Eclipse na C? Eclipse je sotva dobrý na Javu... Použi radšej niečo kvalitné, ako napr. Visual Studio…
los 19.12.2012 22:03
los
Myslis jo? Ja prave pisu javu v eclipsu a jelikoz na to existujou doplnky, tak v tom pisu i Ccko...B…
karlos15 19.12.2012 22:15
karlos15
Ak si zvyknutý na Eclipse, tak asi ok. Môj názor je, že pre C existujú aj lepšie IDE.
los 19.12.2012 22:24
los
Čo znamená tá podmienka "if(strlen(nacteno) == 1)"? Dúfam, že znak '\0' nepočítaš do dĺžky reťazca.…
los 19.12.2012 21:59
los
Nepocitam, jelikoz fgets nacte radku i s '\n', takze v retezci je "\n\0", coz je dlouhy prave 1, pro…
karlos15 19.12.2012 22:12
karlos15
Aha, takto to dáva zmysel. Osobne by som tam dal podmienku, nech to odstráni len v prípade, že na ko…
los 19.12.2012 22:29
los
Ok. Muzu se zeptat, jestli bych ti mohl napsat mail, kdybych mel nejakej dotaz z Ccka? nemam/neznam…
karlos15 19.12.2012 23:07
karlos15
A jestli mi muzes prosim poradit jeste: Jak je to s "freeovanim" pointeru p, ktery vznikl jako: char…
karlos15 20.12.2012 14:13
karlos15
Pole char p[velikost] je alokované na stacku, takže po opustení rozsahu platnosti sa automaticky uvo…
los 20.12.2012 22:18
los
Tak sem se hnul dal a vyladuju uniky pameti valgrindem. Potreboval jsem si napsat metodu, ktera mi o…
karlos15 22.12.2012 12:42
karlos15
Ani jedna metóda nie je správne, pretože obidve sú zbytočné. Funkcia strlen určí dĺžku reťazca práve…
los 22.12.2012 12:55
los
strnlen? Jak to ale pouzit, kdyz prave nevim neznam ten druhy parametr maxlen?
karlos15 22.12.2012 14:03
karlos15
Druhý parameter ale musíš poznať, pretože na reťazec si rezervoval nejakú pamäť, ktorá má svoju veľk…
los 22.12.2012 18:41
los
A v tom pripade nerozumim tomu, ze tento kod: char *p = (char *) malloc(sizeof(char) * 10); strncpy…
karlos15 22.12.2012 14:16
karlos15
Prečítaj si ešte raz, čo som napísal o nevkladaní ukončovacieho znaku. Nauč sa pracovať s dokumentác… poslední
los 22.12.2012 18:41
los

Smerník r ukazuje po priradení toho reťazca do pamäte, v ktorej je uložený reťazec "no nazdarek". Táto pamäť je pripravená ešte pred samotným spustením funkcie main. Takže odpoveď je taká, že alokovať pamäť pre premennú r nemusíš preto, lebo to za teba spravil už niekto iný.

Smerník p ukazuje po priradení výsledku funkcie malloc na začiatok pamäte veľkosti 1 bajt. Samozrejme, že to nestačí na to, aby si tam narval 12 bajtov pomocou funkcie strcpy. Stane sa jednoducho to, že sa prepíše aj pamäť za miestom alokovaným funkciou malloc. V prípade, že do tohto miesta môžeš zapisovať, tak program nespadne hneď. Ale tým, že si prepíšeš iné dáta, je pravdepodobné, že sa to zosype neskôr. Takýmto spôsobom si môžeš prepísať nielen iné dáta, ale aj napr. kód programu, čím môžeš šikovnému útočníkovi umožniť vykonávať ľubovoľný kód v rámci tvojho procesu.

Funkcia strcpy dáva na koniec aj znak '\0', takže bude na tom 12-tom mieste. Mimochodom, bezpečnejšie je v takýchto prípadoch používať funkciu strncpy, kde môžeš určiť aj maximálnu dĺžku kopírovaného reťazca. V programe ti chýba jedno volanie free, aj keď v tomto konkrétnom prípade je to jedno.

Len pre úplnosť, pri alokovaní pomocou funkcie malloc v jazyku C nemusíš pretypovať výsledok na smerník konkrétneho typu. Keď to ale kompiluješ kompilátorom C++, tak to tam nechaj.

Ok, takze vzdy, kdyz potrebuju zkopirovat retezec nekam jinam, musim nejdriv naalokovat pamet o znak navic, aby se tam vesel pri kopirovani znak '\0', ktery tam sama vlozi strcpy, pocitam, ze ho vam vlozi i strncpy.

Ano a to jsem se take chtel zeptat, free volam pro pointery, do kterych sem ulozil adresu pouze funkci malloc?
Cili:

r = "no nazdarek";
p = (char *) malloc(sizeof(char));

free(p);     //SPRAVNE - protoze adresa byla ziskana mallocem?
p = NULL;    //SPATNE - protoze ????
free(r);     //SPATNE - protoze adresa nebyla ziskana mallocem?
r = NULL;    //SPRAVNE - protoze ???

Diky za ochotu!

Funkcia strncpy tam ten ukončovací znak nevloží, ak je kopírovaný reťazec dlhší. Len zabezpečí, aby sa neprekopírovalo viac znakov než zadáš.

V manuáli si môžeš prečítať, že funkciu free môžeš zavolať na výsledkoch funkcie malloc, calloc alebo realloc. V opačnom prípade, alebo v prípade ak si už free zavolal, je správanie programu nedefinované. Ak zavoláš free na NULL, nič sa nevykoná.

Takže áno, prvé volanie free(p) je správne, pretože adresa bola získaná malloc-om. Po tomto volaní smerník p ukazuje na miesto, ktoré bolo uvoľnené. Ak po tomto volaní priradíš do smerníka p hodnotu NULL, tak stratíš odkaz na to uvoľnené miesto, čo je v poriadku.

Ak by si však predtým nezavolal free a rovno priradil NULL, tak by to už v poriadku nebolo. Stratil by si totiž možnosť tú pamäť uvoľniť. Nič strašné by sa nemuselo stať, pretože po ukončení procesu ju operačný systém uvoľní za teba. Ak však program alokuje pamäť bez jej uvoľňovania, tak sa môže stať, že ju celú postupne vyčerpá.

Volanie free(r) je nesprávne, pretože o pamäť pre "no nazdarek" sa stará niekto iný. Navyše, tieto statické reťazce sa môžu alokovať v pamäti určenej len na čítanie, takže nie len že tú pamäť neuvoľníš, ale nebudeš do nej vedieť ani zapísať (závisí od prekladača).

Priradenie NULL do r je v poriadku. Stratíš síce odkaz na "no nazdarek", ale ak ti to nevadí, tak je to ok. V tomto prípade k žiadnemu úniku pamäte nedojde.

Diky!
A kdyz zavolam free(p): free uvolni pamet, ale p porad ukazuje na uvolnene misto. Musim do "p" jeste ulozit NULL kvuli dalsi nove alokaci? Protoze v mym programu v dalsim prubehu cyklu znovu volam malloc, ktera ne vzdy alokuje stejne mnozstvi pameti...
Da se tedy rict, ze nasledujici kod je v poradku?

fgets(nacteno, MAX_DELKA_VSTUPU, soubor);
if(strlen(nacteno) == 1) {
	printf("konec.");
	break;
}
nacteno[strlen(nacteno)-1] = '\0';
radka = (char *) malloc(sizeof(char) * (strlen(nacteno) + 1));
strncpy(radka, nacteno, strlen(nacteno));
zpracuj(radka);
free(radka);

Eclipse na C? Eclipse je sotva dobrý na Javu... Použi radšej niečo kvalitné, ako napr. Visual Studio alebo Qt Creator.

Na čo by ti bolo vidieť znak \0? Keď vidíš v debuggeri reťazec, tak je určite ukončený nulovým znakom. Také niečo určite nenájdeš v žiadnom rozumnom IDE. Určite si niekde môžeš pozrieť výpis pamäte, tam uvidíš nielen \0, ale aj to, čo je za tým.

Čo znamená tá podmienka "if(strlen(nacteno) == 1)"? Dúfam, že znak '\0' nepočítaš do dĺžky reťazca.

Prečo potrebuješ skrátiť hodnotu v nacteno o jeden znak? (nacteno[strlen(nacteno)-1] = '\0';)

Keď už toľko pracuješ s hodnotou strlen(nacteno), bolo by celkom dobré si ju uložiť do premennej.

Toto je blbosť:

strncpy(radka, nacteno, strlen(nacteno));

To by si už rovno mohol použiť strcpy. Keď alokuješ presne toľko miesta, koľko ideš kopírovať, tak nemá zmysel použiť strncpy. Alebo pre istotu tam dať tú MAX_DELKA_VSTUPU (predpokladám, že pre premennú nacteno máš alokovaných toľko bajtov).

Nepocitam, jelikoz fgets nacte radku i s '\n', takze v retezci je "\n\0", coz je dlouhy prave 1, proto konec souboru
A tohle: nacteno[strlen(nacteno)-1] = '\0'; mi prave prepise '\n' na '\0'...
Ano, ted uz vim, ze je to blbost, uz jsem to zmenil.

Muzu se zeptat, umis pracovat s valgrindem? Rozjel jsem si ho v eclipsu, neco jsem zvladnul opravit, ale u neceho jsem ztraceny...
A o prispevek vys, nevis, jestli jde v nejakem editoru v debuggeru zobrazovat retezec i s '\0' na konci?

Dekuju!

A jestli mi muzes prosim poradit jeste:
Jak je to s "freeovanim" pointeru p, ktery vznikl jako: char p[velikost];, kde velikost se vypocita v prubehu?
Muzu na to pouzit realloc?
Je unik pameti pocin p = NULL?
A pro upresneni, realloc funguje tak, ze pokud udelam char *p = malloc(sizeof(char) * 4), tak mi to naalokuje misto pro 4 znaky, pokud udelam p = realloc(p, sizeof(char) * 2), tak to urizne polovinu mista? co s datama, ktera tam byla ulozena? pokud uz je nepotrebuju, tak nevadi, ze k nim neni pristup, ne?

Pole char p[velikost] je alokované na stacku, takže po opustení rozsahu platnosti sa automaticky uvoľní. Keď pracuješ len na stacku, tak žiaden memory leak nenastane.

Počin "p = NULL" znamená únik pamäte len vtedy, ak si tým stratil akúkoľvek referenciu na alokovanú pamäť. Rovnako aj "p = malloc(...)" znamená únik pamäte, ak si tam mal aj predtým alokovanú pamäť (a tiež už nemáš žiaden iný smerník na tú pamäť, pomocou ktorého by si ju uvoľnil).

Áno, realloc ureže polovicu miesta. Kľudne môže alokovať novú pamäť a prekopírovať do nej polovicu toho poľa. Dáta, ktoré tam boli uložené, zostanú v pamäti, ale ty k nej už prístup mať nebudeš. Keď alokuješ novú pamäť, tak sa môžeš (ale nemusíš) k tým dátam dostať. Závisí od konkrétnej implementácie prekladača, jeho nastavení a knižníc (napr. v debug móde sa pamäť prepíše, v release móde nie a pod.). Keď už dáta nepotrebuješ, tak samozrejme nevadí, keď k nim už nebudeš mať prístup.

Tak sem se hnul dal a vyladuju uniky pameti valgrindem.
Potreboval jsem si napsat metodu, ktera mi overi, ze na konci retezce je ukoncovaci znak, je vubec napsana spravne? protoze mi to nejak nesedi:

void obsahuje_ukoncovaci_znak(char *p){
	int delka = strlen(p);
	if(strncmp(p+delka,"\0",1) == 0) printf("RETEZEC: %s obsahuje ukoncovaci znak\n\n",p);
	else printf("RETEZEC: %s NEobsahuje!!! ukoncovaci znak\n\n",p);
}

nebo tato?

void obsahuje_ukoncovaci_znak(char *p){
	int delka = strlen(p);
	if(p[delka] == '\0') printf("RETEZEC: %s obsahuje ukoncovaci znak\n\n",p);
	else printf("RETEZEC: %s NEobsahuje!!! ukoncovaci znak\n\n",p);
}

Obe metody mi vraceji stejne vypisy, akorat mi valgrind porad na nekolika radkach pise, ze nekde ctu o 1 bajt mimo alokovany blok.
Diky

Ani jedna metóda nie je správne, pretože obidve sú zbytočné. Funkcia strlen určí dĺžku reťazca práve podľa ukončovacieho znaku, takže nemá zmysel testovať, či je na konci reťazca ukončovací znak, pretože tam bude vždy.

Môžeš použiť funkciu strnlen, potom by mala kontrola ukončovacieho znaku aký-taký zmysel. Efektívnejší spôsob by bol potom ten druhý, pretože tam priamo porovnávaš znak namiesto ďalšieho volania funkcie. V tom prvom spôsobe bije do očí ten reťazec "\0", tam stačí predsa prázdny reťazec.

Normálne sa to ale robí tak, že ten ukončovací znak zapíšeš na posledné miesto vyhradenej pamäte a takéto veci potom nemusíš riešiť.

Druhý parameter ale musíš poznať, pretože na reťazec si rezervoval nejakú pamäť, ktorá má svoju veľkosť - takže sa ti tam vojde reťazec s nejakou konkrétnou maximálnou dĺžkou.

Neviem, na akej úrovni chceš programovať, ale ak len nejaké zadania do školy, tak kľudne môžeš zabudnúť na všetky bezpečné funkcie typu strnlen, strncpy, strncmp a pod., tam ti chýbať nebudú.

A v tom pripade nerozumim tomu, ze tento kod:

char *p = (char *) malloc(sizeof(char) * 10);
strncpy(p,"nazdarek, jak je?",5);
int delka_p = strlen(p);
printf("delka p: %d\n",delka_p);

...vraci: delka p: 5,
kdyz jsem zkopiroval jenom 5 znaku BEZ '\0' a ty jsi psal, ze strncpy NEvklada ukoncovaci znak.

Zpět do poradny Odpovědět na původní otázku Nahoru