Archief - [PROG][C] Tabel van tweedimensionale tabellen.

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

Duffman-

Legacy Member
Beste,

ik ben een applicatie aan het schrijven in C. Nu is het de bedoeling dat ik een array ga bijhouden van een tweedimensionale array. Hoe kan ik dit bekomen? Het is ook de bedoeling dat ik deze tabel dynamisch ga alloceren (realloc) omdat ik in het begin niet kan weten hoeveel keer ik de tweedimensionale tabel ga opslagen. Ik snap nu niet goed in wat voor soort array ik verschillende tweedimensionale tabellen ga opslaan. Weet iemand hoe ik dit kan bekomen? Ik dacht dit eerst te doen door een tabel bij te houden met de verschillende pointers naar de tweedimensionale tabel maar het probleem is dat het adres naar de tweedimensionale tabel hetzelfde kan blijven. Deze tweedimensionale tabel is namelijk ook dynamisch gealloceerd.

Greetz,
Duffman-

Duffman-

Legacy Member
Tyfius, alvast bedankt voor die link.
Ik heb nu het volgende bereikt:
Code:
int ***replay;
	replay = realloc(*replay,aantal_zetten*sizeof(int**));
	replay[aantal_zetten-1]=malloc(8*sizeof(int*));
    replay[0][0]=malloc(8*sizeof(int));
    replay[0]=speelveld;
Het vreemde is dat die code wél werkt, maar de volgende code niet:
Code:
	int ***replay;
    int i;
	replay = realloc(*replay,aantal_zetten*sizeof(int**));
	replay[aantal_zetten-1]=malloc(8*sizeof(int*));
    replay[0][0]=malloc(8*sizeof(int));
    replay[0]=speelveld;
Het enige verschil is dat ik in de tweede code een variabele (i) extra declareer die ik nodig ga hebben in die functie. Iemand een idee waarom dit conflicten oplevert? :s

Tyfius

Legacy Member
Het kan misschien dom zijn, maar declareer eens die "i" voor die "***replay". Het eerste waar ik (hoogstwaarschijnlijk foutief) aan dacht was de geheugenlocatie. Je reserveert een block van onbepaalde groote, en daarachter eem block van een bepaalde groote (zijnde sizeof(int)). Als je dan realloc doet gaat die het geheugen van "i" overschrijven.

Als ik er 2x over nadenk slaat mijn uitleg op niet veel, maar het is misschien het proberen waard.

Duffman-

Legacy Member
Ja, dat dacht ik ook even. Tevergeefs :(
Hij higlight trouwens die "realloc" en realloc gaat een nieuw adresgeheugen reserveren indien nodig.

Ik heb trouwens deze code gevonden op het internet die toch wel lijkt op mijn code imho:
Code:
int x, y, ***array3;

array3 = malloc(d1 * sizeof(int**));
for (x = 0; x <= d2; x++) {
	array3[x] = malloc(d2 * sizeof(int*));
	for (y = 0; y <= d3; y++) {
		array3[x][y] = calloc(d3, sizeof(int));
	}
}

Ik zie op het eerste zicht niet zoveel verschil :s

Duffman-

Legacy Member
Tyfius,

deze code:
Code:
void maak_replay(int** speelveld)
{
	int i=0;
	int ***replay=NULL;
    aantal_zetten++;//functie wordt enkel opgeroepen bij een zet.
	replay = realloc(replay,aantal_zetten*sizeof(int**));
	replay[aantal_zetten-1]=malloc(8*sizeof(int*));
	for(i=0;i<8;i++)
		replay[aantal_zetten-1][i]=malloc(8*sizeof(int));
	replay[aantal_zetten-1]=speelveld;
}
werkt wel ... kheb de 2 variabelen is vooraf geïnitialiseerd ... -_-;;

killgore

Legacy Member
Tyfius zei:
Het kan misschien dom zijn, maar declareer eens die "i" voor die "***replay". Het eerste waar ik (hoogstwaarschijnlijk foutief) aan dacht was de geheugenlocatie. Je reserveert een block van onbepaalde groote, en daarachter eem block van een bepaalde groote (zijnde sizeof(int)). Als je dan realloc doet gaat die het geheugen van "i" overschrijven.

Als ik er 2x over nadenk slaat mijn uitleg op niet veel, maar het is misschien het proberen waard.

uw uitleg klopt totaal niet :x.

Je reserveert geen onbekend geheugen, je reserveert met die *** enkel een pointer, ofte 4 bytes op een 32 bits processor of 8 byte op een 64 bits processor. niets onbekend aan.

Realloc gaat dan ook zijn geheugen niet op die plaats gaan zoeken maar ergens totaal anders en enkel dat adres aan uw pointer toekennen.

En derde reden waarom dit op niets slaat: realloc gaat nooit reeds gereserveerd geheugen overschrijven, laat staan uw stack.

Ik had vroeger een nederlandse uitleg ivm het dynamisch creëren van meerdimensionale arrays, maar die site is spijtig genoeg down.

Uw laatste code dan maar even bezien :).

Code:
void maak_replay(int** speelveld)
{
	int i=0;
	int ***replay=NULL;
    aantal_zetten++;//functie wordt enkel opgeroepen bij een zet.
	replay = realloc(replay,aantal_zetten*sizeof(int**));
	replay[aantal_zetten-1]=malloc(8*sizeof(int*));
	for(i=0;i<8;i++)
		replay[aantal_zetten-1][i]=malloc(8*sizeof(int));
	replay[aantal_zetten-1]=speelveld;
}

Het eerste dat me puur op SE vlak opvalt: Je gaat er zomaar van uit dat die functie enkel gaat worden opgeroepen bij een zet. Een methode/functie direct van een andere functionaliteit laten afhangen zonder 100% zeker te zijn dat dit ook daadwerkelijk zo zal zijn is een vrij zware consistentiefout.

Over array allocatie zelf:
eerst en vooral zou ik al checken of aantal_zetten groter is als nul.
wat me ook opvalt: je laatste statement overschrijft alle wijzigingen die je de 3 regels ervoor hebt gedaan (met waarschijnlijk ook nog eens een enorm aantal geheugenlekken als volgt). Buiten deze regel klopt alles wel ongeveer.

Ik gok dat die laatste regel ofwel omgekeerd zal moeten zijn ofwel (waarschijnlijkste) je alle inhoud zal moeten kopiëren ipv enkel die pointer.

Duffman-

Legacy Member
killgore zei:
uw uitleg klopt totaal niet :x.

Je reserveert geen onbekend geheugen, je reserveert met die *** enkel een pointer, ofte 4 bytes op een 32 bits processor of 8 byte op een 64 bits processor. niets onbekend aan.

Realloc gaat dan ook zijn geheugen niet op die plaats gaan zoeken maar ergens totaal anders en enkel dat adres aan uw pointer toekennen.

En derde reden waarom dit op niets slaat: realloc gaat nooit reeds gereserveerd geheugen overschrijven, laat staan uw stack.

Ik had vroeger een nederlandse uitleg ivm het dynamisch creëren van meerdimensionale arrays, maar die site is spijtig genoeg down.

Uw laatste code dan maar even bezien :).

Code:
void maak_replay(int** speelveld)
{
	int i=0;
	int ***replay=NULL;
    aantal_zetten++;//functie wordt enkel opgeroepen bij een zet.
	replay = realloc(replay,aantal_zetten*sizeof(int**));
	replay[aantal_zetten-1]=malloc(8*sizeof(int*));
	for(i=0;i<8;i++)
		replay[aantal_zetten-1][i]=malloc(8*sizeof(int));
	replay[aantal_zetten-1]=speelveld;
}

Het eerste dat me puur op SE vlak opvalt: Je gaat er zomaar van uit dat die functie enkel gaat worden opgeroepen bij een zet. Een methode/functie direct van een andere functionaliteit laten afhangen zonder 100% zeker te zijn dat dit ook daadwerkelijk zo zal zijn is een vrij zware consistentiefout.

Over array allocatie zelf:
eerst en vooral zou ik al checken of aantal_zetten groter is als nul.
wat me ook opvalt: je laatste statement overschrijft alle wijzigingen die je de 3 regels ervoor hebt gedaan (met waarschijnlijk ook nog eens een enorm aantal geheugenlekken als volgt). Buiten deze regel klopt alles wel ongeveer.

