Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C ja taulukon palautus funktiosta

Sivun loppuun

Ville [15.10.2009 11:29:33]

#

Rupesi tässä askarruttamaan eräs asia.
Jos esimerkkitapaus olisi tälläinen :
Pääohjelmassa pyydetään käyttäjältä luku joka sitten toimitetaan funktiolle, jossa lukua käytetään apuna taulukon täyttämisessä kokonaisluku arvoilla.
Kun taulukko on täytetty, palautetaan taulukko pääohjelmaan ja tulostetaan ruudulle.

Tuli pari toteutustapaa mieleen. Ensimmäinen olisi sellainen jossa käytettävä taulukko olisi esitelty globaalisti ja sitä voi sitten käsitellä missä tahansa kohdassa ohjelmaa. Alla esimerkki :

#define MONTAKO 3
void teeTaulukko(int a);
int taulukko[MONTAKO];

int main()
{
	int i, x;
	puts("\nAnna luku ");
	scanf("%d", &x);
        teeTaulukko(x);

        for(i = 0; i < MONTAKO ; i++)
	{
		printf("%d\n", taulukko[i]);
	}
	return 0;
}
void teeTaulukko(int a)
{
	int lisays;
	for(lisays = 0; lisays < MONTAKO ; lisays++)
	{
		taulukko[lisays] = lisays + a;
	}
}

Tämä oli se helpompi tapa(jonka sain toimimaan). Mutta eikös globaali taulukko (tai ylipäätään muuttujan globaali esittely) saa aikaan sen että kaikki funktiot voisi kirjoittaa void palautustyyppisiksi. Silloinhan funktion idea hiukan katoaa, ainakin omasta mielestäni...

Mutta sitten se vaikeampi tapa (ainakin itselleni) jota en saanut toimimaan oikein. Se on muuten sama mutta nyt funktion prototyyppiä ja otsikkoa on korjattu siten että funktio palauttaa osoittimen ja taulukko ei ole globaali mutta sen sisältö pitäisi kuitenkin saada palautettua. Koetan käyttää osoitinta apuna, alla koodiräpellys:

#define MONTAKO 3
int *teeTaulukko(int a);

int main()
{
	int i, x, *osoitin;
	puts("\nAnna luku ");
	scanf("%d", &x);
        osoitin = teeTaulukko(x);

	for(i = 0; i < MONTAKO ; i++)
	{
		printf("%d\n", *(osoitin+i));
	}
	return 0;
}
int *teeTaulukko(int a)
{
	int lisays, taulukko[MONTAKO];
	for(lisays = 0; lisays < MONTAKO ; lisays++)
	{
		taulukko[lisays] = lisays + a;
	}
	return taulukko;
}

Kun koetan pääohjelmassa käydä osoittimen avulla palautettuja taulukon arvoja läpi, saan oikean määrän arvoja ja ensimmäisen arvon kyllä oikein mutta seuraavat arvot ovatkin sitten mitä sattuu.
Nyt ei enää vajavainen c ymmärrys riitä, joten voisitteko valaista. Ja anteeksi että tästä taisi tulla melko pitkä sepustus..

vehkis91 [15.10.2009 11:36:20]

#

Meni vähä ohi toi sun ongelmas? Eli onko tarkoituksena palauttaa funktiosta taulukko vai mitä?

Edit: Yksinkertainen esimerkki, missä palautetaan taulukko

#include <stdio.h>

int *annaTaulukko();

int main(void)
{
   int *taulukko = annaTaulukko();
    printf("\ntaulukko[0] = %i",taulukko[0]);
   return 0;
}

int *annaTaulukko()
{
   int taulukko[10];
   taulukko[0] = 1;
   printf("taulukko[0] = %i",taulukko[0]);
   return taulukko;
}

Ville [15.10.2009 12:14:15]

#

Oli taas sormi mieltä vikkelämpi ja vahingossa lähetin tuon pohdintani sinne esille ja muokkauskin kesti melko kauan ;p

Esimerkkisi on selkeä mutta kun funktiossa sijoitan taulukkoon useamman arvon ja koetan niitä pääohjelmassa käydä läpi en edelleenkää saa kuin ensimmäisen arvon oikein ...
Tässä yritykseni :

#include <stdio.h>
#define MONTAKO 3
int *annaTaulukko();

int main(void)
{
   int *taulukko = annaTaulukko(), i;
	for(i = 0; i < MONTAKO ; i++)
	{
	 	  printf("\ntaulukko[%i] = %i",i, taulukko[i]);
	}
   return 0;
}
int *annaTaulukko()
{
   int taulukko[10],  lisays;
   for(lisays = 0; lisays < MONTAKO ; lisays++)
	{
		taulukko[lisays] = lisays + 2;
		printf("luku %d\n", taulukko[lisays]);
	}
   return taulukko;
}

vehkis91 [15.10.2009 12:51:39]

#

Tämän pitäis toimia.

#include <stdio.h>

void annaTaulukko(int *x);

