Szukaj…


Wprowadzenie

Tablice są pochodnymi typami danych, reprezentującymi uporządkowany zbiór wartości („elementów”) innego typu. Większość tablic w C ma stałą liczbę elementów dowolnego typu, a ich reprezentacja przechowuje elementy w pamięci w sposób ciągły, bez przerw i wypełnień. C dopuszcza tablice wielowymiarowe, których elementami są inne tablice, a także tablice wskaźników.

C obsługuje dynamicznie przydzielane tablice, których rozmiar jest określany w czasie wykonywania. C99 i nowsze wersje obsługują tablice o zmiennej długości lub VLA.

Składnia

  • wpisz nazwę [długość]; / * Zdefiniuj tablicę „typu” o nazwie „nazwa” i długości „długość”. * /
  • int arr [10] = {0}; / * Zdefiniuj tablicę i zainicjuj WSZYSTKIE elementy na 0. * /
  • int arr [10] = {42}; / * Zdefiniuj tablicę i zainicjuj 1. elementy do 42, a pozostałe do 0. * /
  • int arr [] = {4, 2, 3, 1}; / * Zdefiniuj i zainicjuj tablicę o długości 4. * /
  • arr [n] = wartość; / * Ustaw wartość na indeks n. * /
  • wartość = arr [n]; / * Uzyskaj wartość o indeksie n. * /

Uwagi

Dlaczego potrzebujemy tablic?

Tablice zapewniają sposób organizowania obiektów w agregaty o własnym znaczeniu. Na przykład ciągi C to tablice znaków ( char ) i ciąg taki jak „Hello, World!” ma znaczenie jako agregat, który nie jest nieodłącznie związany z postaciami. Podobnie, tablice są powszechnie używane do reprezentowania wektorów matematycznych i macierzy, a także list różnego rodzaju. Co więcej, bez jakiegoś sposobu grupowania elementów należałoby zająć się każdym z osobna, na przykład za pomocą oddzielnych zmiennych. Jest nie tylko niewygodny, ale nie łatwo pomieści kolekcje o różnych długościach.

Tablice są domyślnie konwertowane na wskaźniki w większości kontekstów .

Z wyjątkiem pojawiania się jako operand operatora sizeof operatora _Alignof (C2011) lub operatora unary & (adres) lub jako dosłowny ciąg znaków używany do inicjalizacji (innej) tablicy, tablica jest domyślnie konwertowana na ( „rozpada się”) wskaźnik do pierwszego elementu. Ta niejawna konwersja jest ściśle powiązana z definicją operatora indeksowania tablicy ( [] ): wyrażenie arr[idx] jest zdefiniowane jako równoważne *(arr + idx) . Ponadto, ponieważ arytmetyka wskaźnika jest przemienna, *(arr + idx) jest również równoważne *(idx + arr) , co z kolei jest równoważne idx[arr] . Wszystkie te wyrażenia są poprawne i mają tę samą wartość, pod warunkiem, że idx lub arr jest wskaźnikiem (lub tablicą, która rozpada się na wskaźnik), druga jest liczbą całkowitą, a liczba całkowita jest poprawnym indeksem w tablicy do którego wskazuje wskaźnik.

W szczególnym przypadku zauważ, że &(arr[0]) jest równoważne z &*(arr + 0) , co upraszcza arr . Wszystkie te wyrażenia są wymienne wszędzie tam, gdzie ostatnie zanikają do wskaźnika. To po prostu wyraża ponownie, że tablica rozpada się na wskaźnik do swojego pierwszego elementu.

Natomiast jeśli operator adresu zostanie zastosowany do tablicy typu T[N] ( tj. &arr ), wówczas wynik ma typ T (*)[N] i wskazuje na całą tablicę. Różni się to od wskaźnika do pierwszego elementu tablicy przynajmniej w odniesieniu do arytmetyki wskaźnika, która jest zdefiniowana pod względem wielkości typu wskazanego.

Parametry funkcji nie są tablicami .

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

Chociaż pierwsza deklaracja foo używa składni tablicowej dla parametru a , taka składnia służy do deklaracji parametru funkcji, deklaruje ten parametr jako wskaźnik do typu elementu tablicy. Zatem druga sygnatura dla foo() jest semantycznie identyczna z pierwszą. Odpowiada to zanikowi wartości tablic wskaźnikom, w których pojawiają się one jako argumenty wywołania funkcji, tak że jeśli zmienna i parametr funkcji są zadeklarowane z tym samym typem tablicy, to wartość tej zmiennej nadaje się do użycia w wywołaniu funkcji jako argument powiązany z parametrem.

