Przekazywanie tablic i macierzy do funkcji jako wskaźników i wskaźników do wskaźników w C

Podano następujący kod:

void
foo( int* array ) 
{
    // ...
}

void
bar( int** matrix ) 
{
    // ...
}

int
main( void ) {
    int array[ 10 ];
    int matrix[ 10 ][ 10 ];

    foo( array );
    bar( matrix );

    return 0;
}

Nie rozumiem dlaczego dostaję to Ostrzeżenie:

Ostrzeżenie: przekazanie argumentu 1 'bar' z niezgodnego typu wskaźnika

/ Align = "left" /

Dzięki:)

Author: Auron, 2009-02-13

6 answers

Cóż, na pewno nie jest to dobrze zrozumiane przez społeczność C, co widać patrząc na to. Magia jest, wszystkie z poniższych są całkowicie, 100%, równoważne :

void foo(int (*array)[10]);
void foo(int array[][10]);
void foo(int array[10][10]);
void foo(int array[42][10]);

To jest bardzo ważne, aby narysować rozróżnienie wskaźnika i tablicy. tablica nie jest wskaźnikiem. Tablica może być przekonwertowana na wskaźnik do pierwszego elementu. Jeśli masz wskaźnik masz to:

--------
| ptr  |  -------> data
--------

Jednakże, jeśli masz tablicę, masz to:

---------------------------
| c1 | c2 | c3 | ... | cn |
---------------------------

Ze wskaźnikiem, dane są na zupełnie innej planecie, ale połączone ze wskaźnikiem. Tablica zawiera same dane. wielowymiarowa tablica jest tylko tablicą tablic. tablice są zagnieżdżane do tablicy nadrzędnej. Tak więc rozmiar tablicy wynosi:

(sizeof(int) * 10) * 10

To dlatego, że masz 10 tablic, z których wszystkie są tablicami 10 liczb całkowitych. Teraz, jeśli chcesz przekazać tę tablicę, zostanie ona przekonwertowana. Ale do czego? Wskaźnik do pierwszego elementu. Element type is not A pointer, but an array. W konsekwencji przekazujesz wskaźnik do tablicy 10 int:

int (*)[10] // a pointer to an int[10]

Nie jest ani tablicą int*, ani int**. Możesz zapytać, dlaczego tablica nie jest przekazywana jako int**. Ponieważ kompilator musi znać długość wiersza. Jeśli wykonasz array[1][0], kompilator zajmie miejsce sizeof(int) * 10 bajtów poza początkiem tablicy 2 wymiarowej. Dekoduje te informacje w typie wskaźnik-tablica.

Więc musisz wybrać spośród jeden z powyższych w pełni równoważnych prototypów funkcji. Oczywiście, ten ostatni jest po prostu mylący. Kompilator po cichu ignoruje dowolną liczbę zapisaną w najbardziej zewnętrznym wymiarze, jeśli parametr jest zadeklarowany jako tablica. Dlatego też nie użyłbym drugiej ostatniej wersji. Najlepiej jest użyć pierwszej lub drugiej wersji. Ważne jest, aby pamiętać, że C nie ma (rzeczywistych) parametrów tablicy! Parametr będzie wskaźnikiem na końcu (wskaźnik do tablicy w tym przypadku).

Uwaga Jak wielowymiarowy przypadek powyżej jest podobny do zdegenerowanego, jednowymiarowego przypadku poniżej. Wszystkie następujące 4 wersje są w pełni równoważne:

void foo(int *array);
void foo(int array[]);
void foo(int array[10]);
void foo(int array[42]);
 44
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-14 06:25:35

Przekazywanie wielowymiarowych tablic w C jest trudnym tematem. Zobacz ten FAQ .

Pytanie brzmi, jak będziesz używać bar. Jeśli zawsze wiesz, że zostanie przekazana tablica 10x10, przepisz ją jako

bar(int matrix[10][10]);

Jeśli chcesz poradzić sobie z tablicami o różnych wymiarach, być może będziesz musiał przejść w długościach:

bar(int *matrix, int width, int height);
 14
Author: Mark Pim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-13 17:41:15

Problem polega na tym, że macierz struktury danych[10][10] w rzeczywistości nie jest tablicą dziesięciu wskaźników do tablicy[10], ale jest tablicą sekwencyjną 100 liczb całkowitych. Właściwy podpis dla paska to

bar (int matrix[10][10])

Jeśli chcesz reprezentować macierz za pomocą indrection i mieć int * * macierz jako typ parametru dla bar, musisz ją przydzielić inaczej:

int *matrix[10];
int my_data[100];
int i;
for (i = 0; i < 10; i++) { matrix[i] = &(my_data[i * 10]); }
bar(matrix);

Teraz 'matrix' pasuje do typu int**. 'macierz' jest tablicą dziesięciu wskaźników i można ją przekazać za pomocą wskaźnika, stąd otrzymanie drugiego *.

 6
Author: Antti Huima,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-13 17:36:04

Oto trochę kodu do Ćwiczenia-zawiera on wszystkie możliwe typy przekazywania tablicy 2dimensional oraz kod dostępu do wartości elementów

#include <stdio.h>

#define NUMROWS 2
#define NUMCOLUMNS 5

#define FILL_ARRAY() \
    *array[0] = '1'; \
    (*array)[7] = '2'; \
    *(array[1]) = '3'; \
    *(*(array+1)+1) = '4'; \
    *(array[0]+3) = '5'; \
    *(*array+2) = '7'; \
    array[0][1] = '6'; 

void multi_01( char (*array)[NUMCOLUMNS] )       { FILL_ARRAY(); }
void multi_02( char array[][NUMCOLUMNS] )        { FILL_ARRAY(); }
void multi_03( char array[NUMROWS][NUMCOLUMNS] ) { FILL_ARRAY(); }
void multi_04( char **array )                    { FILL_ARRAY(); }
void multi_05( char *array[] )                   { FILL_ARRAY(); }
void multi_06( char *array[NUMCOLUMNS] )         { FILL_ARRAY(); }

int main(int argc, char **argv)
{
    int i;
    char mystr[NUMROWS][NUMCOLUMNS] = { { 'X', 'X', 'X', 'X'}, {'X','X','X'} };
    char *pmystr[sizeof(mystr)/sizeof(*mystr)];
    int numcolumns = sizeof(*mystr);
    int numrows = sizeof(mystr)/sizeof(*mystr);
    for( i=0; i<numrows; i++ ) pmystr[i] = *(mystr+i);

    multi_01( mystr );  multi_02( mystr );  multi_03( mystr );
    multi_04( pmystr ); multi_05( pmystr ); multi_06( pmystr );

    printf("array '%s', '%s'\n", mystr[0], mystr[1]);

    getc(stdin);
    return 0;
}
 2
Author: Ulterior,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-30 13:00:53

Powinieneś zdefiniować bar jako:

bar( int* matrix )

W C wszystkie tablice powinny być przekazywane jako int* (lub type_of_element* dla innych typów).

int ** byłoby w porządku, gdyby Twoje dane były naprawdę tablicą wskaźników. Na przykład. To jest to, co dostajesz main(int argc, char *argv[]).

 1
Author: kmkaplan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-13 17:31:26
int **matrix

Wskazuje, że masz wskaźnik do wskaźnika do int. Jest to powszechnie używane do wskazania wskaźnika do tablicy wskaźników (zwanych również wektorem). Tak na pewno nie jest w przypadku

int matrix[10][10]

, który jest bardziej wskaźnikiem do pojedynczej sekcji pamięci o rozmiarze 10x10 int. Spróbuj zmienić na:

void bar(int *matrix[])
 -1
Author: dwc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-13 17:42:32