int main(void)
{
    int taulukko[10] = {0};
    annaTaulukko(taulukko);

    if(taulukko)
    {
        printf("\ntaulukko[0] = %i", taulukko[0]);
        printf("\ntaulukko[1] = %i", taulukko[1]);
    }

    return 0;
}

void annaTaulukko(int *x)
{
    x[0] = 1;
    x[1] = 2;
    printf("taulukko[0] = %i", x[0]);
    printf("\ntaulukko[1] = %i", x[1]);
}

Torgo [15.10.2009 12:57:47]

#

Vehkiksen esimerkki ei palauta taulukkoa, vaan osoittimen siihen kohtaan mistä taulukko on joskus alkanut. Kokeilepa vaikka näin:

#include <stdio.h>

#define MONTAKO 3

void annaTaulukko(int *taulukko, int maara);

int main(void)
{
   int taulukko[10], i;
   annaTaulukko(taulukko, MONTAKO);

   for(i=0; i<MONTAKO; ++i) {
      printf("\ntaulukko[%i] = %i",i, taulukko[i]);
   }
   return 0;
}


void annaTaulukko(int *taulukko, int maara)
{
   int i;
   for(i=0; i<maara; ++i) {
      taulukko[i] = i+2;
   }
}

Edit. Vehkis ehti ensin. Kommentti viittasi Vehkiksen ensimmäiseen koodiin.

Ville [15.10.2009 13:41:48]

#

Torgo ja Vehkis91(jälkimmäinen esimerkki), kun katselen esimerkkejänne joissa viette funktiolle pääohjelmassa esitellyn taulukon ja sitten käsittelette taulukkoa funktion sisällä sitä kuitenkaan palauttamatta.

Niin eikö tuo vastaa sitä jos esittelin käytettävän taulukon globaalisti ja käyttäisin sen jälkeen void tyyppistä funktiota taulukon muokkaamiseen? Kuten alkuperäisessä kysymyksessäni pohdin (jäisi taulukon vienti funktiolle pois).

Mutta miten alkuperäisen kysymyksen ongelma, taulukon (tai osoittimen) palautus funktiosta pääohjelmaan ja arvojen (siis usean) tulostus ruudulle?
Täällä jo aiemmin versioni Vehkiksen koodista esitin, jossa en sitä saanut toimimaan.
Eli miten hoidan homman jos funktion otsikko on :

int *funktio(int jokuMuuttuja)

ja jokuMuuttuja ei siis ole taulukko vaan yhden arvon sisältävä muuttuja (kuten alkuperäisessä kysymyksesäni oli).

Valitan että en ilmeisesti tätä nyt oikein osaa fiksusti esittää ...

vehkis91 [15.10.2009 13:47:55]

#

Käsittääkseni tuo on ainut keino muokata taulukkoa funktion sisällä...

Miks sun pitäis saada palautettua taulukko?

Kuten Torgo tuossa sanoi, tuo koodi palauttaa vain osoittimen taulukon alkuun.

Ville [15.10.2009 13:55:55]

#

vehkis91 kirjoitti:

Käsittääkseni tuo on ainut keino muokata taulukkoa funktion sisällä...

Ok.

vehkis91 kirjoitti:

Miks sun pitäis saada palautettua taulukko?

Kun tässä koetan näitä C:n alkeita hahmottaa ja tiedän että muissa ohjelmointikielissä kyseinen temppu onnistuu (taulukon palautus metodeista), niin arvelin että kai C kielessäkin.

vehkis91 kirjoitti:

Kuten Torgo tuossa sanoi, tuo koodi palauttaa vain osoittimen taulukon alkuun.

No sekin kelpaa, jos vain saisin käytyä arvoja osoittimen avulla läpi, kuten olen ymmärtänyt että olisi mahdollista.

Torgo [15.10.2009 15:37:39]

#

Ville kirjoitti:

Torgo ja Vehkis91(jälkimmäinen esimerkki), kun katselen esimerkkejänne joissa viette funktiolle pääohjelmassa esitellyn taulukon ja sitten käsittelette taulukkoa funktion sisällä sitä kuitenkaan palauttamatta.

Niin eikö tuo vastaa sitä jos esittelin käytettävän taulukon globaalisti ja käyttäisin sen jälkeen void tyyppistä funktiota taulukon muokkaamiseen? Kuten alkuperäisessä kysymyksessäni pohdin (jäisi taulukon vienti funktiolle pois).

Ei vastaa. Taulukko on lokaali muuttuja funktiolle main. Käsittelevä funktio ei millään tavalla näe sitä taulukkoa. Main-funktio kertoo taulukkoa käsittelevälle funktiolle, mistä osoitteesta data alkaa ja montako alkiota täytyy käsitellä. Siinä mielessä tuo toimii kuitenkin samalla tavalla, että molemmissa tapauksissa funktio käsittelee suoraan alkuperäistä taulukkoa.

Ville kirjoitti:

Mutta miten alkuperäisen kysymyksen ongelma, taulukon (tai osoittimen) palautus funktiosta pääohjelmaan ja arvojen (siis usean) tulostus ruudulle?
Täällä jo aiemmin versioni Vehkiksen koodista esitin, jossa en sitä saanut toimimaan.
Eli miten hoidan homman jos funktion otsikko on :

int *funktio(int jokuMuuttuja)

ja jokuMuuttuja ei siis ole taulukko vaan yhden arvon sisältävä muuttuja (kuten alkuperäisessä kysymyksesäni oli).

C käsittelee taulukoita osoittimina ja varsinaisen taulukon palauttaminen on hieman monimutkaisempaa. Eikä siinä varsinkaan tässä tapauksessa olisi mitään järkeäkään. Kulutat aivan suotta muistia ja resursseja siihen että välität kopiota taulukosta funktioiden välillä parametrina ja paluuarvona.

Jos mieluiten haluat että funktiolla itsellään on taulukko-muuttuja ja se palauttaa osoittimen kyseiseen taulukkoon, niin taulukon on oltava static ja siihen on kopioitava main-funktion taulukon sisältö. Lisäksi taulukon koko on oltava selvillä.

Jos taulukko ei ole static, niin funktio palauttaa osoittimen taulukkoon, jota ei ole enää olemassa, koska sen elinkaari päättyy funktion loppuun. Sen sijaan staticina sen elinkaari jatkuu ja palautettu osoitin osoittaa edelleen validiin dataan. Eli voit lisätä vain static-määreen alkuperäiseen versioosi ja varmuudeksi alustamaan taulukko. Suosittelen kuitenkin välttämään kyseistä tapaa.

Metabolix [15.10.2009 20:26:53]

#

Selkein tapa välittää oikeasti kokonaisia taulukoita funktioiden välillä on määritellä rakenne, joka sisältää halutunlaisen taulukon.

struct int_taulu {
  int t[2];
};

struct int_taulu luo_taulu() {
  struct int_taulu tmp;
  tmp.t[0] = 12;
  tmp.t[1] = 34;
  return tmp;
}

#include <stdio.h>

int main() {
  int i;
  struct int_taulu t1, t2;
  t1 = luo_taulu();
  t2 = t1;
  t1.t[0] = 45;
  printf("%3s  %9s  %9s\n", "i", "t1.t[i]", "t2.t[i]");
  for (i = 0; i < 2; ++i) {
    printf("%3d  %9d  %9d\n", i, t1.t[i], t2.t[i]);
  }
  return 0;
}

Toinen (tavanomainen) vaihtoehto ovat osoittimet. Tällöin uusi taulukko varataan malloc-funktiolla, ja lopuksi muisti pitää vapauttaa free-funktiolla.

eq [15.10.2009 22:28:07]

#

Ville kirjoitti:

vehkis91 kirjoitti:

Miks sun pitäis saada palautettua taulukko?

Kun tässä koetan näitä C:n alkeita hahmottaa ja tiedän että muissa ohjelmointikielissä kyseinen temppu onnistuu (taulukon palautus metodeista), niin arvelin että kai C kielessäkin.

Kuten ketjussa on jo käynyt ilmi, näin ei kuitenkaan ole – taulukkoa ei voi funktiosta (suoraan) palauttaa.

(Kuten Metabolix yllä totesti), yksi taulukon palauttamista vastaava idea olisi riittävänkokoiseen muistialueeseen osoittavan osoittimen palauttaminen. Funktion elinkaaren ylittävään muistinvaraukseen tarvitaan kuitenkin dynaamisen muistinhallinnan käyttöä, ja tällöin varattava muisti on itse myös vapautettava (funktiolla free). Jos käpisteltävän taulukon koko on vakio (kuten esimerkiksi aloitusviestin esimerkissä), ovat tästä vaivasta saatavat edut kyseenalaiset; erikokoisia "taulukoita" palauttavasta funktiosta alla kuitenkin esimerkki:

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

int *luo_fibonacci_taulu(size_t pituus);

int main(void)
{
  int n, i, *taulu;
  scanf("%d", &n);
  taulu = luo_fibonacci_taulu(j);
  if (taulu == NULL) {
    fprintf(stderr, "Taulukon luonti epäonnistui.\n");
    return EXIT_FAILURE;
  }
  for (i = 0; i < j; ++i)
    printf("%d\t", taulu[i]);
  free(taulu);
}

int *luo_fibonacci_taulu(size_t pituus)
{
  int a, b, i, *taulu;
  taulu = malloc(pituus * sizeof *taulu);
  if (taulu == NULL) // muistinvaraus epäonnistui
    return NULL;
  for (a = 1, b = 0, i = 0; i < pituus; ++i) {
    taulu[i] = b;

    int c = a;
    a += b;
    b = c;
  }
  return taulu;
}

Ville [16.10.2009 14:18:39]

#

Kiitos kaikille jälleen ja erityisesti eq:lle.
Esimerkkisi avulla sain alkuperäisen ongelmani ratkaistua :)


Sivun alkuun

Vastaus

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

Tietoa sivustosta