Как передаются массивы разных размеров в функцию?

Вопрос довольно тривиальный, и есть множество ответов на данный вопрос. И поверьте я прочитал очень много по этому вопросу. Я могу скопировать решение для моего конкретного случая, но моя голова взрывается, потому что я не могу систематизировать знания и из-за этого не могу двигаться вперёд.

Меня не совсем интересует поиск "совета как сделать", мне нужно чтобы подтвердили/опровергли/дополнили мою картину мира по вопросу "передача массива в функцию".

И так что я знаю на данный момент:

// ОДНОМЕРНЫЙ МАССИВ

по значению с определенным размером

Функция: void func(int massive[10]){} Вызов в программе: func(massive);

по значению с неопределенным размером

Функция: void func(int massive[], int massiveSize){}

Вызов в программе: func(myMassive, 10);

по ссылке c определенным размером

Функция: void func(int (&massive)[10]){}

Вызов в программе: func(myMassive);

по ссылке c определенным размером

Функция: void func(int (&massive)[], int massiveSize){}

Вызов в программе: func(myMassive, 10);

по указателю с неопределенным размером (при этом если внутри функции будем мерить sizeof() указателя, то получим размер 4 байта - одного int

Функция: void func(int *massive, massiveSize){};

Вызов в программе: func(&myMassive,10);

// ДВУМЕРНЫЙ МАССИВ

по значению с определенным размером

Функция: void func(int massive[5][10]){}

Вызов в программе: func(myMassive);

по значению с неопределенным размером - но мы должны указать размерность второго измерения, то есть кол-во столбцов

Функция: void func( int massive[][10],int sizeRows){}

Вызов в программе: func(myMassive, 5);

по ссылке с неопределенным размером

Функция: void func(int& (*massive)[],int sizeRows, sizeCollums){}

Вызов в программе:

rows = sizeof(massive)/sizeof(*massive);
collums = sizeof(massive[0])/sizeof(massive[0][0]);
func (massive, rows,collums);

по ссылке с определенным размером

Функция: void func(int& (*massive[5])[10]){}

Вызов в программе: func(myMassive);

по указателю на первый элемент

Функция: void func(int* (&massive), sizeRows, sizeCollums){};

Вызов в программе:

rows = sizeof(massive)/sizeof(*massive);
collums = sizeof(massive[0])/sizeof(massive[0][0]);
func( (int*)massive, rows,collums);

// ТРЁХМЕРНЫЙ МАССИВ

по указателю на первый элемент, больше рабочего кода для себя я не нашёл

Функция: void func(int* (&massive), sizeDimension1, sizeDimension2,sizeDimension3){};

Вызов в программе:

dimension1 = sizeof(massive)/sizeof(*massive);
dimension2 = sizeof(massive[0])/sizeof(massive[0][0]);
dimension3 = sizeof(massive[0][0])/sizeof(massive[0][0][0]);
func( (int*)massive, dimension1,dimension2,dimension3);

Бонусный вопрос, как панчлайн к шутке:

А есть ли смысл, что я вообще так сильно запариваюсь с массивами?

Потому что я пока не приступил к изучению и освоению всех контейнеров из STL, насколько я понимаю, то в данном случае просто проще начать пользоваться контейнером array.


Ответы (1 шт):

Автор решения: user7860670

Одномерные массивы

  • Получение размера массива: для этого есть специальная функция в стандартной библиотеке. Типичная ошибка использовать для этих целей sizeof.
#include <iterator>
#include <cstddef>
using item_t = int;
using items_t = item_t [10];
int main()
{
    items_t items{};
    ::std::size_t items_count{::std::size(items)}; // 10
}
  • Передача указателя на первый элемент массива и размера массива: работает с массивами конкретного размера и неопределенного размера. Указатель потенциально может быть нулевым. Ответственность за указание правильного размера лежит на вызывающей стороне. Создание временного объекта с типом указателя на первый элемент массива из объекта с типом массива языком допускается даже неявное.
#include <iterator>
#include <cstddef>
using item_t = int;
using items_t = item_t [10];
void func(item_t * p_items, ::std::size_t items_count)
{}
int main()
{
    items_t items{};
    // то же самое func(static_cast<item_t *>(items), ::std::size(items));
    // то же самое func(::std::addressof(items[0]), ::std::size(items));
    func(items, ::std::size(items));
}
  • Передача массива конкретного размера по значению: языком не поддерживается. При указании массива в качестве типа аргумента тип будет заменен компилятором на указатель на элемент массива. По сути получается как при передаче указателя на первый элемент, но без указания размера.
#include <type_traits>
using item_t = int;
using items_t = item_t [10];
void func(items_t items)
{
    static_assert(::std::is_same_v<item_t *, decltype(items)>);
}

Так как передается указатель, то узнать размер массива внутри функции нельзя, а при передаче можно передвать нулевой указатель и размер не проверяется:

#include <iterator>
#include <cstddef>
using item_t = int;
using items_t = item_t [10];
void func(items_t items)
{
    ::std::size_t items_count{::std::size(items)}; // error
}
int main()
{
    func(nullptr); // компилятору все равно
    item_t small[5]{};
    func(small); // компилятору все равно
}
  • Передача массива неопределенного размера по значению: языком не поддерживается. При указании массива в качестве типа аргумента тип будет заменен компилятором на указатель на элемент массива. По сути получается как при передаче указателя на первый элемент, но без указания размера.
#include <type_traits>
using item_t = int;
using items_t = item_t [];
void func(items_t items)
{
    static_assert(::std::is_same_v<item_t *, decltype(items)>);
}

Узнать размер массива внутри функции нельзя, а при передаче можно передавать нулевой указатель:

#include <iterator>
#include <cstddef>
using item_t = int;
using items_t = item_t [];
void func(items_t items)
{
    ::std::size_t items_count{::std::size(items)}; // error
}
int main()
{
    func(nullptr); // компилятору все равно
}
  • Передача массива конкретного размера по ссылке: работает без проблем. Внутри функции можно получить размер массива, а при вызове компилятор отловит попытки передать массив другого размера или нулевой указатель.
#include <iterator>
#include <cstddef>
using item_t = int;
using items_t = item_t [10];
void func(items_t & items)
{
    ::std::size_t items_count{::std::size(items)}; // 10
}
int main()
{
    items_t items{};
    func(items); // OK
    func(nullptr); // error
    item_t small[5]{};
    func(small); // error
}
  • Передача массива неопределенного размера по ссылке: возможно, но бесполезно. Внутри функции получить размер массива нельзя, а при вызове компилятор отловит попытки передать нулевой указатель.
#include <iterator>
#include <cstddef>
using item_t = int;
using items_t = item_t [];
void func(items_t & items)
{
    ::std::size_t items_count{::std::size(items)}; // error
}
int main()
{
    item_t items[10];
    func(items); // OK
    func(nullptr); // error
    item_t small[5]{};
    func(small); // OK
}
  • Передача массива конкретного размера по указателю: работает без проблем. Внутри функции можно получить размер массива, а при вызове компилятор отловит попытки передать массив другого размера.
#include <iterator>
#include <memory>
#include <cstddef>
using item_t = int;
using items_t = item_t [10];
void func(items_t * p_items)
{
    ::std::size_t items_count{::std::size(*p_items)}; // 10
}
int main()
{
    items_t items{};
    func(::std::addressof(items)); // OK
    func(nullptr); // OK
    item_t small[5]{};
    func(::std::addressof(small)); // error
}
  • Передача массива неопределенного размера по указателю: возможно, но еще более бесполезно, чем по ссылке. Внутри функции получить размер массива нельзя. При вызове ничего не проверяется.
#include <iterator>
#include <memory>
#include <cstddef>
using item_t = int;
using items_t = item_t [];
void func(items_t * p_items)
{
    ::std::size_t items_count{::std::size(*p_items)}; // error
}
int main()
{
    item_t items[10];
    func(::std::addressof(items)); // OK
    func(nullptr); // OK
    item_t small[5]{};
    func(::std::addressof(small)); // OK
}

Двумерные массивы

  • Являются одномерными массивами, элементами которых являются другие массивы. Так что к ним приминимо все из предыдущего раздела.
using item_t = int [10];
using items_t = item_t [10];

А еще в языке есть

  • Обертка для массивов ::std::array, позволяющая передавать массивы по значению, которые следует всегда использовать вместо c-style массивов.
  • Проекции массивов ::std::span или array_view, хранящие указатель на первый элемент и размер.
→ Ссылка