Я видел много онлайн-ресурсов о доступе к 2D-массиву в C, и все они показывают только один ответ *( *(arr + i) + j))
Но, к сожалению, это дало мне эту ошибку
error: invalid type argument of unary ‘*’ (have ‘int’)
27 | printf("%d ",*(*(arr + i) + j));
Мой код:
void pointer_string_array_indexing(char *arr){
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 4; ++j)
{
//printf("%c : %d ",*(arr+4*i+j),(arr+4*i+j)); //this will give correct answer
printf("%c ",*( *(arr + i) + j)); // compilation error
}
printf("\n");
}
}
int main(){
char S[][4] = {"abc","efg"};
pointer_string_array_indexing(S);
return 0;
}
Я знаю, что это очень известный и повторяющийся вопрос, но, пожалуйста, предоставьте фрагмент кода решения. Я использую Ubuntu-20 (впрочем, это не имеет значения)
@JHBonarius здесь я даю 2 ссылки, geeksforgeeks.org/pointer-array-array-pointer overiq.com/c-programming-101/pointers-and-2-d-arrays
@user3121023 user3121023 Да, это работает. Не могли бы вы уточнить свой ответ, почему это работает и где я делаю неправильно в своем коде?
Беглый взгляд показывает, что источники копировали друг друга. Но в любом случае ни один из источников не дает примера, как передать 2D-массив в функцию, поэтому вам нужны источники получше.
@JHBonarius Спасибо за ваш ответ, можете ли вы упомянуть какой-нибудь полезный ресурс, из которого я могу его получить?
*( *(arr + i) + j)
— это очень запутанный способ написания arr[i][j]
.
И вы должны прочитать все ошибки и предупреждения, которые дает вам компилятор, в том числе "передача аргумента 1" pointer_string_array_indexing "из несовместимого типа указателя"
Давняя традиция C заключается в создании NULL
терминированных массивов, а не в блокировке их определенного размера. Как я бы рекомендовал это как char **s = { "abc", "def", NULL }
. Затем вы можете перебирать этот список, пока не получите указатель NULL
. Это не чистый многомерный массив, но это своего рода то, с чем вы работаете с данными в его самом элементарном представлении. Вы по-прежнему можете получить к нему доступ как s[i][j]
, если хотите.
Чтобы понять, почему один способ работает, а другой нет, вы должны понять некоторые странные вещи, связанные с тем, как C выделяет строки и массивы, и почему иногда строки являются массивами, а иногда — указателями.
Ваш массив объявлен как char S[][4]
. Все символы в S
находятся в непрерывном блоке памяти. Когда вы обращаетесь к S[i][j]
, скомпилированный код определяет адрес символа, на который вы ссылаетесь, вычисляя смещение, представленное i
и j
, как char *indexed_character_address = S + i*4 + j
. Теперь скомпилированный код использует indexed_character_address
для загрузки или сохранения, в зависимости от того, как используется S[i][j]
.
Еще один совершенно другой, но очень похожий способ объявления массива строк выглядит следующим образом:
char *T[] = { "tuv", "wxyz" };
То, что компилятор делает за кулисами для создания этого массива, очень похоже на это:
const char hidden_tuv[4] = { 't', 'u', 'v', '\0' };
const char hidden_wxyz[5] = { 'w', 'x', 'y', 'z', '\0' };
char *T[2] = { hidden_tuv, hidden_wxyz };
Строки "tuv"
и "wxyz"
сначала размещаются в каком-то произвольном месте, нет гарантии, что они будут первыми или что они будут где-то рядом друг с другом. Затем создается и инициализируется другой массив с этими двумя произвольными указателями.
Код, сгенерированный компилятором для доступа к T[i][j]
, работает совершенно иначе, чем для S
. Сначала он вычисляет адрес i
-го указателя строки в T
, затем загружает из памяти один из начальных адресов строки, затем использует j
для смещения от начала этого адреса. Так:
char **address_in_T = T + i;
char *string_start_address = *address_in_T;
// now string_start_address is either hidden_tuv or hidden_wxyz
char *indexed_character_address = string_start_address + j;
Этот подробный код эквивалентен этому небольшому выражению:
char *indexed_character_address = T[i] + j;
// or &(T[i][j])
// or *(*(T + i) + j)
Ваша переменная S
представляет собой двумерный массив символов, инициализированный строковыми данными, с типом char[2][4]
. Моя переменная T
представляет собой одномерный массив указателей на символы типа (char *)[2]
.
Действительно очень запутанная часть здесь заключается в том, что, хотя эти две переменные имеют очень разные типы, синтаксис C может сделать доступ к ним очень похожим.
char c1 = S[i][j]; // actually reads *(S + i*4 + j)
char c2 = T[i][j]; // actually reads *( *(T + i) + j)
Пожалуйста, поделитесь некоторыми из этих «многих ресурсов» (чтобы мы могли показать вам, где вы неправильно их истолковываете). И поделитесь своей ошибкой.