Я создаю собственный тип для таблицы (двумерный массив):
typedef int table_t[4][2]; // 4 rows, 2 columns
Далее я объявляю таблицу и указатель на таблицу:
table_t a = { {10, 0}, // my table
{20, 0},
{30, 0},
{40, 0} };
table_t *b = &a; // pointer to my table
Далее я пытаюсь напечатать первый столбец таблицы:
printf("%d\n", a[0][0]); // var1.
printf("%d\n", a[1][0]); // It's work
printf("%d\n", a[2][0]);
printf("%d\n", a[3][0]);
Оно работает. Как я могу получить доступ к элементам таблицы по указателю на table_t?
Я пытаюсь напечатать первый столбец таблицы по указателю:
printf("%d\n", *b[0][0]); // var2.
printf("%d\n", *b[1][0]); // It's print true value only from *b[0][0] and
printf("%d\n", *b[2][0]); // some strange from other elements
printf("%d\n", *b[3][0]);
Может быть, я делаю что-то не так? Внезапно у меня работает следующий код, но почему таблица повернулась? :
printf("%d\n", *b[0][0]); // var3
printf("%d\n", *b[0][1]); // Suddenly, it's work too.
printf("%d\n", *b[0][2]);
printf("%d\n", *b[0][3]);
Демо-версия Godbolt: https://godbolt.org/z/7bb63vGxT
Оператор []
группируется более плотно, чем унарный оператор *
. Итак, *b[0][1]
эквивалентно *(b[0][1])
, а это не то, что вам нужно. Вместо этого вы хотите сначала разыменовать b
, как в (*b)[0][1]
.
b
имеет указатель типа на table_t
, следовательно, вам следует обращаться к таблице через *b
, а поскольку постфиксные операторы имеют более высокий приоритет, чем префиксные операторы, вы должны заключить *b
в круглые скобки перед разыменованием элементов таблицы:
void print_column_zero(table_t *b) {
printf("%d\n", (*b)[0][0]);
printf("%d\n", (*b)[1][0]);
printf("%d\n", (*b)[2][0]);
printf("%d\n", (*b)[3][0]);
}
[...]
// passing a pointer to the table
print_column_zero(&a);
Обратите внимание, что вместо указателя на table_t
вы можете написать:
void print_column_zero(table_t b) {
printf("%d\n", b[0][0]);
printf("%d\n", b[1][0]);
printf("%d\n", b[2][0]);
printf("%d\n", b[3][0]);
}
[...]
// passing a table, which decays to a pointer to its first element
print_column_zero(a);
Поскольку table_t
является определением типа для массива, это будет эквивалентно: void print_column_zero(int (*b)[2])...
и print_column_zero(&a[0])
, т.е. будет передан указатель на элементы таблицы, как и для void print_column_zero(table_t *b)
, но с типом, который не требует разыменования. Сгенерированный код должен быть очень похож, если не идентичен.
Указатели на массивы несколько сбивают с толку и редко используются на практике. Вместо этого вы можете захотеть инкапсулировать свою таблицу в структуру:
typedef struct table_t {
int table[4][2]; // 4 rows, 2 columns
} table_t;
table_t a = {{ { 10, 0 }, // my table
{ 20, 0 },
{ 30, 0 },
{ 40, 0 } }};
void print_column_zero(table_t *b) {
printf("%d\n", b->table[0][0]);
printf("%d\n", b->table[1][0]);
printf("%d\n", b->table[2][0]);
printf("%d\n", b->table[3][0]);
}
[...]
printf("%d\n", a.table[0][0]);
printf("%d\n", a.table[1][0]);
printf("%d\n", a.table[2][0]);
printf("%d\n", a.table[3][0]);
print_column_zero(&a); // same output
table_t *b = &a; // pointer to my table
print_column_zero(b); // same output again
Этот подход не добавляет никаких накладных расходов по сравнению с предыдущими, однако инкапсуляция struct
позволяет добавлять связанные данные, которые могут оказаться полезными, и позволяет копировать таблицу с помощью одной операции присваивания.
Спасибо, Вы правы. Я ошибся с приоритетами операций. И спасибо за совет — код со структурой понятнее.
Скрывать массивы (или указатели) за typedef — не лучшая идея, поскольку это усложняет чтение и поддержку программы. А баги писать проще, как вы заметили.
Проблема в том, что []
имеет более высокий приоритет оператора, чем *
разыменование.
b
— int (*)[4][2]
.*b[i][j]
первый [i]
разыменовывает указатель.[j]
дает нам номер массива j
с типом int [2]
.*
дает первый элемент в этом массиве, поскольку «распад массива» означает, что мы получаем здесь указатель на первый элемент.Так, например, *b[0][1]
даст второй массив типа int [2]
и *
первый элемент, 20
. Не так, как ожидалось.
И очевидным решением было бы написать (*b)[0][0]
.
Лучшим решением было бы избавиться от typedef и объявить указатель как int (*b)[2] = a;
. Это избавит вас от лишней косвенности, поскольку позволит вам вводить b[i][j]
.
(В C23 мы могли бы также несколько загадочно написать typeof (*a)* b = a;
, но это сложно читать и понимать, поэтому я не рекомендую это делать.)
Или просто auto b = a;
@tstanisl typeof (*a)* b = a;
типобезопасен, auto b = a;
нет и примерно так же плох, как писать void* b = a;
. Что, если бы я хотел сделать &a
или *a
? Первый пример выдает ошибку компилятора, затем версии auto и void указателя просто весело продолжают работать, и теперь у нас есть скрытая ошибка. Изменение значения auto
было одной из самых глупых ошибок C++, разработанных людьми, которые никогда не работали с высокодобросовестным программным обеспечением.
Что такого небезопасного в использовании auto b=a
?
@tstanisl В какой-то момент я хотел написать вопросы и ответы по этому поводу. Возможно, сейчас самое время :) Я разместил вопрос на Codidact Почему новое ключевое слово auto из C++11 или C23 опасно? и я туда же напишу ответ.
Спасибо за ссылку. Это очень интересно, хотя с некоторыми аргументами я не согласен. Должен признаться, он был очень удивлен разницей const auto
и auto const
. Я пытался протестировать его в CLANG (см. ссылку ), но похоже, что в обоих случаях тип char * const
. Это ожидаемое поведение или потенциальная ошибка в CLANG?
@tstanisl Хм, на самом деле эту часть, вероятно, можно было бы сделать с корректурой. В любом случае мы ожидаем получить const char*
, но не получаем его. Посмотрим, смогу ли я придумать лучший пример.
@tstanisl Я отредактировал сообщение Codidact, чтобы сделать его менее конфронтационным, поскольку я мог перепутать его с каким-либо расширением компилятора или другим. Вернусь, когда у меня будет больше времени. В любом случае, проблема, заключающаяся в том, что мы не можем смешать auto
с константной корректностью, также является достаточно серьезным недостатком, чтобы мотивировать удаление auto
из языка.
*b[x][y]
==>(*b)[x][y]