Ik gok dat die laatste regel ofwel omgekeerd zal moeten zijn ofwel (waarschijnlijkste) je alle inhoud zal moeten kopiëren ipv enkel die pointer.

Mijn laatste statement overschrijft toch niet alle wijzigingen? Ik steek de data die op dat moment in "speelveld" zit toch in de tabel replay, niet? Verder is het zeker dat "aantal_zetten" groter is dan 0. Dat is een globale integer die begint op 0 en aangezien die in die functie vermeerderd wordt met 1, zal hij nooit kleiner zijn dan 0 hé. Verder zeg je:
"Je gaat er zomaar van uit dat die functie enkel gaat worden opgeroepen bij een zet". Ja ik ga daar vanuit, ik laat de functie dan ook enkel oproepen als ik een geldige zet maak.

Greetz,
Duffman-

//edit: ik heb van "int ***replay;", "static int ***replay;" gemaakt.

killgore

Legacy Member
Duffman- zei:
Mijn laatste statement overschrijft toch niet alle wijzigingen? Ik steek de data die op dat moment in "speelveld" zit toch in de tabel replay, niet?
Neen, Je steekt enkel het adres dat in de pointer speelveld zit nu in je variabele replay[aantal_zetten-1]. De data van speelveld wordt dus niet gekopiëerd, je wijst gewoon vanuit 2 variabelen naar hetzelfde geheugen.
de nieuw aangemaakte data in die regels erboven gaan dan ook verloren omdat je er niet meer naar wijst vanuit replay[aantal_zetten-1], wat als ernstig gevolg heeft dat je di eniet meer kan vrijgeven (geheugenlek dus).


Verder is het zeker dat "aantal_zetten" groter is dan 0. Dat is een globale integer die begint op 0 en aangezien die in die functie vermeerderd wordt met 1, zal hij nooit kleiner zijn dan 0 hé. Verder zeg je:
"Je gaat er zomaar van uit dat die functie enkel gaat worden opgeroepen bij een zet". Ja ik ga daar vanuit, ik laat de functie dan ook enkel oproepen als ik een geldige zet maak.

Daar mag je niet zomaar vanuit gaan. Een functie moet een afgeschermd geheel zijn. Ik weet dat het in C iets moeilijker is om modulair te werken als in C++ e.d., maar het is toch vrij essentieel. Jouw code is door die "assumptions" en het niet checken van die ene variabele weinig tot compleet niet herbruikbaar. Daarnaast introduceer je zo zeer simpel fouten die moeilijk te vinden zijn. Stel dat je dit spel blijft verbeteren & uitbreiden, dan kan het zijn dat je binnen 3 maand ergens een functie hebt die om god weet welke redenen aantal_zetten op een negatieve waarde zet. Als dan deze functie nog wordt aangeroepen zit je met een probleem.
En je moet niet denken dat als je 100'en functies en broncode files hebt je nog perfect gaat weten wat je nog mag doen en niet mag doen op een global variabele of zo (1 van de redenen waarom wordt aangeraden deze niet te gebruiken).
Dus: zorg dat je functie altijd correct werkt en niet moet uitgaan van bepaalde veronderstellingen.

edit: en idd, die static was wel nodig, even overgezien :).

Tyfius

Legacy Member
killgore zei:
uw uitleg klopt totaal niet :x.

Je reserveert geen onbekend geheugen, je reserveert met die *** enkel een pointer, ofte 4 bytes op een 32 bits processor of 8 byte op een 64 bits processor. niets onbekend aan.

Realloc gaat dan ook zijn geheugen niet op die plaats gaan zoeken maar ergens totaal anders en enkel dat adres aan uw pointer toekennen.

En derde reden waarom dit op niets slaat: realloc gaat nooit reeds gereserveerd geheugen overschrijven, laat staan uw stack.
Achteraf gezien sloeg die uitleg inderdaad op niet veel :P

In 't vervolg pas antwoorden als ik volledig wakker ben dus.

Duffman-

Legacy Member
Killgore, bedankt voor de tips. Ik ga het niet aanpassen in dit programma (te weinig tijd) maar ik zal het zeker in gedachten houden vanaf nu. Het is soms ook afwegen, als ik een variabele nodig heb in (bijna) alle functies. Is het dan best deze mee te geven als parameter of is het dan best om deze globaal te definiëren?
Kheb nog wel een vraagje, hoe kan ik nu heel de inhoud van die 3D-array wegschrijven naar een bestand (tekst of binbestand)?

Greetz,
Duffman-

kheb momenteel dit:
Code:
for(i=0;i<aantal_zetten;i++)
			for(j=0;j<8;j++)
				for(k=0;k<8;k++)
					fwrite(&replay[i][j][k],sizeof(int),1,fp);
Volgens mij kan dit veel efficiënter. In een keer bijvoorbeeld. Maar kzou ni weten hoe.:(

killgore

Legacy Member
Duffman- zei:
Killgore, bedankt voor de tips. Ik ga het niet aanpassen in dit programma (te weinig tijd) maar ik zal het zeker in gedachten houden vanaf nu. Het is soms ook afwegen, als ik een variabele nodig heb in (bijna) alle functies. Is het dan best deze mee te geven als parameter of is het dan best om deze globaal te definiëren?
globale moet je zoveel mogelijk vermijden omdat je er geen echte consistentheid van kan afdwingen in C. In c++ wordt dit opgelost door zogenaamde singleton klasses.
Nuja, zoals je zegt is afwegen het beste :). Als je denkt dat het echt noodzakelijk is om een var als global te definiëren kan je dat doen. Het kan je coden vereenvoudigen, maar gebruik met mate, want het is zeer rot als er in je globals een fout terecht komt. Het is ook vaak een kwestie van ervaring.
OOP biedt veel mooiere oplossingen hiervoor.
Duffman- zei:
Code:
Kheb nog wel een vraagje, hoe kan ik nu heel de inhoud van die 3D-array wegschrijven naar een bestand (tekst of binbestand)?

Greetz,
Duffman-

kheb momenteel dit:
[code]
for(i=0;i<aantal_zetten;i++)
			for(j=0;j<8;j++)
				for(k=0;k<8;k++)
					fwrite(&replay[i][j][k],sizeof(int),1,fp);
Volgens mij kan dit veel efficiënter. In een keer bijvoorbeeld. Maar kzou ni weten hoe.:(
welja, dat laatste kan efficiënter. gezien die laatste 8 variabelen na elkaar zitten (als ik me niet vergis):

Code:
for(i=0;i<aantal_zetten;i++)
			for(j=0;j<8;j++)
					fwrite(replay[i][j],sizeof(int)*8,1,fp);
Maar dat is het ook, gezien die array dynamisch is gealloceerd is hij scattered over je gewone geheugen. Als je hem statisch alloceerde kon je gewoon alles wegschrijven in 1 blok :).

Maar bij wegschrijven is het loopen ietsje minder erg. Het is vooral bij lezen dat 1 blok inlezen efficiënter is.

Duffman-

Legacy Member
Momenteel doe ik uitlezen ook zo:
Code:
		for(i=0;i<aantal_zetten;i++)
		{
			for(j=0;j<8;j++)
				for(k=0;k<8;k++)
					fread(&replay_speelveld[j][k],sizeof(int),1,fp);
maar daar werk ik nog aan. :)

killgore

Legacy Member
als speelveld statisch gealloceerd is:

fread(replay_speelveld,sizeof(int)*8*8,1,fp);

zou het moeten doen :).

Het beste van al is, als je heel het bestand nodig hebt, dit van de eerste keer in een array in te laden. Moet je hem daarna versnipperen (bv. voor een dynamische array), so be it.

Maar versnipperd inladen is trager als in 1x inladen en dan versnipperen. Omdat dat deerste telkens opnieuw gaat teruggaan naar uw harde schijf (bij grotere bestanden althans, kleinere worden door het operating system zelf normaal gezien al vrij volledig gebufferd).

Duffman-

Legacy Member
Allé mannen, het is allemaal in orde gekomen. Als je het resultaat wil hebben, laat het dan maar weten.

Het werkje is afgeleverd so no changes possible! :)
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan