Ero sivun ”C” versioiden välillä

Poistettu sisältö Lisätty sisältö
Tronic (keskustelu | muokkaukset)
Kirjoitettu moniulotteiset taulukot uusiksi, koska alkuperäinen oli virheellinen.
Rivi 358:
 
=== Moniulotteiset ===
 
Moniulotteiset taulukot ovat taulukkoja, joiden alkioissa on taulukko. Esimerkiksi kaksiulotteisessa taulukossa <tt>numerot[5][5]</tt> on viisi alkiota, joista jokainen sisältää taulukon.
Moniulotteiset taulukot ovat taulukoita, joita indeksoidaan useammalla kuin yhdellä muuttujalla, esim. taulukko[i][j][k] (kolmiulotteinen taulukko). Niitä on C:ssä kahta päätyyppiä: staattisia ja dynaamisia, joista jälkimmäisiä kutsutaan myös pointteritaulukoiksi, koska ne on toteutettu pointterein.
 
==== Staattiset ====
 
[[Kuva:Array2.svg|Tyhjä kaksiulotteinen taulukko.]]
 
Staattisessa kaksiulotteisessa taulukossa <tt>taulukko[3][4]</tt> on 12 alkiota, jotka on sisäisesti tallennettu 12-alkioiseen 1-ulotteiseen taulukkoon, mutta kääntäjä antaa indeksoida taulukkoa ikään kuin se olisi kaksiulotteinen.
Moniulotteisiin taulukoihin voi lisätä arvoja samalla tavalla kuin yksiulotteisiin taulukoihin.
 
Kirjoittaessasi taulukko[i][j] hakee kääntäjä taulukostaan alkion [i * 4 + j]. Huomaa, että tässä kääntäjän oli tiedettävä taulukon taulukon koko toisen indeksin suhteen. Vastaavasti kolmiulotteisella staattisella taulukolla taulukko[3][4][5], viitattaessa soluun taulukko[i][j][k], indeksi lasketaan [i * 4 * 5 + j * 5 + k]. Huomionarvoista tässä on, että taulukon käyttämiseksi kääntäjän täytyy aina tietää sen koko kaikkien muiden paitsi ensimmäisen indeksin suhteen.
 
Staattiset moniulotteiset taulukot esitellään samoin kuin yksiulotteisetkin ja ne voi myös alustaa vastaavasti (jos ei alusta, ovat taulukon sisältämät arvot satunnaisia).
 
int numerottaulukko[3][34] = { { 111, 212, 313, 14 }, { 1621, 3222, 6423, 24 }, { 031, 32, 33, 34 } };
 
Taulukkoa ei voi helposti välittää funktiolle, koska funktion määrittelyssä tulee taulukon koon (paitsi ensimmäisen indeksin) olla määritelty jo lähdekoodissa:
 
#include <stdio.h>
void print(int taulukko[][4]) {
for (size_t i = 0; i < 3; ++i) {
for (size_t j = 0; j < 4; ++j) printf("%d ", taulukko[i][j]);
putchar('\n');
}
}
int main(void) {
int taulukko[3][4] = { { 11, 12, 13, 14 }, { 21, 22, 23, 24 }, { 31, 32, 33, 34 } };
print(taulukko);
}
 
==== Dynaamiset ====
 
Pointteritaulukko on huomattavasti staattista taulukkoa joustavampi ratkaisu, mutta myös merkittävästi hankalampi luoda. Taulukko ei myöskään tuhoudu automaattisesti lohkon päätteeksi, vaan se täytyy itse vapauttaa.
 
// Varataan ulommaisin taulukko (ensimmäinen indeksi), sisältää pointtereita sisätaulukoihin
int** taulukko = malloc(3 * sizeof(int*));
for (size_t i = 0; i < 3; ++i) {
// Varataan sisätaulukko (toinen indeksi), sisältää numeroita
taulukko[i] = malloc(4 * sizeof(int));
// Alustetaan taulukon solut vastaavilla arvoilla kuin staattisella taulukolla edellä
for (size_t j = 0; j < 4; ++j) taulukko[i][j] = (i + 1) * 10 + j + 1;
}
/* ... Käytetään taulukkoa ... */
// Vapautetaan taulukolle varattu muisti
for (size_t i = 0; i < 3; ++i) free(taulukko[i]);
free(taulukko);
taulukko = NULL; // Jottei vahingossa käytettäisi jo vapautettua muistia
Edellä olevassa esimerkkikoodissa mallocin paluuarvoja ei tarkistettu, mutta oikeassa koodissa pitää aina mallocin kutsumisen jälkeen tarkistaa palauttiko kutsu NULL:n ja suorittaa vaadittavat siivoustoimenpiteet (esim. jo varatun muistin vapautus).
 
Täysin dynaamisessa pointteritaulukossa rivien ei tarvitse olla yhtä pitkiä, eli loopin sisällä oltaisiin voitu tehdä malloc((i + 1) * sizeof(int)), jolloin ensimmäisellä rivillä olisi yksi alkio, toisella kaksi, jne. Rivien kokoa voi muuttaa (realloc) ilman että muulle taulukossa olevalle tiedolle tarvitsee tehdä mitään ja myös rivien lisäys ja poisto hoituu kevyesti rivipointtereita siirtelemällä (sisällä olevaa tietoa ei tarvitse siirtää esim. lisättäessä uusi rivi taulukon alkuun).
 
Monesti kuitenkin riittää myös kevyempi "puolidynaaminen" pointteritaulukko, jossa tieto on tallennettu isoon yksiulotteiseen taulukkoon (voi olla esim. staattinen moniulotteinen taulukko), mutta sitä indeksoidaan pointteritaulukon avulla. Seuraavassa esimerkki:
 
int t[3 * 4] = { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34 };
int** taulukko = malloc(3 * sizeof(int*));
for (size_t i = 0; i < 3; ++i) taulukko[i] = t + i * 4 + j;
 
Edellisessä esimerkikssä taulukko t olisi yhtä hyvin voinut olla staattinen kaksiulotteinen taulukko, koska sellaisen sisäinen rakenne on identtinen yksiulotteisen taulukon kanssa.
int numerot[5][5];
numerot[5][0] = 1;
/* ... */
numerot[5][4] = 1;
 
Staattisesta moniulotteisesta taulukosta poiketen, mikä tahansa pointteritaulukko voidaan helposti välittää funktion argumenttina, tietämättä sen kokoa ennalta:
Taulukoita voi myös alustaa samalla tavalla.
 
void print(int** taulukko, size_t dim1, size_t dim2) {
int numerot[3][3] = { { 1, 2, 3 }, { 16, 32, 64 }, { 0 } };
for (size_t i = 0; i < dim1; ++i) {
for (size_t j = 0; j < dim2; ++j) printf("%d ", taulukko[i][j]);
putchar('\n');
}
}
 
== Merkkijonot ==
Noudettu kohteesta ”https://fi.wikibooks.org/wiki/C