Eli tehtävän anto on: Kirjoita funktio, joka etsii merkkijonosta tiettyä merkkiä.
Funktiolle annetaan parametreina merkkijono ja merkki. Funktio palauttaa arvonaan osoittimen merkkijonon ensimmäiseen sellaiseen merkkiin, jota etsitään. Ellei etsittävää merkkiä löydy, funktio palauttaa arvon NULL.
Kirjoita funktiolle myös käyttöympäristö eli pääohjelma, josta kutsut sitä.
--------------------------------
Ongelmani on että en ymmärrä täysin tuota muistinvarausta. Eli kun käännän seuraavan ohjelman gcc:llä linuxissa, niin ohjelma kääntyy, mutta ohjelmaa ajettaessa tulee ilmoitus "muistialueen ylitys". Tämä siis tulee heti kun olen syöttänyt kysyttävän merkin.
Löytyisikö joltakin ystävälliseltä sielulta apuja, että tietäisin missä kohdassa koodiani mennään metsään? Kiitos.
Tässä yritelmäni:
#include
#include
#define MAX 20
char *merkkijono(char *mjono, char merkki);
int main(void){
char mj[MAX 1];
char m,*ptr;
printf("Anna merkkijono (max %d merkkiä): ",MAX);
fgets(mj,MAX 1,stdin);
mj[strlen(mj)-1]='\0';
printf("Anna merkki: ");
scanf("%c",&m);
ptr=(char *)malloc( sizeof (char) );
ptr=merkkijono(mj,m);
printf("\nMerkkijono: %s\n",*ptr);
free(ptr);
return 0;
}
char *merkkijono(char *mjono, char merkki){
while ((*mjono != '\0') && (*mjono != merkki )){
mjono ;
}
if (*(mjono) == '\0') { return NULL; }
else{
return mjono;
}
}
Funktiolle annetaan parametreina merkkijono ja merkki. Funktio palauttaa arvonaan osoittimen merkkijonon ensimmäiseen sellaiseen merkkiin, jota etsitään. Ellei etsittävää merkkiä löydy, funktio palauttaa arvon NULL.
Kirjoita funktiolle myös käyttöympäristö eli pääohjelma, josta kutsut sitä.
--------------------------------
Ongelmani on että en ymmärrä täysin tuota muistinvarausta. Eli kun käännän seuraavan ohjelman gcc:llä linuxissa, niin ohjelma kääntyy, mutta ohjelmaa ajettaessa tulee ilmoitus "muistialueen ylitys". Tämä siis tulee heti kun olen syöttänyt kysyttävän merkin.
Löytyisikö joltakin ystävälliseltä sielulta apuja, että tietäisin missä kohdassa koodiani mennään metsään? Kiitos.
Tässä yritelmäni:
#include
#include
#define MAX 20
char *merkkijono(char *mjono, char merkki);
int main(void){
char mj[MAX 1];
char m,*ptr;
printf("Anna merkkijono (max %d merkkiä): ",MAX);
fgets(mj,MAX 1,stdin);
mj[strlen(mj)-1]='\0';
printf("Anna merkki: ");
scanf("%c",&m);
ptr=(char *)malloc( sizeof (char) );
ptr=merkkijono(mj,m);
printf("\nMerkkijono: %s\n",*ptr);
free(ptr);
return 0;
}
char *merkkijono(char *mjono, char merkki){
while ((*mjono != '\0') && (*mjono != merkki )){
mjono ;
}
if (*(mjono) == '\0') { return NULL; }
else{
return mjono;
}
}
Vastaukset 12
Vanhimmat
Hyvä et laitoit koko koodin. Onkin helppo sanoa virhekohta hirveesti arvaamatta.
Viimeisessä tulostuksessa ei saisi käyttää osoittimen osoittamaa arvoa vaan itse osoitinta:
printf("\nMerkkijono: %s\n",ptr);
Toiseksi määrittelit ptr -osoittimelle arvon kahdesti:
ptr=(char *)malloc( sizeof (char) );
ptr=merkkijono(mj,m);
jolloin ptr:n aiempi sisältö poistuu ja näin ollen lopussa ollut free(ptr) ei toimi. Poista siis malloc ja free -rivit.
>Viimeisessä tulostuksessa ei saisi käyttää >osoittimen osoittamaa arvoa vaan itse osoitinta:
>printf("\nMerkkijono: %s\n",ptr);
Eli onko tämä koska merkkijonotaulukkokin on itsessään vaan osoitin taulukon alkuun?
Niin ja sitten vielä ymmärsin etten ole tuota malloc() funktiota ymmärtänyt koska tässä sitä turhaan käytin. Eli minkälaisissa tilanteissa tuota muistia on varattava?
Kiitos vielä paljon!
silloin kun käsitellään suuria märiä tietoa.
printf:n %s -parametri tarvitsee osoitinta merkkijonoon (ptr). %c -parametrilla tulostettaisiin yksittäinen merkki (*ptr).
Sulla oli siinä mallocissa varattavana vain yksi tavu. Normaalisti miten mä varailen muistia:
- jos taulukon koko on ennakkoon tiedossa ja koko on korkeintaan pari sataa kilotavua (ja ohjelmassa on alustamattomia arvoja alle ½ megaa)
· pienet varaukset funktion sisällä (pinomuisti) tai seuraavan kohdan mukaisesti
· suuret varaukset ja sellaiset jotka on alustettava nollaksi, käyttäen static -valintaa (data -osio)
- jos tarvittavan tiedon koko ei ole tiedossa, käyttäen mallocia vastaavaa käskyä. Esimerkkinä tiedoston luku muistiin
Koska teen tällä hetkellä pelkästään winkkarille, en käyttele mallocia, vaan vastaavaa HeapAllocia (malloc käyttää HeapAlloc -funktiota). Jos tiedosto on suurempi kuin 500 kiloa, täytyy muistinhallintaa mietiskellä uudestaan. Tällöin voisi käytellä suurilla tiedostoilla CreateFileMapping -funktiota.
Kun muistinvarauksen tekee ohjelmassa, tulisi välttää alustettujen arvojen kohdalla varaamasta hirveen isoo klönttiä muistia. Esim. char tiedosto[10240]="c:\\hakemisto\\" sijoitettuna funktioiden ulkopuolelle, vie exe -tiedostosta 10 kilotavua tilaa (loppu teksti on alustettu nolliksi). Jos määrittely on taas funktion sisällä, ohjelman alussa varataan muistia pinosta ja alustettu osa (c:\hakemisto\) siirretään data- osiosta varatun muistin alkuun (loppu muisti on sattumanvaraisia numeroita).
Vuosia sitten, kun tein yhtä dll:n ensimmäistä versiota, opin kantapään kautta mihin alustetut arvot kuuluvat. Tuli meinaan laitettua funktion sisään ilman mitään const -parametrejä, isoja liukulukutaululukoita. Kun siirsin arvot funktion ulkopuolelle, koko pieneni sadoilla kilotavuilla ja ohjelma nopeutui huomattavasti. Funktion sisällä oleviin taulukoihin arvot siirretään koodissa. Liukuluvuilla siirto tapahtuu vieläpä arvo kerrallaan.
mj[strlen(mj)-1]='\0';
Eli etsi merkkijonosta '\0' ja sijoita siihen kohtaan '\0'.
strlen toimii siten että se laskee merkkejä siihen asti että se törmää loppumerkkiin '\0'.
ps. mallocin sijaan käyttäisin callocia, joka varaa muistin ja alustaa sen nollalla.
koska fgets() funktio lukee stdin:ä niin mukaan tulee rivinvaihtomerkki. Eli siitä yritin päästä eroon.. olisiko se sitten pitänyt olla:
mj[strlen(mj)-2]='\0';
Joko
- poista koko rivi mj[strlen(mj)-1]='\0';
- tai muuta aikaisemmat char mj[MAX 2]; fgets(mj,MAX 2,stdin);
Eli ilmeisesti tuo fgets ottaa enterin painalluksen yhdeksi merkiksi. Eli jos siis syötän kissa, niin minulla on tällainen merkkijonotaulukko:
0:k 1:i 2:s 3:s 4:a 5:\n 6:\0
eli eikös tämä minun alkuperäinen rivini:
mj[strlen(mj)-1]='\0';
toimi juuri niin että se poistaa tuon enterin painalluksen eli rivinvaihdon ja korvaa sen loppumerkillä '\0'? tätä ajoin tuossa takaa.
Riippuen onko harkkatyöhön automaattitarkastin, joka katsoo rivivaihdot, jolloin rivivaihto täytynee poistaa. Jos ei niin, ei tuo rivinvaihto pahalta näytä.
Jos päätät poistaa rivinvaihdon, niin sulla oli alussa tekstinä max 20 merkkiä. Tällöin se ei pidä paikkaansa, vaan joudut muuttamaan ne kaksi riviä.
joo eli se MAx 2 taulukon kooksi.
Tämä alkaa mennä jo vähän naurettavaksikin kysellä kyseisestä asiasta näin paljon, mutta haluan tämän ymmärtää perusteellisesti. Toivottavasti ymmärrystä riittää siellä päässä :)
Eli siis jos mulla on kaksi merkkijonoa esim vaikka
mj='kissa'
mj2='kala'
niin sitten kun yhdistän nuo ja en ole poistanut sitä rivinvaihtomerkkiä, niin eikö se haittaa jos minulla on kissa\nkala eikös tuo tuotos ole tavallaan väärin? tietenkin riippuu mitä haetaan, mutta jos pyritään yhdistämään vaikka kaksi nimeä tms...
Oot ymmärtäny täysin oikein. Jos ei tajuu jotain, niin on todellakin hyvä kysellä. C:ssä jos perusasioita ei tajua, ohjelmointi ei avaudu ja varmasti tympäsee.
Tottakai jos halutaan yhdistää 'Etunimi' ja 'Sukunimi', niin tarvitaan jokin erotinmerkki. Jos tehtäisiin ohjelmaa Windowsin graaffiseen käyttöliittymään, rivinvaihtomerkki saattaisi näkyä laatikkomerkkinä, jolloin voisi sanoa, että '\n' -merkkiä ei saa käyttää.
Sun esimerkissäs toi 'kissakala' ei tarvii erotinmerkkiä, jolloin mahdolliset rivinvaihdot on poistettava.
Okei, eiköhän asia mennyt nyt aika hyvin jakeluun. Kiitos vielä paljon sulle vaivannäöstä!