Подтвердить что ты не робот

Передача массивов и матриц в функции указателей и указателей на указатели в C

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

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" выглядит нормально.

Спасибо:)

4b9b3361

Ответ 1

Ну, это, конечно, не совсем понятно для сообщества C, как можно видеть, просматривая 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 вашего массива:

(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 байт, кроме начала 2-мерного массива. Он декодирует эту информацию в типе указатель-массив.

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

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

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

Ответ 2

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

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

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

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

bar(int *matrix, int width, int height);

Ответ 3

Проблема заключается в том, что матрица структуры данных [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" - это массив из десяти указателей, и вы можете передать его указателем, следовательно, получить второй *.

Ответ 4

Вот код для практики: он содержит все возможные типы передачи 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;
}

Ответ 5

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

bar( int* matrix )

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

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

Ответ 6

int **matrix

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

int matrix[10][10]

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

void bar(int *matrix[])