GArray* struktúrában hogyan? [MEGOLDVA]

 ( malcolm | 2007. július 8., vasárnap - 21:08 )

Sziasztok!

Úgy rémlik van itt egy-két Gtk+ fejlesztésben jártas ember. A problémám a következő:
Van egy struktúrám, aminek egy eleme GArray* típusú, ami pedig egy másik struktúrából veszi az adatokat.
A fő struktúra feltöltése rendben megtörténik, de mikor átadom egy az adatokat kiírató függvénynek, a GArray*-ból
nem akarja kivenni az adatokat.

Leegyszerűsítettem a kódot. Megnéznétek, hol a hiba?


#include <glib.h>

typedef struct _Fo   Fo;
typedef struct _Resz Resz;

struct _Resz
{
	gint   resz_id;
	gchar *resz_nev;
};

struct _Fo
{
	gint    id;
	gchar  *nev;
	gint    db;
	GArray *reszek;
};

void
kiir (Fo * fo)
{
	gint i;
	
	g_printf("KIIR: \n=====\n");
	g_printf("FO ID: %d\n", fo->id);
	g_printf("FO NEV: %s\n", fo->nev);
	
	for (i=0; i < fo->db; i++)
	{
		Resz *resz = &g_array_index(fo->reszek, Resz, i);
		g_printf("   %d. resz_id: %s\n", i, resz->resz_id);
		g_printf("   %d. resz_nev: %s\n", i, resz->resz_nev);
	}
}

int
main (int argc, char *argv[])
{
	Fo *fo = g_malloc(sizeof(Fo));
	
	fo->reszek = g_array_new(TRUE, TRUE, sizeof(Resz));
	gint i;
	
	fo->id = 1;
	fo->nev = "Elso";
	fo->db = 3;
	
	g_printf("ADATOK:\n=======\n");
	g_printf("FO ID: %d\n", fo->id);
	g_printf("FO NEV: %s\n", fo->nev);
	g_printf("FO RESZEK DARABSZAMA: %d\n", fo->db);
	
	for (i=0; i < fo->db; i++)
	{
		Resz *resz = g_malloc(sizeof(Resz));
		
		resz->resz_id = 10+i;
		resz->resz_nev = g_strdup_printf("%d. resz", i);
		g_array_append_val(fo->reszek, resz);
		g_printf("%d. resz_id: %d\n", i, resz->resz_id);
		g_printf("%d. resz_nev: %s\n", i, resz->resz_nev);
		g_free(resz);
	}
	g_printf("\n");
	kiir (fo);
	g_free(fo);
	return 0;
}

Előre is nagyon köszönöm a segítséget!

Üdv

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Egy valamelyest javított verzió:

#include <glib.h>

typedef struct _Fo   Fo;
typedef struct _Resz Resz;

struct _Resz
{
	gint   resz_id;
	gchar *resz_nev;
};

struct _Fo
{
	gint    id;
	gchar  *nev;
	gint    db;
	GArray *reszek;
};

void
kiir (Fo * fo)
{
	gint i;
	
	g_printf("KIIR: \n=====\n");
	g_printf("FO ID: %d\n", fo->id);
	g_printf("FO NEV: %s\n", fo->nev);
	
	for (i=0; i < fo->db; i++)
	{
		Resz *resz = g_array_index(fo->reszek, Resz*, i);
		g_printf("   %d. resz_id: %d\n", i, resz->resz_id);
		g_printf("   %d. resz_nev: %s\n", i, resz->resz_nev);
	}
}

int
main (int argc, char *argv[])
{
	Fo *fo = g_malloc(sizeof(Fo));
	
	fo->reszek = g_array_new(TRUE, TRUE, sizeof(Resz*));
	gint i;
	
	fo->id = 1;
	fo->nev = "Elso";
	fo->db = 3;
	
	g_printf("ADATOK:\n=======\n");
	g_printf("FO ID: %d\n", fo->id);
	g_printf("FO NEV: %s\n", fo->nev);
	g_printf("FO RESZEK DARABSZAMA: %d\n", fo->db);
	
	for (i=0; i < fo->db; i++)
	{
		Resz *resz = g_malloc(sizeof(Resz));
		
		resz->resz_id = 10+i;
		resz->resz_nev = g_strdup_printf("%d. resz", i);
		g_array_append_val(fo->reszek, resz);
		g_printf("%d. resz_id: %d\n", i, resz->resz_id);
		g_printf("%d. resz_nev: %s\n", i, resz->resz_nev);
		// g_free(resz);
	}
	g_printf("\n");
	kiir (fo);
	g_free(fo);
	return 0;
}

Nagyon szépen köszönöm! Működik.
Egy valamit viszont nem értek. A main() for ciklusában minden körben lefoglal egy memóriarészt a *resz-nek.
Ezt miért nem kell felszabadítani?

Köszi

A ciklisban azért nem kell, mert most a GArray mutatókat tartalmaz, amik a lefoglalt területekre mutatnak.

Persze kilépés előtt fel kell ezeket szabadítani, mint ahogy a GArray-t is...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Kerdes: abbol nem lesz gond, hogy a resznev es a fo.nev is mutato, de nincs neki hely foglalva?

Nincs hely foglalva, mert nem kell, hiszen egy konstans stringre van állítva:

fo->nev = "Elso";

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

(_Nem_ vagyok gtk guru...)

Létrehozol egy GArray-t Resz structokból:

    fo->reszek = g_array_new(TRUE, TRUE, sizeof(Resz));

Majd hozzáadsz egy Resz mutatót:

    Resz *resz = g_malloc(sizeof(Resz));
...
    g_array_append_val(fo->reszek, resz);

Pedig ide nem mutató kéne.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Csekély C tudásomat elővéve sztem de. Mert ha byval adja át a struct-ot akkor a g_array_append nem fogja tudni módosítani, mert csak egy local copy-t kap. Ha yól emléxem azt tanították, hogy minden olyan esetbe ahol a függvényben a bejövö paramétereket akarom módosítani a hívó számára, pointert kell átadnom. Egyedül tömbök esetén nem áll ez.

Majd a C tudósok lehurrognak...

Itt lehet valahol az igazság!

Az igazság az, hogy a kód egy program része. Az említett példa csak egy egyszerű másolata a hibás résznek.
Úgy csináltam meg, hogy önmagában is működjön, de ugyanazokkal a hibákkal.
A hiba pedig az, hogy az adatok kiírásakor a GArray adatainak kivételével mindent szépen visszaad, csak a GArray adatai helyett ad mindenféle memóriából vett karaktersorozatokat.

A jelenlegi helyzet az, hogy a példaprogram módosított változata szépen működik, de az eredeti programom (a példához hasonló felépítésben) nem.

Akkor hogy is van ez a g_array_append_val()?

Köszi a segítséget!

Jelzem _NEM_ értek a C programozáshoz, amit tudok, azt is úgy verték belém.

Amennyire én látom, a g_array_append_val egy void metódus, azaz nincs értelmes visszatérési értéke.
Ebben az esetben a neki átadott GArray-nak pointernek kell lennie, hiszen nem a módosított GArray ojjektummal tér vissza, hanem az eredeti GArray objektumot módosítja
Esetleg megpróbálhatod azt, hogy

g_array_append_val(&fo->reszek, resz);
[code]

vagy

[code]
g_array_append_val((GArray *)fo->reszek, resz);

De télleg nem értek hozzá, lehet hülyeségeket beszélek.

A tömbben vagy Resz típusú elemekre mutató pointereket tárolsz (ilyenre alakítottam át én), vagy Resz típusú elemeket tárolsz.
1. pointer tömb
Ilyenkor g_array_new-nál és a g_array_index-nél is Resz*-ot kell megadni, mivel az elemek pointerek.

2. Resz típusú elemeket tartalmazó tömb
Ilyenkor marad az amit Te is csináltál a g_array_new-nál és g_array_index-nél, azonban a g_array_append_val-ban hibáztál, mivel nem a Resz típusú elem címét kell átadni, hanem magát az elemet.

Beszúrom ide a nem pointeres verziót is. Itt sem törekedtem a tökéletes megoldásra, szóval a helyes felszabadítás most is elmaradt, mert nem volt hozzá kedvem.

#include <glib.h>

typedef struct _Fo   Fo;
typedef struct _Resz Resz;

struct _Resz
{
	gint   resz_id;
	gchar *resz_nev;
};

struct _Fo
{
	gint    id;
	gchar  *nev;
	gint    db;
	GArray *reszek;
};

void
kiir (Fo * fo)
{
	gint i;
	
	g_printf("KIIR: \n=====\n");
	g_printf("FO ID: %d\n", fo->id);
	g_printf("FO NEV: %s\n", fo->nev);
	
	for (i=0; i < fo->db; i++)
	{
		Resz *resz = &g_array_index(fo->reszek, Resz, i);
		g_printf("   %d. resz_id: %d\n", i, resz->resz_id);
		g_printf("   %d. resz_nev: %s\n", i, resz->resz_nev);
	}
}

int
main (int argc, char *argv[])
{
	Resz resz;
	Fo *fo = g_malloc(sizeof(Fo));
	
	fo->reszek = g_array_new(TRUE, TRUE, sizeof(Resz));
	gint i;
	
	fo->id = 1;
	fo->nev = "Elso";
	fo->db = 3;
	
	g_printf("ADATOK:\n=======\n");
	g_printf("FO ID: %d\n", fo->id);
	g_printf("FO NEV: %s\n", fo->nev);
	g_printf("FO RESZEK DARABSZAMA: %d\n", fo->db);
	
	for (i=0; i < fo->db; i++)
	{
		resz.resz_id = 10+i;
		resz.resz_nev = g_strdup_printf("%d. resz", i);
		g_array_append_val(fo->reszek, resz);
		g_printf("%d. resz_id: %d\n", i, resz.resz_id);
		g_printf("%d. resz_nev: %s\n", i, resz.resz_nev);
		// g_free(resz);
	}
	g_printf("\n");
	kiir (fo);
	g_free(fo);
	return 0;
}

Pont erről beszélek. Kösz, hogy leírtad helyettem. :)

Egyébként ez a verzió valószínűleg gyorsabb mint a mutatós, bár a különbség nem számottevő (persze attól függ...).

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Azért tévedsz, mert azt hiszed, a g_array_val függvény.
Pedig az egy makró.

http://developer.gnome.org/doc/API/glib/glib-arrays.html#G-ARRAY-APPEND-VAL

"Note: g_array_append_val() is a macro which uses a reference to the value parameter v. This means that you cannot use it with literal values such as "27". You must use variables."

Egyébként pont ezért szokták a makrókat nagybetűvel írni, mert akkor egyértelmű. Igaz, hogy más szempontból kevésbé szép. :)

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Áááá, átsiklottam felette. Mea culpa.

Na, született egy működőképes változat. Így most minden mutatókkal megy, bár egy kicsit elbizonytalanodtam a fentebb említett teljesítménybeli különbség miatt. Használat közben ugyan nem érzékelhető...

Most rendben működik, nem merem bántani.


#include <glib.h>

typedef struct _Fo   Fo;
typedef struct _Resz Resz;

struct _Resz
{
	gint   resz_id;
	gchar *resz_nev;
};

struct _Fo
{
	gint    id;
	gchar  *nev;
	gint    db;
	GArray *reszek;
};

void
kiir (Fo * fo)
{
	gint i;
	
	g_printf("KIIR: \n=====\n");
	
	for (i=0; i < 3; i++)
	{
		Resz *resz = &g_array_index(fo->reszek, Resz, i);
		g_printf("   %d. resz_id: %d\n", i, resz->resz_id);
		g_printf("   %d. resz_nev: %s\n", i, resz->resz_nev);
	}
}

int
main (int argc, char *argv[])
{
	Fo *fo = g_malloc(sizeof(Fo));
	gint i;
	
	fo->reszek = g_array_new (TRUE, TRUE, sizeof (Resz));
	
	for (i=0; i < 3; i++) {
		Resz *reszek = g_malloc(sizeof(Resz));
		
		reszek->resz_id  = i;
		reszek->resz_nev = g_strdup_printf("resz %d", i);
		g_array_append_val(fo->reszek, *reszek);
		g_printf("resz %d: %s\n", i, reszek->resz_nev);
	}
	
	for (i=0; i < 3; i++) {
		Resz *reszek = &g_array_index(fo->reszek, Resz, i);
		g_printf("vissza %d: %s\n", reszek->resz_id, reszek->resz_nev);
	}
	
	kiir(fo);
	
	return 0;
}

Nagyon szépen köszönöm mindenkinek a segítséget!

Üdv