Передача массивов и матриц в функции в качестве указателей и указателей на указатели в C - PullRequest
14 голосов
/ 13 февраля 2009

С учетом следующего кода:

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

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

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

    foo( array );
    bar( matrix );

    return 0;
}

Я не понимаю, почему я получаю это предупреждение:

предупреждение: передача аргумента 1 'bar' из несовместимого типа указателя

Хотя вызов 'foo' кажется нормальным.

Спасибо:)

Ответы [ 6 ]

46 голосов
/ 13 февраля 2009

Ну, это, конечно, не очень понятно сообществом Си, что можно увидеть, взглянув на SO. Волшебство в том, что все следующее полностью, 100%, эквивалентно :

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

Очень важно провести различие между указателем и массивом. Массив не является указателем . Массив может быть преобразован в указатель на его первый элемент. Если у вас есть указатель, у вас есть это:

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

Однако, если у вас есть массив, у вас есть это:

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

С указателем данные находятся на другой планете, но связаны с указателем. Массив содержит сами данные. Теперь многомерный массив - это просто массив массивов. Массивы вложены в родительский массив. Итак, размер вашего массива:

(sizeof(int) * 10) * 10

Это потому, что у вас есть 10 массивов, каждый из которых является массивом из 10 целых чисел. Теперь, если вы хотите передать этот массив, он конвертируется. А к чему? Указатель на его первый элемент. Тип элемента не указатель, а массив. Как следствие, вы передаете указатель на массив из 10 int:

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

Это ни массив int*, ни int**. Вы можете спросить, почему массив не передается как int**. Это потому, что компилятор должен знать длину строки. Если вы сделаете array[1][0], компилятор обратится к месту sizeof(int) * 10 байтов, кроме начала двумерного массива. Он декодирует эту информацию в виде указателя на массив.

Итак, вы должны выбрать один из приведенных выше полностью эквивалентных прототипов функций. Естественно, последний просто сбивает с толку. Компилятор просто молча игнорирует любое число, записанное в самом внешнем измерении, если параметр объявлен как массив. Так что я бы тоже не стал использовать вторую последнюю версию. Лучше всего использовать первую или вторую версию. Важно помнить, что C не имеет (реальных) параметров массива ! Параметр в конце будет указателем (в данном случае указателем на массив).

Обратите внимание, что многомерный случай выше похож на вырожденный, одномерный случай ниже. Все следующие 4 версии полностью эквивалентны:

void foo(int *array);
void foo(int array[]);
void foo(int array[10]);
void foo(int array[42]);
15 голосов
/ 13 февраля 2009

Передача многомерных массивов в C - сложная тема. См. этот FAQ .

Вопрос, который нужно задать, - как вы будете использовать bar. Если вы всегда знаете, что ему будет передан массив 10x10, перепишите его как

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

Если вы хотите работать с массивами разных размеров, вам, возможно, придется указать длины:

bar(int *matrix, int width, int height);
6 голосов
/ 13 февраля 2009

Проблема в том, что матрица структуры данных [10] [10] на самом деле не является таблицей из десяти указателей на массив [10], а представляет собой последовательный массив из 100 целых чисел. Подходящая подпись для бара

bar (int matrix[10][10])

Если вы на самом деле хотите представить матрицу с помощью косвенного обращения и в качестве типа параметра для бара указать матрицу int **, то вам нужно распределить ее по-другому:

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

Теперь 'matrix' соответствует типу int **. 'matrix' - это массив из десяти указателей, и вы можете передать его по указателю, следовательно, получите второй *.

2 голосов
/ 30 июня 2012

Вот некоторый код для практики - он содержит все возможные типы передачи 2-мерного массива и код для доступа к значениям элемента

#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 голосов
/ 13 февраля 2009

Вы должны определить бар как:

bar( int* matrix )

В C все массивы должны быть переданы как int* (или type_of_element* для других типов).

int ** было бы хорошо, если бы ваши данные были массивом указателей. int[*data[] например. Вот что вы получаете в main(int argc, char *argv[]).

0 голосов
/ 13 февраля 2009
int **matrix

будет означать, что у вас есть указатель на указатель на int. Это обычно используется для указания указателя на массив указателей (также называемый вектором). Это определенно НЕ в случае с

int matrix[10][10]

, который является скорее указателем на один раздел памяти размером 10x10 дюймов. Попробуйте изменить на:

void bar(int *matrix[])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...