Kirjautuminen

Haku

Tehtävät

Koodit: C: Argumenttilista; moniulotteinen dynaaminen taulukko

Kirjoittaja: Metabolix

Kirjoitettu: 06.06.2006 – 06.06.2006

Tagit: koodi näytille, vinkki

Ohjelmointi on ihmeitä täynnä, ja tämä esimerkki onnistuu käsittelemään jo useampaa kappaletta. Vinkin tarkoitus on näyttää hieman, kuinka voi käyttää argumenttilistaa eli kuinka tehdä funktio, joka printf:n tapaan voi ottaa argumentteja juuri sen verran, kuin niitä annetaan. Lisäksi samalla tulee esiteltyä dynaamista muistinkäsittelyä, moniulotteisia taulukoita ja rekursiivisia funktioita.

Esimerkkinä toimii funktiopari, jolla saa luotua ja tuhottua moniulotteisia taulukoita. Luontifunktio ottaa taulukon yhden alkion koon, ulottuvuuksien määrän ja ulottuvuudet. Tuhoajafunktio ottaa taulukon, ulottuvuuksien määrän ja ulottuvuudet viimeistä lukuun ottamatta.

Virheentarkistusta koodissa ei taaskaan ole, mutta sen sijaan siitä löytyy esimerkillinen goto-viritelmä, jonka ansiosta ohjelman sammumisen estämiseen ei tarvita purukumia.

Funktiot

/* Otsikot */
#include <stdarg.h>
#include <stdlib.h>

/* Funktio, joka varaa rekursiivisesti tietyn taulukon.
** "Viimeinen argumentti" on argumenttilista. */
void * v_tee_taulu(int alkio, int ulottuvuuksia, va_list argp)
{
	void * taulu;
	int pituus, i;

	/* Kyllä niitä suuntia saisi edes jokunen olla */
	if (ulottuvuuksia < 1)
		return NULL;

	/* Jos on yksi ulottuvuus, luodaan sellainen */
	if (ulottuvuuksia == 1)
		return calloc(va_arg(argp, int), alkio);

	/* On vielä useampi ulottuvuus.
	** Otetaan päällimmäinen ulottuvuus, varataan tilaa uusille osoittimille
	** ja luodaan niihin uudet taulukot vähemmillä ulottuvuuksilla */
	/* va_arg ottaa listan ja tyypin */
	pituus = va_arg(argp, int);

	taulu = malloc(pituus * sizeof(void *));

	for (i = 0; i < pituus; ++i) {
		((void **)taulu)[i] = v_tee_taulu(alkio, ulottuvuuksia - 1, argp);
	}

	return taulu;
}

/* Funktio, joka tuhoaa rekursiivisesti tietyn taulukon */
void v_tuhoa_taulu(void * t, int ulottuvuuksia, va_list argp)
{
	int pituus, i;

	/* Jos ei ole ulottuvuuksia, mennään pois vain */
	if (ulottuvuuksia < 1)
		return;

	/* Jos on yksi ulottuvuus, tuhotaan se */
	if (ulottuvuuksia == 1)
		return free(t);

	/* On vielä useampi ulottuvuus.
	** Otetaan päällimmäinen niistä, tuhotaan taulukot ja itsemme */
	pituus = va_arg(argp, int);

	for (i = 0; i < pituus; ++i) {
		v_tuhoa_taulu(((void **)t)[i], ulottuvuuksia - 1, argp);
	}
	free(t);
}

/* Kutsuttavat funktiot.
** Näissä vain avataan argumenttilista ja kutsutaan listallista funktiota.
** Argumenttina annetaan alkion koko, ulottuvuuksien määrä ja itse ulottuvuudet. */
void * tee_taulu(int alkion_koko, int ulottuvuuksia, ...)
{
	void * t;
	va_list argp;
	/* Lista alustetaan viimeisen oikean parametrin avulla. */
	va_start(argp, ulottuvuuksia);
	t = v_tee_taulu(alkion_koko, ulottuvuuksia, argp);
	/* Lopuksi se pitää sulkea. */
	va_end(argp);
	return t;
}

/* Tuhoajafunktiolle annetaan taulukko, ulottuvuuksien määrä ja ulottuvuudet.
** Viimeistä ulottuvuutta ei kuitenkaan tarvitse antaa. */
void tuhoa_taulu(void * t, int ulottuvuuksia, ...)
{
	va_list argp;
	va_start(argp, ulottuvuuksia);
	v_tuhoa_taulu(t, ulottuvuuksia, argp);
	va_end(argp);
}

Esimerkki

#include <stdio.h>

int main(void)
{
	int maara, vastaus;
	char ** t;

Alku:
	/* Varataan taulukko, jossa on viisi sadan merkin char-taulua */
	t = (char **) tee_taulu(1, 2, 5, 100);

	/* Käytetään niitä vähän */
	printf("Anna viisi sanaa.\n>> ");
	scanf("%99s %99s %99s %99s %99s", t[0], t[1], t[2], t[3], t[4]);
	printf("'%s', '%s', '%s', '%s' ja '%s'\n", t[4], t[3], t[2], t[1], t[0]);

	/* Tuhotaan taulu. Viimeistä ulottuvuutta ei tarvitse ilmoittaa,
	** kuten ei myöskään alkion kokoa */
	tuhoa_taulu(t, 2, 5);

Lopetetaanko:
	printf("Joko lopetetaan? Vastaa 1 tai 0.\nVastaus: ");
	maara = scanf("%i", &vastaus);

	if (maara != 1 || (vastaus != 1 && vastaus != 0))
		goto Lopetetaanko;

	if (vastaus == 0)
		goto Alku;

	return 0;
}

Kommentit

ZcMander [06.06.2006 12:46:49]

#

Hyi hyi, ei mitään gotoja, nehän olisi jo pitänyt kuopata qbasic aikana.

Antti Laaksonen [07.06.2006 01:15:14]

#

Vaan vinkin pääasia on muualla, ja esimerkki on hyvä. Aika harvoin kyllä tarvitsee tuollaisia funktioita.

tgunner [07.06.2006 14:46:32]

#

GOTO on ihan toimiva jos sitä osaa käyttää oikeissa paikoissa.

Ceez [11.06.2006 06:25:05]

#

Omenasalaatti vyöryi banaanimäkeä ylävasempaan. Missä viisi.

aaämdee [03.08.2009 17:45:43]

#

Kiitos tästä vinkistä. Opin paljon uutta! :-)

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta