Передача массивов и матриц в функции указателей и указателей на указатели в 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" выглядит нормально.
Спасибо:)
Ответы
Ответ 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[])