Deklarowanie i inicjowanie tablicy

Ogólna składnia deklarowania tablicy jednowymiarowej to

type arrName[size];

gdzie type może być dowolnym type wbudowanym lub typami zdefiniowanymi przez użytkownika, takimi jak struktury, arrName jest identyfikatorem zdefiniowanym przez użytkownika, a size jest stałą całkowitą.

Zadeklarowanie tablicy (w tym przypadku tablicy 10 zmiennych int) odbywa się w następujący sposób:

int array[10];

obecnie posiada nieokreślone wartości. Aby upewnić się, że podczas deklarowania zawiera wartości zerowe, możesz to zrobić:

int array[10] = {0};

Macierze mogą mieć inicjatorów, w tym przykładzie deklaruje tablicę 10 int „s, w którym pierwsze trzy int ” S będzie zawierać wartości 1 , 2 , 3 , wszystkie inne wartości będą Zero

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

W powyższej metodzie inicjalizacji pierwsza wartość z listy zostanie przypisana do pierwszego elementu tablicy, druga wartość zostanie przypisana do drugiego elementu tablicy i tak dalej. Jeśli rozmiar listy jest mniejszy niż rozmiar tablicy, to tak jak w powyższym przykładzie, pozostałe elementy tablicy zostaną zainicjowane na zera. Dzięki wyznaczonej inicjalizacji listy (ISO C99) możliwa jest jawna inicjalizacja elementów tablicy. Na przykład,

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

W większości przypadków kompilator może wydedukować długość tablicy, co można osiągnąć, pozostawiając puste nawiasy kwadratowe:

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

Zadeklarowanie tablicy o zerowej długości jest niedozwolone.

C99 C11

Tablice o zmiennej długości (w skrócie VLA) zostały dodane w C99, a opcjonalnie w C11. Są one równe zwykłym tablicom, z jedną ważną różnicą: długości nie trzeba znać w czasie kompilacji. VLA mają automatyczny czas przechowywania. Tylko wskaźniki do VLA mogą mieć statyczny czas przechowywania.

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

Ważny:

VLA są potencjalnie niebezpieczne. Jeśli tablica vla w powyższym przykładzie wymaga więcej miejsca na stosie niż dostępna, stos się przepełni. Dlatego korzystanie z VLA jest często odradzane w przewodnikach po stylu oraz w książkach i ćwiczeniach.

Czyszczenie zawartości tablicy (zerowanie)

Czasami konieczne jest ustawienie tablicy na zero po zakończeniu inicjalizacji.

#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;
}

Typowym skrótem do powyższej pętli jest użycie memset() z <string.h> . Przekazanie array jak pokazano poniżej, powoduje jej rozpad do wskaźnika do pierwszego elementu.

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

lub

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

Ponieważ w tym przykładzie array jest tablicą, a nie tylko wskaźnikiem do pierwszego elementu tablicy (zobacz Długość tablicy, dlaczego jest to ważne), trzecia opcja zerowania tablicy jest możliwa:

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

Długość tablicy

Tablice mają ustalone długości, które są znane w zakresie ich deklaracji. Niemniej jednak możliwe i czasami wygodne jest obliczanie długości tablic. W szczególności może to uczynić kod bardziej elastycznym, gdy długość tablicy jest określana automatycznie na podstawie inicjalizatora:

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]); 

Jednak w większości kontekstów, w których tablica pojawia się w wyrażeniu, jest ona automatycznie konwertowana na wskaźnik („rozpada się”) na pierwszy element. Przypadek, w którym tablica jest operandem operatora sizeof , jest jednym z niewielkiej liczby wyjątków. Wynikowy wskaźnik sam w sobie nie jest tablicą i nie przenosi żadnych informacji o długości tablicy, z której został uzyskany. Dlatego jeśli ta długość jest potrzebna w połączeniu ze wskaźnikiem, na przykład gdy wskaźnik jest przekazywany do funkcji, wówczas musi być przenoszony osobno.

Załóżmy na przykład, że chcemy napisać funkcję zwracającą ostatni element tablicy int . Kontynuując powyższe, możemy to tak nazwać:

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

Funkcję można zaimplementować w następujący sposób:

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

Zwróć uwagę w szczególności, że chociaż deklaracja input parametrów przypomina deklarację tablicy, w rzeczywistości deklaruje ona input jako wskaźnik (do int ). Jest to dokładnie równoważne z deklarowaniem input jako input int *input . To samo byłoby prawdą, nawet gdyby podano wymiar. Jest to możliwe, ponieważ tablice nigdy nie mogą być rzeczywistymi argumentami funkcji (rozpadają się na wskaźniki, gdy pojawiają się w wyrażeniach wywołań funkcji), i można je postrzegać jako mnemoniczne.

Bardzo częstym błędem jest próba ustalenia rozmiaru tablicy za pomocą wskaźnika, który nie może działać. NIE RÓB TEGO:

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 */
}

W rzeczywistości ten konkretny błąd jest tak powszechny, że niektóre kompilatory go rozpoznają i ostrzegają przed nim. clang , na przykład, wyświetli następujące ostrzeżenie:

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[])
                     ^

Ustawianie wartości w tablicach

Dostęp do wartości tablic odbywa się zwykle za pomocą nawiasów kwadratowych:

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];

Jako efekt uboczny wymiany argumentów na operatorze + (-> prawo przemienne), równoważne jest:

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

więc również następne stwierdzenia są równoważne:

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

i te dwa również:

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

C nie wykonuje żadnych kontroli granic, dostęp do zawartości poza zadeklarowaną tablicą jest niezdefiniowany ( Dostęp do pamięci poza przydzieloną porcją ):

int val;
int array[10];

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

Zdefiniuj tablicę i uzyskaj dostęp do elementu tablicy

#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;
}

Przydziel i zainicjuj zero tablicy o rozmiarze zdefiniowanym przez użytkownika

#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;
}

Ten program próbuje zeskanować wartość całkowitą bez znaku ze standardowego wejścia, przydzielić blok pamięci dla tablicy n elementów typu int , wywołując funkcję calloc() . Pamięć jest inicjowana do wszystkich zer przez te ostatnie.

W przypadku powodzenia pamięć jest zwalniana przez wywołanie free() .

Wydajne iterowanie przez tablicę i porządek rzędu

Tablice w C mogą być postrzegane jako ciągły fragment pamięci. Dokładniej, ostatni wymiar tablicy to przylegająca część. Nazywamy to porządkiem głównym . Rozumiejąc to i fakt, że błąd pamięci podręcznej ładuje całą linię pamięci podręcznej do pamięci podręcznej podczas uzyskiwania dostępu do niebuforowanych danych, aby zapobiec kolejnym błędom pamięci podręcznej, możemy zrozumieć, dlaczego dostęp do tablicy o wymiarach 10000 x 10000 za pomocą array[0][0] potencjalnie załadowałby array[0][1] w pamięci podręcznej, ale dostęp do array[1][0] zaraz po niej wygenerowałby drugi błąd pamięci podręcznej, ponieważ jest to sizeof(type)*10000 bajtów od array[0][0] , a zatem na pewno nie na tej samej linii pamięci podręcznej. Dlatego iteracja w ten sposób jest nieefektywna:

#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;
    }
}

I takie powtarzanie jest bardziej wydajne:

#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;
    }
}

W tym samym duchu dlatego właśnie, gdy mamy do czynienia z tablicą z jednym wymiarem i wieloma indeksami (powiedzmy tutaj 2 wymiary dla uproszczenia z indeksami i i j), ważne jest, aby iterować po tablicy w ten sposób:

#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;
    }
}

Lub z 3 wymiarami i indeksami i, j i 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;
        }
    }
}

Lub w bardziej ogólny sposób, gdy mamy tablicę z elementami N1 x N2 x ... x Nd , d wymiarami i indeksami oznaczonymi jako n1, n2, ... i przesunięcie jest obliczane w ten sposób

Formuła

Zdjęcie / wzór pochodzi z: https://en.wikipedia.org/wiki/Row-major_order

Tablice wielowymiarowe

Język programowania C umożliwia tablice wielowymiarowe . Oto ogólna forma deklaracji tablic wielowymiarowych -

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

Na przykład następująca deklaracja tworzy trójwymiarową tablicę liczb całkowitych (5 x 10 x 4):

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

Tablice dwuwymiarowe

Najprostszą formą tablicy wielowymiarowej jest tablica dwuwymiarowa. Dwuwymiarowa tablica jest w istocie listą jednowymiarowych tablic. Aby zadeklarować dwuwymiarową tablicę liczb całkowitych o wymiarach mxn, możemy napisać w następujący sposób:

type arrayName[m][n];

Gdzie type może być dowolnym poprawnym typem danych C ( int , float , itp.), A arrayName może być dowolnym poprawnym identyfikatorem C. Dwuwymiarową tablicę można zwizualizować jako tabelę z m rzędami i n kolumnami. Uwaga : Kolejność ma znaczenie w C. Tablica int a[4][3] nie jest taka sama jak tablica int a[3][4] . Liczba wierszy jest pierwsze jako C to rząd -major języka.

Dwuwymiarową tablicę a , która zawiera trzy rzędy i cztery kolumny, można przedstawić w następujący sposób:

wizualny układ tablicy 2D jako tabeli

Zatem każdy element w tablicy a jest identyfikowany przez nazwę elementu w postaci a[i][j] , gdzie a jest nazwą tablicy, i oznacza, który wiersz, a j reprezentuje kolumnę. Przypomnij sobie, że wiersze i kolumny są zerowane. Jest to bardzo podobne do zapisu matematycznego do indeksowania macierzy 2-D.

Inicjowanie tablic dwuwymiarowych

Tablice wielowymiarowe można zainicjować, określając wartości w nawiasach dla każdego wiersza. Poniżej zdefiniowano tablicę z 3 wierszami, w których każdy wiersz ma 4 kolumny.

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 */
};

Zagnieżdżone nawiasy klamrowe, które wskazują zamierzony rząd, są opcjonalne. Następująca inicjalizacja jest równoważna z poprzednim przykładem:

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

Chociaż metoda tworzenia tablic z zagnieżdżonymi nawiasami klamrowymi jest opcjonalna, zdecydowanie zaleca się ją, ponieważ jest bardziej czytelna i wyraźniejsza.

Dostęp do dwuwymiarowych elementów macierzy

Dostęp do elementu w dwuwymiarowej tablicy można uzyskać za pomocą indeksów dolnych, tj. Indeksu wierszy i indeksu kolumny tablicy. Na przykład -

int val = a[2][3];

Powyższa instrukcja pobierze czwarty element z trzeciego rzędu tablicy. Sprawdźmy następujący program, w którym użyliśmy zagnieżdżonej pętli do obsługi tablicy dwuwymiarowej:

#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;
}

Gdy powyższy kod zostanie skompilowany i wykonany, daje następujący wynik:

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

Tablica trójwymiarowa:

Tablica 3D jest zasadniczo tablicą tablic tablic: jest tablicą lub kolekcją tablic 2D, a tablica 2D to tablica tablic 1D.

wizualny układ tablicy 2D jako zbioru tabel

Mapa pamięci macierzy 3D:

Tablica 3D ułożona w sposób ciągły w pamięci

Inicjowanie tablicy 3D:

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}} 
};

Możemy mieć tablice o dowolnej liczbie wymiarów, chociaż prawdopodobne jest, że większość tworzonych tablic będzie miała jeden lub dwa wymiary.

Iteracja po tablicy za pomocą wskaźników

#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;
}

Tutaj, w inicjalizacji p W pierwszym for stanu pętli, tablica a rozpady do wskaźnika do jej pierwszego elementu, jak byłoby w prawie wszystkich miejsc, w których stosuje się taką zmienną tablicową.

Następnie ++p wykonuje arytmetykę wskaźnika na wskaźniku p i przechodzi jeden po drugim przez elementy tablicy i odwołuje się do nich, usuwając z nich odwołanie za pomocą *p .

Przekazywanie tablic wielowymiarowych do funkcji

Tablice wielowymiarowe podczas przekazywania ich do funkcji podlegają tym samym regułom, co tablice jednowymiarowe. Jednak połączenie wskaźnika rozpadu do wskaźnika, pierwszeństwa operatora i dwóch różnych sposobów deklarowania tablicy wielowymiarowej (tablica tablic vs tablica wskaźników) może sprawić, że deklaracja takich funkcji nie będzie intuicyjna. Poniższy przykład pokazuje prawidłowe sposoby przekazywania tablic wielowymiarowych.

#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);
}

Zobacz też

Przekazywanie tablic do funkcji



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow