Forum » Programiranje » [C/C++] pointerji in reference
[C/C++] pointerji in reference
DejaVu ::
zdaj sem ze tako dalec s programiranje v C/C++ pa mi se vedno niso najbolj jasni pointerji in reference. sem gledal nekaksne tutoriale o teh pointerjih, vendar edino kar vem je to, da pointer kaze na naslov memorija, kjer se vsebina spremenljivke nahaja. do zdaj sem vedno pisal vse tak bolj po izkusnjah, brez da bi dejansko vedo sploh, zakaj mora bit pointer ali pa referenca. zelo bi bil hvalezen, ce ma kdo dober tutorial, da ti popolnoma razjasni to zadevo.
no zdaj pa k problemu.
imam neko listo besed, ki je z zacetka prazna:
char *listabesed[];
kako lahko v to listo vstavim besede, ki se recimo preberejo iz enega fajla. jaz bi to naredil tako:
for (int x = 0; x < 10; x++) {
fscanf (pFile, "%s", buffer);
sprintf (listabesed[x], buffer);
}
skompila se, vendar potem med zagonom program javi napako. ocitno je nekaj narobe in mi manjka kje kak casting al pa referenca/pointer.
hvala za pomoc.
no zdaj pa k problemu.
imam neko listo besed, ki je z zacetka prazna:
char *listabesed[];
kako lahko v to listo vstavim besede, ki se recimo preberejo iz enega fajla. jaz bi to naredil tako:
for (int x = 0; x < 10; x++) {
fscanf (pFile, "%s", buffer);
sprintf (listabesed[x], buffer);
}
skompila se, vendar potem med zagonom program javi napako. ocitno je nekaj narobe in mi manjka kje kak casting al pa referenca/pointer.
hvala za pomoc.
- spremenilo: snow ()
64202 ::
Pointerjev se boš najbolje naučil, če na roke po c-jevsko sprogramiraš linked listo z malloci, pa po možnosti še potem binarno drevo, kar je standard po srednjih šolah / faxih. Malo poglej po forumu. Brez tega boš zelo bos v c/c++.
Torej kar pa gori počneš, je c-jevska zguba časa, razen če veš kaj delaš . Raje uporabiš taprav c++:
Torej kar pa gori počneš, je c-jevska zguba časa, razen če veš kaj delaš . Raje uporabiš taprav c++:
std::vector<std::string> vrstice; std::ifstream fajl("input.txt"); if(fajl) { std::string vrstica; while(std::getline(fajl, vrstica)) vrstice.push_back(vrstica); }
I am NaN, I am a free man!
64202 ::
To se mi zdi fajno: Essential C, potem pa izberi še pointers & memory, ter linked list, itd.
I am NaN, I am a free man!
jernejl ::
Predvidevam, da si med deklaracijo char *listabesed[] in for-zanko rezerviral dovolj prostora v pomnilniku?
(z ukazom new v c++, oziroma po C-jevsko z malloc oziroma calloc, ker gre za array)
(z ukazom new v c++, oziroma po C-jevsko z malloc oziroma calloc, ker gre za array)
Gundolf ::
pointer je kot vsaka druga spremenljivka. Le da si lahko njegovo vrednost razlagamo kot naslov v pomnilniku (torej si spremenljivko, ki je pointer, lahko predstavljaš kot neko število). Potem je tu še tip pointerja, ki to pravzaprav ni ampak je tip spremenljivke, ki se nahaja na naslovu, ki je zapisan v pointerju. To je najbolj enostavno razloženo, kar si zdajle spomnim, za tutorial pa res nebi vedel.
Glej tvoj problem, preveden tako kot sem zgoraj skušal pojasniti pointerje:
ti imaš char *listabesed[];
to je enako **listabesed;
Tabele so namreč navadni pointerji. Sploh take nedoločene velikosti so ekvivalentne pointerjem v vseh pogledih.
Kaj je torej listabesed? Glej zgoraj - eno število. V spremenljivki je zapisano le eno število. Ker tule delaš z navadnimi pointerji je to število (ker spremenljivka ni initializirana - nisi ji določil nobene vrednosti) karkoli. Pointer torej 'kaže' kamorkoli v spominu (ne veš kam). Ko rečeš *listabesed (ali listabesed[0], to dvoje je namreč spet ekvivalento) naznaniš prevajalniku, da delaš s spremenljivko, ki je na točno na naslovu, ki ga program lahko prebere iz spremenljivke listabesed (če rečeš listabesed[1] bo prevajalnik vedel da mora na podanem naslovu prvo spremenljivko preskočiti, podobno velja za višje številke). Ampak ker je vsebina listabesed neznana, bo program na naslovu, ki je enak te vsebini, zelo težko našel kako spremenljivko. Pravzaprav bo ugotovil da je naslov (razen če imaš zelo zelo veliko 'srečo') tak, ki je njemu prepovedan in bo storil samomor. Zakaj program in ne prevajalnik (drugače rečeno, zakaj se program prevede)? Prevajalnik ne pozna vsebine tvoje spremenljivke vnaprej. Kolikor se njega tiče mora verjeti, da bo vsebina veljaven naslov. V najboljšem primeru ti lahko vrne warning, da uporabljaš neinitializirano spremenljivko.
Kako initializirat pointer? Več možnosti imaš. Prva je da uporabiš tabelo določene velikosti:
char toJePointer[10];
Tako dobiš pointer, ki že po defaultu kaže na prvo od desetih zaporednih spremenljivk (tipa char v tem primeru). Tak pointer je tudi nezahteven za oskrbo, ker ga recimo ni treba explicitno ubiti, ko ga ne rabiš več. Ima pa slabost - velikost tabele moraš vedeti v naprej - velikost torej ne more biti spremenljivka. To pomeni, da ne moreš recimo najprej iz fajla prebrati dolžine fajla, nato pa ustvariti tabelo take dolžine na ta način, da bi lahko vanjo prebral ostanek fajla.
Druga možnost je, da vanj zapišeš naslov neke spremenljivke ali konstante:
char a;
char *pointerToA = &a; // naslov katerekoli spremenljivke dobiš z operatorjem &
Tak pointer ti kaže na prej določeno spremenljivko tipa char. Pozor, še vedno imaš le eno spremenljivko tipa char! Dodal si le en način preko karetega lahko dostopaš do nje.
Poseben primer druge možnosti, ki velja le za nize znakov:
char *toJePravTakoPointer = "pointer!!"
Prevajalnik nekam v pomnilnik vrže besedo pointer!!/0 in ti njen naslov (naslov prve črke) takoj zapiše v toJePravTakoPointer.
Tretja možnost je, da v pointer zapišeš naslov kosa pomnilnika ki si ga prav s tem namenom ustvaril:
char *pointerToBlock = new char[10] // c++ način
char *pointerToBlockC = (char*)malloc(10); // c način
Tako narediš 10 zaporednih spremenljivk brez imena nekje v tvojem spominu. Ampak to da so brez imena (in jih zato ne moreš klicat direktno po imenu) kompenziraš s tem, da naslov prve zapišeš v tvoj pointer, in lahko torej do njih dostopaš preko tega pointerja. V bistvu ustvariš tabelo nekje v pomnilniku. V primerjavi s prvo možnostjo ima ta prednost, da je lahko velikost tabele spremenljivka. Slabost je pa (poleg relativne počasnosti) ta, da moraš kos pomnilnika na koncu explicitno ubiti (s tem da prevajalniku poveš naslov od prve spremenljivke v tem kosu):
delete pointerToBlock; // c++
free(pointerToBlockC); // c
Ta pointer ti je služil le kot kos pomnilnika, v katerem si imel shranjen naslov. Pomembno je da veš, da moraš ubiti/sprostiti kos pomnilnika, ki si ga zasedel, in ne pointerjev.
Sem že predolg, da bi se spustil v dvojne pointerje/pointerje na tabele/tabele pointerjev itd ali reference.
Glej tvoj problem, preveden tako kot sem zgoraj skušal pojasniti pointerje:
ti imaš char *listabesed[];
to je enako **listabesed;
Tabele so namreč navadni pointerji. Sploh take nedoločene velikosti so ekvivalentne pointerjem v vseh pogledih.
Kaj je torej listabesed? Glej zgoraj - eno število. V spremenljivki je zapisano le eno število. Ker tule delaš z navadnimi pointerji je to število (ker spremenljivka ni initializirana - nisi ji določil nobene vrednosti) karkoli. Pointer torej 'kaže' kamorkoli v spominu (ne veš kam). Ko rečeš *listabesed (ali listabesed[0], to dvoje je namreč spet ekvivalento) naznaniš prevajalniku, da delaš s spremenljivko, ki je na točno na naslovu, ki ga program lahko prebere iz spremenljivke listabesed (če rečeš listabesed[1] bo prevajalnik vedel da mora na podanem naslovu prvo spremenljivko preskočiti, podobno velja za višje številke). Ampak ker je vsebina listabesed neznana, bo program na naslovu, ki je enak te vsebini, zelo težko našel kako spremenljivko. Pravzaprav bo ugotovil da je naslov (razen če imaš zelo zelo veliko 'srečo') tak, ki je njemu prepovedan in bo storil samomor. Zakaj program in ne prevajalnik (drugače rečeno, zakaj se program prevede)? Prevajalnik ne pozna vsebine tvoje spremenljivke vnaprej. Kolikor se njega tiče mora verjeti, da bo vsebina veljaven naslov. V najboljšem primeru ti lahko vrne warning, da uporabljaš neinitializirano spremenljivko.
Kako initializirat pointer? Več možnosti imaš. Prva je da uporabiš tabelo določene velikosti:
char toJePointer[10];
Tako dobiš pointer, ki že po defaultu kaže na prvo od desetih zaporednih spremenljivk (tipa char v tem primeru). Tak pointer je tudi nezahteven za oskrbo, ker ga recimo ni treba explicitno ubiti, ko ga ne rabiš več. Ima pa slabost - velikost tabele moraš vedeti v naprej - velikost torej ne more biti spremenljivka. To pomeni, da ne moreš recimo najprej iz fajla prebrati dolžine fajla, nato pa ustvariti tabelo take dolžine na ta način, da bi lahko vanjo prebral ostanek fajla.
Druga možnost je, da vanj zapišeš naslov neke spremenljivke ali konstante:
char a;
char *pointerToA = &a; // naslov katerekoli spremenljivke dobiš z operatorjem &
Tak pointer ti kaže na prej določeno spremenljivko tipa char. Pozor, še vedno imaš le eno spremenljivko tipa char! Dodal si le en način preko karetega lahko dostopaš do nje.
Poseben primer druge možnosti, ki velja le za nize znakov:
char *toJePravTakoPointer = "pointer!!"
Prevajalnik nekam v pomnilnik vrže besedo pointer!!/0 in ti njen naslov (naslov prve črke) takoj zapiše v toJePravTakoPointer.
Tretja možnost je, da v pointer zapišeš naslov kosa pomnilnika ki si ga prav s tem namenom ustvaril:
char *pointerToBlock = new char[10] // c++ način
char *pointerToBlockC = (char*)malloc(10); // c način
Tako narediš 10 zaporednih spremenljivk brez imena nekje v tvojem spominu. Ampak to da so brez imena (in jih zato ne moreš klicat direktno po imenu) kompenziraš s tem, da naslov prve zapišeš v tvoj pointer, in lahko torej do njih dostopaš preko tega pointerja. V bistvu ustvariš tabelo nekje v pomnilniku. V primerjavi s prvo možnostjo ima ta prednost, da je lahko velikost tabele spremenljivka. Slabost je pa (poleg relativne počasnosti) ta, da moraš kos pomnilnika na koncu explicitno ubiti (s tem da prevajalniku poveš naslov od prve spremenljivke v tem kosu):
delete pointerToBlock; // c++
free(pointerToBlockC); // c
Ta pointer ti je služil le kot kos pomnilnika, v katerem si imel shranjen naslov. Pomembno je da veš, da moraš ubiti/sprostiti kos pomnilnika, ki si ga zasedel, in ne pointerjev.
Sem že predolg, da bi se spustil v dvojne pointerje/pointerje na tabele/tabele pointerjev itd ali reference.
DejaVu ::
hmm ja hvala za razlago. se najbolj mikavn je ta zadnji primer, vendar pri meni ne pride v upostev, ker bo program imel tudo po 200+ threadov in vsi bojo manipulirali z listo in dostopali precej pogosto. ze zdaj ko sem testiral z napisano listo v samem programu je bil cpu usage precej visok.
ce definiram *listabesed[10], stvar vseeno ne dela.
dvojni pointerji, daj prosim, povej se kaj o tem :) mogoce je v tem resitev mojega problema.
ce definiram *listabesed[10], stvar vseeno ne dela.
dvojni pointerji, daj prosim, povej se kaj o tem :) mogoce je v tem resitev mojega problema.
Vesoljc ::
>> ker bo program imel tudo po 200+ threadov in vsi bojo manipulirali z listo in dostopali precej pogosto.
ouch!
bad design imho :)
mocno upam, da poznas izraz thread safety
ouch!
bad design imho :)
mocno upam, da poznas izraz thread safety
Abnormal behavior of abnormal brain makes me normal...
Matako ::
Težava je, da nikjer ne alociraš pomnilnika - program se ti bo skoraj sigurno sesul.
No tukaj je, samo za okus, delujoč ANSI C primer - uporablja polje 10 kazalcev (ker kao beremo 10 nizov - akr nekaj). Za vsako besedo alocira s strdup() ravno prav prostora (upam!), vendar kvečjemo 64 bytov - kar je več se odreže, glej format string v fscanf().
Ta primer je tipičen primer kompromisa dinamično-statično alociranje. Fiksne velikosti je namreč polje *kazalcev* kar dostikrat nanese ceneje kot pa recimo dinamično polje *fiksnih* stringov. Najbolje je verjetno kar oboje.
PS. Mter, kako se vstavlja koda dobesedno tukaj?
[edit - tag "st.koda c" - vsc]
No tukaj je, samo za okus, delujoč ANSI C primer - uporablja polje 10 kazalcev (ker kao beremo 10 nizov - akr nekaj). Za vsako besedo alocira s strdup() ravno prav prostora (upam!), vendar kvečjemo 64 bytov - kar je več se odreže, glej format string v fscanf().
char* besede[10]; int main() { char buf[64]; int i; for (i = 0; i < 10; i++) { fscanf (stdin, "%64s", buf); besede[i] = strdup(buf); } // sprosti pomnilnik ... }
Ta primer je tipičen primer kompromisa dinamično-statično alociranje. Fiksne velikosti je namreč polje *kazalcev* kar dostikrat nanese ceneje kot pa recimo dinamično polje *fiksnih* stringov. Najbolje je verjetno kar oboje.
PS. Mter, kako se vstavlja koda dobesedno tukaj?
[edit - tag "st.koda c" - vsc]
/\/\.K.
Zgodovina sprememb…
- spremenil: Vesoljc ()
DejaVu ::
aha, bom probal se to ja. no saj besede ki se preberejo bi naj bile maksimalne velikosti 8 charov, tako da...
Vredno ogleda ...
Tema | Ogledi | Zadnje sporočilo | |
---|---|---|---|
Tema | Ogledi | Zadnje sporočilo | |
» | [C] struct in int[] (strani: 1 2 )Oddelek: Programiranje | 7365 (6438) | MrBrdo |
» | Program ne deluje več (strani: 1 2 )Oddelek: Programiranje | 6607 (5345) | Genetic |
» | C osnovaOddelek: Programiranje | 1559 (1074) | RunoTheDog |
» | Pomoc v C-juOddelek: Programiranje | 1308 (1220) | rfmw |
» | Pointer-ji v C-juOddelek: Programiranje | 1784 (1482) | rokpok |