Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Struktuuriin tallantamisessa ongelma

woodoo [06.04.2010 09:30:15]

#

Kieli olis C ja olen tapellu erään ohjelmani kanssa jo monta tuntia. Noh ei sitten auttanu muu kun luovuttaa ja antaa muitten neuvoa. Enkä tosiaan osaa etsiä virhettä kun en osaa edes käyttää tuota gdb:ta josta olis voinu olla paljonki hyötyä jos sen antamista tuloksia osaisi jotain ymmärtää. (Kehitysympäristönä Dev-C++)

Tuossa ohjelmaani kuuluva funktio jossa luulen virheen olevan ja jos se ei ratkaise ongelmaa yritän etsiä oikean ja kysyn uudelleen:

struct row *Cut_Row(char *str) /*Tällä parsitaan rivi*/
{
char *str2;
struct row *pair;

pair = malloc(sizeof(struct row));
str2 = strtok(str,"=");
pair->word2 = malloc(strlen(str2));
strcpy(pair->word,str2);
str2 = strtok(NULL,"=");
pair->word2 = malloc(strlen(str2));
strcpy(pair->word2,str2);
return pair;
}

Struktuuri oli tällainen:

struct row
{
char *word;
char *word2;
};

Funktion tarkoitus on ottaa osoitin merkkijonon alkuun ja tallentaa dynaamisesti merkkijonosta parsitut palaset struktuuriin.

Joo o. Varsinainen viritys nyt on tuoki funktio mut eipä sitä harjoittelematta ja kysymättä opi joten näin.
Ensin ohjelma kaatuu siihen et msvcrt.dll:ssä tuli ongelma ja sit ku vähän muutan tuota niin bugittaa jälleen ja tulee ntdll.dll ongelma.

GDB valitti ainaki jotain heapista(keosta) eli ongelma on vissiin just dynaamisessa muistinvaaraamisessa ja sit GDB sanoo et SIGTRAP _libmsvcrt_a_iname().
Olen koittanu tökätä free() funkkariaki sinne tänne mut sekään ei ole näyttäny auttavan. Taitaa olla mulla vielä vissiin tuo dynaaminen muistin varaus hukassa tai nuo strtok funkkarit.

Lisää tarkennusta saa kysyä jos tuntuu et tuo ei vielä kerro mitään ongelmasta tai funktio on niin epäselvä.

Gaxx [06.04.2010 12:12:05]

#

Nopeasti katsottuna ongelma näyttäisi olevan yhdessä merkissä. Varaat kaksi kertaa muistia pair->word2:lle, vaikka ensimmäisellä kertaa pitäisi varata pair->word:lle.

Huomaa myös, että strlen() palauttaa nimenomaan tekstin merkkien määrä, jolloin merkkijonon päättävälle nollatavulle ei koodissasi välttämättä aina varata muistia(jos kiinnostaa tietää enemmän, tutustu malloc-funktion sielunelämään). Varattavan muistin määrä tulisi siis olla strlen(str2) + 1.

Legu [06.04.2010 12:44:07]

#

Näiden lisäksi tulee huomioida, että strtok vaatii ensimmäiseksi argumentiksi merkkijonon, jota voi muuttaa (char*, ei siis const char*). Funktiosi määrittelyssä on ihan oikein char* str, mutta kuitenkaan ainakaan GCC ei sano mitään, vaikka kutsu olisi muotoa Cut_Row("Joku=Merkkijono"). Tästä kuitenkin seuraa Segmentation Fault.

Tämä sen sijaan toimii:

char mjono[] = "Joku=Merkkijono";
Cut_Row(mjono);

Aiheesta enemmän esim. täällä.

Vielä yksi virheellinen tapa on, ettei strtok:n palautusarvoja tarkisteta NULL:n varalta, jolloin yhtäsuuruusmerkin puuttuessa voi tapahtua ikäviä asioita.

Metabolix [06.04.2010 14:45:19]

#

Itse toteuttaisin tuon yhdellä malloc-kutsulla, jotta vapauttaminenkin käy helpommin yhdellä free-kutsulla, ja strchr-funktiolla, joka on paljon käytännöllisempi kuin strtok tuossa tilanteessa. Lisäksi tykkään toteuttaa funktiot C:ssä niin, että mahdollisimman suuri osa virhetilanteista tarkistetaan jo ennen malloc-kutsuja ja muita vastaavia, jotka joudutaan aina ennen return-lausetta kumoamaan.

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

struct palat {
	char *nimi, *arvo;
};

struct palat* poikki(const char* teksti) {
	struct palat *p;
	const char* vali;

	/* Tarkistetaan, että tekstissä on '=' joskus alun jälkeen. */
	if (!teksti || !(vali = strchr(teksti, '=')) || vali == teksti) {
		return NULL;
	}

	/* Varataan kerralla sekä rakenne että teksti. */
	if (!(p = malloc(sizeof(*p) + strlen(teksti) + 1))) {
		return NULL;
	}

	/* Kopioidaan teksti paikalleen eli rakenteen perään. */
	p->nimi = (char*)p + sizeof(*p);
	strcpy(p->nimi, teksti);

	/* Lasketaan arvon alku ja lisätään sitä ennen '='-merkin paikalle 0. */
	p->arvo = p->nimi + (vali - teksti) + 1;
	p->arvo[-1] = 0;
	return p;
}

int main(void) {
	struct palat* p = poikki("koe=kaniini");
	if (p) {
		printf("'%s', '%s'\n", p->nimi, p->arvo); /* 'koe', 'kaniini' */
		free(p);
	}
	return 0;
}

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta