Zoeken…


Invoering

Arrays zijn afgeleide gegevenstypen die een geordende verzameling waarden ("elementen") van een ander type vertegenwoordigen. De meeste arrays in C hebben een vast aantal elementen van elk type en de weergave ervan slaat de elementen aaneengesloten in het geheugen op zonder gaten of opvulling. C maakt multidimensionale arrays mogelijk waarvan de elementen andere arrays zijn, en ook arrays of pointers.

C ondersteunt dynamisch toegewezen arrays waarvan de grootte tijdens runtime wordt bepaald. C99 en later ondersteunt arrays met variabele lengte of VLA's.

Syntaxis

  • type naam [lengte]; / * Definieer array van 'type' met naam 'naam' en lengte 'lengte'. * /
  • int arr [10] = {0}; / * Definieer een array en initialiseer ALLE elementen naar 0. * /
  • int arr [10] = {42}; / * Definieer een array en initialiseer 1e elementen naar 42 en de rest naar 0. * /
  • int arr [] = {4, 2, 3, 1}; / * Definieer en initialiseer een array met lengte 4. * /
  • arr [n] = waarde; / * Waarde instellen op index n. * /
  • waarde = arr [n]; / * Krijg waarde bij index n. * /

Opmerkingen

Waarom hebben we arrays nodig?

Arrays bieden een manier om objecten te ordenen in een aggregaat met een eigen betekenis. C-reeksen zijn bijvoorbeeld reeksen tekens ( char ) en een reeks zoals "Hallo wereld!" heeft betekenis als een aggregaat dat niet inherent is aan de tekens afzonderlijk. Evenzo worden arrays gewoonlijk gebruikt om wiskundige vectoren en matrices voor te stellen, evenals lijsten van vele soorten. Bovendien zou men, zonder enige manier om de elementen te groeperen, elk afzonderlijk moeten aanpakken, zoals via afzonderlijke variabelen. Niet alleen is dat log, het is niet gemakkelijk om collecties van verschillende lengtes te huisvesten.

In de meeste contexten worden arrays impliciet omgezet in pointers .

Behalve wanneer deze wordt weergegeven als de operand van de operator sizeof , de operator _Alignof (C2011) of de operator unary & (adres-of), of als een letterlijke tekenreeks die wordt gebruikt om een (andere) array te initialiseren, wordt een array impliciet omgezet in ( "vervalt naar") een wijzer naar zijn eerste element. Deze impliciete conversie is nauw gekoppeld aan de definitie van de array-subscripting-operator ( [] ): de uitdrukking arr[idx] is gedefinieerd als gelijk aan *(arr + idx) . Bovendien, aangezien pointer rekenkundig commutatief is, is *(arr + idx) ook equivalent aan *(idx + arr) , wat op zijn beurt equivalent is aan idx[arr] . Al deze uitdrukkingen zijn geldig en evalueren met dezelfde waarde, op voorwaarde dat idx of arr een pointer is (of een array die vervalt naar een pointer), de andere een geheel getal is en het gehele getal een geldige index in de array is waarnaar de aanwijzer verwijst.

Merk in het bijzonder op dat &(arr[0]) equivalent is aan &*(arr + 0) , wat vereenvoudigt naar arr . Al die uitdrukkingen zijn uitwisselbaar waar de laatste vervalt naar een wijzer. Dit geeft eenvoudig weer aan dat een array vervalt naar een wijzer naar zijn eerste element.

Als het adres van de operator daarentegen wordt toegepast op een array van het type T[N] ( ie &arr ), dan heeft het resultaat het type T (*)[N] en verwijst het naar de hele array. Dit onderscheidt zich van een pointer tot het eerste array-element, althans met betrekking tot rekenkundige aanwijzer, die wordt gedefinieerd in termen van de grootte van het puntige type.

Functieparameters zijn geen arrays .

void foo(int a[], int n);
void foo(int *a, int n);

Hoewel de eerste declaratie van foo array-achtige syntaxis gebruikt voor parameter a , wordt een dergelijke syntaxis gebruikt om een functieparameter aan te geven die parameter aangeeft als een pointer naar het elementtype van de array. De tweede handtekening voor foo() is dus semantisch identiek aan de eerste. Dit komt overeen met het verval van arraywaarden pointers wanneer ze als argumenten voor een functieaanroep, zodanig dat indien een variabel en afhankelijk parameter worden aangegeven met dezelfde array soort dan die waarde variabel is geschikt voor toepassing in een functieaanroep als argument geassocieerd met de parameter.

Een array declareren en initialiseren

De algemene syntaxis voor het declareren van een eendimensionale array is

type arrName[size];

waarbij het type elk ingebouwd type of door de gebruiker gedefinieerde typen kan zijn, zoals structuren, is arrName een door de gebruiker gedefinieerde identificatie en is de size een geheel arrName .

Het declareren van een array (in dit geval een array van 10 int-variabelen) gaat als volgt:

int array[10];

het bevat nu onbepaalde waarden. Om er zeker van te zijn dat het nulwaarden bevat tijdens het declareren, kunt u dit doen:

int array[10] = {0};

Arrays kunnen ook initializers hebben, dit voorbeeld declareert een array van 10 int 's, waarbij de eerste 3 int 's de waarden 1 , 2 , 3 zullen bevatten, alle andere waarden zullen nul zijn:

int array[10] = {1, 2, 3};

In de bovenstaande initialisatiemethode wordt de eerste waarde in de lijst toegewezen aan het eerste lid van de array, wordt de tweede waarde toegewezen aan het tweede lid van de array enzovoort. Als de lijstgrootte kleiner is dan de arraygrootte, worden de resterende leden van de array zoals in het bovenstaande voorbeeld geïnitialiseerd naar nullen. Met aangewezen lijstinitialisatie (ISO C99) is expliciete initialisatie van de arrayleden mogelijk. Bijvoorbeeld,

int array[5] = {[2] = 5, [1] = 2, [4] = 9}; /* array is {0, 2, 5, 0, 9} */

In de meeste gevallen kan de compiler de lengte van de array voor u afleiden, dit kan worden bereikt door de vierkante haken leeg te laten:

int array[] = {1, 2, 3}; /* an array of 3 int's */
int array[] = {[3] = 8, [0] = 9}; /* size is 4 */

Het declareren van een array met lengte nul is niet toegestaan.

C99 C11

Arrays met variabele lengte (kortweg VLA) werden toegevoegd in C99 en optioneel gemaakt in C11. Ze zijn gelijk aan normale arrays, met één belangrijk verschil: de lengte hoeft niet bekend te zijn tijdens het compileren. VLA's hebben automatische opslagduur. Alleen verwijzingen naar VLA's kunnen een statische opslagduur hebben.

size_t m = calc_length(); /* calculate array length at runtime */
int vla[m];               /* create array with calculated length */

Belangrijk:

VLA's zijn potentieel gevaarlijk. Als de array vla in het bovenstaande voorbeeld meer ruimte op de stapel vereist dan beschikbaar, loopt de stapel over. Het gebruik van VLA's wordt daarom vaak afgeraden in stijlgidsen en door boeken en oefeningen.

Array-inhoud wissen (nulstellen)

Soms is het nodig om een array op nul te zetten, nadat de initialisatie is voltooid.

#include <stdlib.h> /* for EXIT_SUCCESS */

#define ARRLEN (10)

int main(void)
{
  int array[ARRLEN]; /* Allocated but not initialised, as not defined static or global. */

  size_t i;
  for(i = 0; i < ARRLEN; ++i)
  {
    array[i] = 0;
  }

  return EXIT_SUCCESS;
}

Een algemene snelkoppeling naar de bovenstaande lus is om memset() van <string.h> . Als u de array passeert zoals hieronder wordt weergegeven, vervalt deze in een aanwijzer naar het eerste element.

memset(array, 0, ARRLEN * sizeof (int)); /* Use size explicitly provided type (int here). */

of

memset(array, 0, ARRLEN * sizeof *array); /* Use size of type the pointer is pointing to. */

Zoals in dit voorbeeld array is een array en niet alleen een pointer naar de 1e element een array (zie Arraylengte over de reden waarom dit belangrijk is) een derde optie om 0-out de array is mogelijk:

 memset(array, 0, sizeof array); /* Use size of the array itself. */

Array lengte

Arrays hebben vaste lengtes die bekend zijn binnen het bereik van hun verklaringen. Desondanks is het mogelijk en soms handig om matrixlengtes te berekenen. Dit kan met name code flexibeler maken wanneer de arraylengte automatisch wordt bepaald door een initialisatieprogramma:

int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

/* size of `array` in bytes */
size_t size = sizeof(array);

/* number of elements in `array` */
size_t length = sizeof(array) / sizeof(array[0]); 

In de meeste contexten waarin een array in een uitdrukking wordt weergegeven, wordt deze echter automatisch geconverteerd naar ("vervalt in") een aanwijzer naar het eerste element. Het geval waarin een array de operand van de operator sizeof , is een van de weinige uitzonderingen. De resulterende aanwijzer is zelf geen array en bevat geen informatie over de lengte van de array waarvan deze is afgeleid. Daarom, als die lengte nodig is in combinatie met de aanwijzer, zoals wanneer de aanwijzer wordt doorgegeven aan een functie, moet deze afzonderlijk worden overgedragen.

Stel bijvoorbeeld dat we een functie willen schrijven om het laatste element van een array van int te retourneren. In het verlengde van het bovenstaande zouden we het zo kunnen noemen:

/* array will decay to a pointer, so the length must be passed separately */
int last = get_last(array, length);

De functie kan als volgt worden geïmplementeerd:

int get_last(int input[], size_t length) {
    return input[length - 1];
}

Merk in het bijzonder dat hoewel de verklaring van parameter input lijkt op die van een matrix, in feite verklaart input als pointer (naar int ). Het is precies hetzelfde als input declareren als int *input . Hetzelfde zou waar zijn, zelfs als een dimensie zou worden gegeven. Dit is mogelijk omdat arrays nooit echte argumenten voor functies kunnen zijn (ze vervallen tot aanwijzers wanneer ze worden weergegeven in functie-aanroepuitdrukkingen), en het kan worden gezien als ezelsbruggetje.

Het is een veel voorkomende fout om te proberen de array-grootte te bepalen met behulp van een aanwijzer, die niet kan werken. DOE DIT NIET:

int BAD_get_last(int input[]) {
    /* INCORRECTLY COMPUTES THE LENGTH OF THE ARRAY INTO WHICH input POINTS: */
    size_t length = sizeof(input) / sizeof(input[0]));

    return input[length - 1];  /* Oops -- not the droid we are looking for */
}

Die specifieke fout is zelfs zo gewoon dat sommige compilers deze herkennen en ervoor waarschuwen. clang zal bijvoorbeeld de volgende waarschuwing geven:

warning: sizeof on array function parameter will return size of 'int *' instead of 'int []' [-Wsizeof-array-argument]
        int length = sizeof(input) / sizeof(input[0]);
                           ^
note: declared here
int BAD_get_last(int input[])
                     ^

Waarden instellen in arrays

Toegang tot matrixwaarden wordt meestal gedaan met vierkante haken:

int val;
int array[10];

/* Setting the value of the fifth element to 5: */
array[4] = 5;

/* The above is equal to: */
*(array + 4) = 5;

/* Reading the value of the fifth element: */
val = array[4];

Als een bijwerking van de operanden dat de operator + uitwisselbaar is (-> commutatief recht), is het volgende equivalent:

*(array + 4) = 5;
*(4 + array) = 5;

dus ook de volgende uitspraken zijn equivalent:

array[4] = 5;
4[array] = 5; /* Weird but valid C ... */

en die twee ook:

val = array[4];
val = 4[array]; /* Weird but valid C ... */

C voert geen grenscontroles uit, toegang tot inhoud buiten de aangegeven array is ongedefinieerd ( Toegang tot geheugen voorbij toegewezen chunk ):

int val;
int array[10];

array[4] = 5;    /* ok */
val = array[4];  /* ok */
array[19] = 20;  /* undefined behavior */
val = array[15]; /* undefined behavior */

Definieer array en toegang tot array-element

#include <stdio.h>
 
#define ARRLEN (10)

int main (void) 
{

   int n[ ARRLEN ]; /* n is an array of 10 integers */
   size_t i, j; /* Use size_t to address memory, that is to index arrays, as its guaranteed to 
                   be wide enough to address all of the possible available memory. 
                   Using signed integers to do so should be considered a special use case, 
                   and should be restricted to the uncommon case of being in the need of 
                   negative indexes. */
 
   /* Initialize elements of array n. */         
   for ( i = 0; i < ARRLEN ; i++ ) 
   {
      n[ i ] = i + 100; /* Set element at location i to i + 100. */
   }
   
   /* Output each array element's value. */
   for (j = 0; j < ARRLEN ; j++ ) 
   {
      printf("Element[%zu] = %d\n", j, n[j] );
   }
 
   return 0;
}

Wijs een array met door de gebruiker gedefinieerde grootte toe en initialiseer deze opnieuw

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


int main (void)
{
  int * pdata;
  size_t n;

  printf ("Enter the size of the array: ");
  fflush(stdout); /* Make sure the prompt gets printed to buffered stdout. */

  if (1 != scanf("%zu", &n)) /* If zu is not supported (Windows?) use lu. */
  {
    fprintf("scanf() did not read a in proper value.\n");
    exit(EXIT_FAILURE);
  }

  pdata = calloc(n, sizeof *pdata);
  if (NULL == pdata) 
  {
    perror("calloc() failed"); /* Print error. */
    exit(EXIT_FAILURE);
  }

  free(pdata); /* Clean up. */

  return EXIT_SUCCESS;
}

Dit programma probeert een niet-getekende integerwaarde van de standaardinvoer te scannen, een geheugenblok toe te wijzen voor een array van n elementen van het type int door de calloc() -functie aan te roepen. Het geheugen wordt door alle laatste nullen geïnitialiseerd.

In geval van succes wordt het geheugen vrijgegeven door de aanroep van free() .

Efficiënt door een array bladeren en de volgorde van de belangrijkste rijen

Arrays in C kunnen worden gezien als een aaneengesloten stuk geheugen. Meer precies, de laatste dimensie van de reeks is het aaneengesloten deel. We noemen dit de rij-hoofdorde . Als we dit begrijpen en het feit dat een cachefout een volledige cacheregel in de cache laadt bij het openen van niet-gecachte gegevens om latere cachefouten te voorkomen, kunnen we zien waarom toegang tot een array met dimensie 10000x10000 met array[0][0] mogelijk array[0][1] zou laden array[0][1] in cache, maar toegang tot array[1][0] direct daarna zou een tweede sizeof(type)*10000 genereren, omdat deze sizeof(type)*10000 bytes verwijderd is van array[0][0] , en daarom zeker niet op dezelfde cache-regel. Daarom is itereren als dit inefficiënt:

#define ARRLEN 10000
int array[ARRLEN][ARRLEN];

size_t i, j;
for (i = 0; i < ARRLEN; ++i)
{
    for(j = 0; j < ARRLEN; ++j)
    {
        array[j][i] = 0;
    }
}

En zo itereren is efficiënter:

#define ARRLEN 10000
int array[ARRLEN][ARRLEN];

size_t i, j;
for (i = 0; i < ARRLEN; ++i)
{
    for(j = 0; j < ARRLEN; ++j)
    {
        array[i][j] = 0;
    }
}

In dezelfde geest is dit de reden waarom bij het omgaan met een array met één dimensie en meerdere indexen (laten we zeggen 2 dimensies hier voor de eenvoud met indexen i en j) het belangrijk is om de array als volgt te doorlopen:

#define DIM_X 10
#define DIM_Y 20

int array[DIM_X*DIM_Y];

size_t i, j;
for (i = 0; i < DIM_X; ++i)
{
    for(j = 0; j < DIM_Y; ++j)
    {
        array[i*DIM_Y+j] = 0;
    }
}

Of met 3 dimensies en indexen i, j en k:

#define DIM_X 10
#define DIM_Y 20
#define DIM_Z 30

int array[DIM_X*DIM_Y*DIM_Z];

size_t i, j, k;
for (i = 0; i < DIM_X; ++i)
{
    for(j = 0; j < DIM_Y; ++j)
    {
        for (k = 0; k < DIM_Z; ++k)
        {
            array[i*DIM_Y*DIM_Z+j*DIM_Z+k] = 0;
        }
    }
}

Of, op een meer generieke manier, wanneer we een array hebben met N1 x N2 x ... x Nd- elementen, d- dimensies en indices genoteerd als n1, n2, ..., nd wordt de offset als volgt berekend

Formule

Afbeelding / formule afkomstig van: https://en.wikipedia.org/wiki/Row-major_order

Multi-dimensionale arrays

De programmeertaal C maakt multidimensionale arrays mogelijk . Hier is de algemene vorm van een multidimensionale arraydeclaratie -

type name[size1][size2]...[sizeN];

Met de volgende declaratie maakt u bijvoorbeeld een driedimensionale (5 x 10 x 4) gehele reeks:

int arr[5][10][4];

Tweedimensionale arrays

De eenvoudigste vorm van een multidimensionale reeks is de tweedimensionale reeks. Een tweedimensionale array is in wezen een lijst met eendimensionale arrays. Om een tweedimensionale gehele reeks dimensies mxn te declareren, kunnen we als volgt schrijven:

type arrayName[m][n];

Waar het type elk geldig C-gegevenstype kan zijn ( int , float , enz.) En arrayName kan elke geldige C-id zijn. Een tweedimensionale array kan worden weergegeven als een tabel met m rijen en n kolommen. Opmerking : de volgorde is belangrijk in C. De array int a[4][3] is niet hetzelfde als de array int a[3][4] . Het aantal rijen komt eerst omdat C een rij is - hoofdtaal.

Een tweedimensionale matrix a , die drie rijen en vier kolommen bevat, kan als volgt worden weergegeven:

visuele lay-out van 2D-array als een tabel

Elk element in de array a wordt dus geïdentificeerd door een elementnaam in de vorm a[i][j] , waarbij a de naam is van de array, i staat voor welke rij en j voor welke kolom. Bedenk dat rijen en kolommen nul zijn geïndexeerd. Dit is zeer vergelijkbaar met wiskundige notatie voor subscripting van 2D-matrices.

Tweedimensionale arrays initialiseren

Multidimensionale arrays kunnen worden geïnitialiseerd door waarden voor haakjes op te geven voor elke rij. Het volgende definieert een array met 3 rijen waarbij elke rij 4 kolommen heeft.

int a[3][4] = {  
   {0, 1, 2, 3} ,   /*  initializers for row indexed by 0 */
   {4, 5, 6, 7} ,   /*  initializers for row indexed by 1 */
   {8, 9, 10, 11}   /*  initializers for row indexed by 2 */
};

De geneste accolades, die de beoogde rij aangeven, zijn optioneel. De volgende initialisatie is gelijk aan het vorige voorbeeld:

int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

Hoewel de methode voor het maken van arrays met geneste accolades optioneel is, wordt deze sterk aangemoedigd omdat deze beter leesbaar en duidelijker is.

Toegang tot tweedimensionale arrayelementen

U krijgt toegang tot een element in een tweedimensionale array met behulp van de subscripts, dwz rijindex en kolomindex van de array. Bijvoorbeeld -

int val = a[2][3];

De bovenstaande instructie neemt het 4e element uit de 3e rij van de array. Laten we het volgende programma bekijken waar we een geneste lus hebben gebruikt om een tweedimensionale array te verwerken:

#include <stdio.h>
 
int main () {

   /* an array with 5 rows and 2 columns*/
   int a[5][2] = { {0,0}, {1,2}, {2,4}, {3,6},{4,8}};
   int i, j;
 
   /* output each array element's value */
   for ( i = 0; i < 5; i++ ) {

      for ( j = 0; j < 2; j++ ) {
         printf("a[%d][%d] = %d\n", i,j, a[i][j] );
      }
   }
   
   return 0;
}

Wanneer de bovenstaande code wordt gecompileerd en uitgevoerd, levert dit het volgende resultaat op:

a[0][0]: 0
a[0][1]: 0
a[1][0]: 1
a[1][1]: 2
a[2][0]: 2
a[2][1]: 4
a[3][0]: 3
a[3][1]: 6
a[4][0]: 4
a[4][1]: 8

Driedimensionale array:

Een 3D-array is in wezen een array van arrays of arrays: het is een array of verzameling 2D-arrays en een 2D-array is een array van 1D-arrays.

visuele lay-out van 2D-array als een verzameling tabellen

Geheugenkaart 3D-array:

3D-array aaneengesloten in het geheugen

Een 3D-array initialiseren:

double cprogram[3][2][4]={ 
{{-0.1, 0.22, 0.3, 4.3}, {2.3, 4.7, -0.9, 2}},
 {{0.9, 3.6, 4.5, 4}, {1.2, 2.4, 0.22, -1}},
 {{8.2, 3.12, 34.2, 0.1}, {2.1, 3.2, 4.3, -2.0}} 
};

We kunnen arrays met een willekeurig aantal dimensies hebben, hoewel het waarschijnlijk is dat de meeste arrays die worden gemaakt, een of twee dimensies zullen hebben.

Door een array bladeren met behulp van pointers

#include <stdio.h>
#define SIZE (10)
int main()
{
    size_t i = 0;
    int *p = NULL;
    int a[SIZE];
    
    /* Setting up the values to be i*i */
    for(i = 0; i < SIZE; ++i) 
    {
        a[i] = i * i;
    }
    
    /* Reading the values using pointers */
    for(p = a; p < a + SIZE; ++p) 
    {
        printf("%d\n", *p);
    }

    return 0;
}

Hier, in de initialisatie van p in de eerste for lusvoorwaarde, vervalt de array a naar een wijzer naar zijn eerste element, zoals op bijna alle plaatsen waar een dergelijke arrayvariabele wordt gebruikt.

Vervolgens voert de ++p aanwijzerberekening uit op de aanwijzer p en loopt een voor een door de elementen van de array en verwijst ernaar door ze te derefereren met *p .

Multidimensionale arrays doorgeven aan een functie

Multidimensionale arrays volgen dezelfde regels als eendimensionale arrays wanneer ze aan een functie worden doorgegeven. De combinatie van decay-to-pointer, operatorprioriteit en de twee verschillende manieren om een multidimensionale array (array van arrays versus array van pointers) te declareren, kan de declaratie van dergelijke functies niet-intuïtief maken. Het volgende voorbeeld toont de juiste manieren om multidimensionale arrays te passeren.

#include <assert.h>
#include <stdlib.h>

/* When passing a multidimensional array (i.e. an array of arrays) to a
   function, it decays into a pointer to the first element as usual.  But only
   the top level decays, so what is passed is a pointer to an array of some fixed
   size (4 in this case). */
void f(int x[][4]) {
    assert(sizeof(*x) == sizeof(int) * 4);
}

/* This prototype is equivalent to f(int x[][4]).
   The parentheses around *x are required because [index] has a higher
   precedence than *expr, thus int *x[4] would normally be equivalent to int
   *(x[4]), i.e. an array of 4 pointers to int.  But if it's declared as a
   function parameter, it decays into a pointer and becomes int **x, 
   which is not compatable with x[2][4]. */
void g(int (*x)[4]) {
    assert(sizeof(*x) == sizeof(int) * 4);
}

/* An array of pointers may be passed to this, since it'll decay into a pointer
   to pointer, but an array of arrays may not. */
void h(int **x) {
    assert(sizeof(*x) == sizeof(int*));
}

int main(void) {
    int foo[2][4];
    f(foo);
    g(foo);

    /* Here we're dynamically creating an array of pointers.  Note that the 
       size of each dimension is not part of the datatype, and so the type 
       system just treats it as a pointer to pointer, not a pointer to array
       or array of arrays. */
    int **bar = malloc(sizeof(*bar) * 2);
    assert(bar);
    for (size_t i = 0; i < 2; i++) {
        bar[i] = malloc(sizeof(*bar[i]) * 4);
        assert(bar[i]);
    }

    h(bar);
    
    for (size_t i = 0; i < 2; i++) {
        free(bar[i]);
    }
    free(bar);
}

Zie ook

Arrays doorgeven aan functies



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow