Ошибка при доступе к двумерному массиву символов из функции в C

Я видел много онлайн-ресурсов о доступе к 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 18.12.2020 17:10

@JHBonarius здесь я даю 2 ссылки, geeksforgeeks.org/pointer-array-array-pointer overiq.com/c-programming-101/pointers-and-2-d-arrays

Karm Patel 18.12.2020 17:25

@user3121023 user3121023 Да, это работает. Не могли бы вы уточнить свой ответ, почему это работает и где я делаю неправильно в своем коде?

Karm Patel 18.12.2020 17:26

Беглый взгляд показывает, что источники копировали друг друга. Но в любом случае ни один из источников не дает примера, как передать 2D-массив в функцию, поэтому вам нужны источники получше.

JHBonarius 18.12.2020 17:29

@JHBonarius Спасибо за ваш ответ, можете ли вы упомянуть какой-нибудь полезный ресурс, из которого я могу его получить?

Karm Patel 18.12.2020 17:34
*( *(arr + i) + j) — это очень запутанный способ написания arr[i][j].
tadman 18.12.2020 17:38

И вы должны прочитать все ошибки и предупреждения, которые дает вам компилятор, в том числе "передача аргумента 1" pointer_string_array_indexing "из несовместимого типа указателя"

JHBonarius 18.12.2020 17:39

Давняя традиция C заключается в создании NULL терминированных массивов, а не в блокировке их определенного размера. Как я бы рекомендовал это как char **s = { "abc", "def", NULL }. Затем вы можете перебирать этот список, пока не получите указатель NULL. Это не чистый многомерный массив, но это своего рода то, с чем вы работаете с данными в его самом элементарном представлении. Вы по-прежнему можете получить к нему доступ как s[i][j], если хотите.

tadman 18.12.2020 17:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
205
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы понять, почему один способ работает, а другой нет, вы должны понять некоторые странные вещи, связанные с тем, как 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)

Другие вопросы по теме