В практической функции параметр объявлен как указатель, а аргументом служит встроенный массив: почему размер массива нельзя вывести из параметра?
При передаче встроенного массива в параметр-указатель массив обычно преобразуется в указатель на первый элемент. Указатель хранит адрес, но не длину массива, поэтому размер нельзя корректно получить из самого параметра.
Встроенные массивы в C++ унаследовали модель C: массивы имеют фиксированный размер, но не являются полноценными объектами, которые можно целиком присваивать или передавать по значению. Для совместимости и эффективности в большинстве выражений массив автоматически преобразуется в указатель на первый элемент.
Это решение уменьшало стоимость передачи массивов, но отделило данные о размере от самого параметра. Поэтому функции, работающие с массивами, обычно получают длину отдельно либо используют другой тип-обёртку.
Если функция принимает только указатель, она не знает, сколько элементов находится по этому адресу. Попытка вычислить размер через sizeof внутри такой функции даст размер указателя, а не исходного массива.
Из-за этого возможны чтение за границами массива, неопределённое поведение и ошибки при обработке пустых или слишком коротких буферов. Особенно опасно передавать указатель и рассчитывать, что функция сможет восстановить исходную длину по адресу.
При вызове функции с параметром-указателем встроенный массив преобразуется в указатель на свой первый элемент. Информация о количестве элементов при этом не передаётся:
Исключения из автоматического преобразования важны: оно не выполняется, когда массив передают по ссылке, применяют sizeof, decltype или оператор взятия адреса. Поэтому функцию можно объявить с параметром-ссылкой на массив фиксированной длины или использовать шаблон, выводящий размер массива.
Другой практичный вариант — передавать указатель вместе с длиной. Для безопасного интерфейса в современном C++ часто применяют std::span: он представляет указатель и количество элементов как единое невладеющее представление. Для владения данными подходят контейнеры вроде std::array или std::vector, поскольку они явно выражают размер и правила управления памятью.
Передача массива по ссылке сохраняет его размер, но делает функцию менее универсальной для массивов разных длин без шаблона. Пара «указатель плюс длина» совместима с низкоуровневыми API, однако требует не допускать рассинхронизации двух значений.
Низкоуровневая функция обработки пакета получила адрес первого байта, но отдельная длина не передавалась. Разработчик попытался вычислить длину через sizeof и получил размер указателя; на одной архитектуре это приводило к чтению только части данных, а при другом использовании — к выходу за границы.
Рассматривались три варианта. Передача фиксированного размера через шаблон хорошо защищает массивы известной длины, но не подходит для произвольных буферов. Передача указателя и длины совместима с существующим C-интерфейсом, но требует дисциплины при каждом вызове. std::span<const std::byte> лучше выражает контракт «буфер без владения», однако требует поддержки современного стандарта C++ и изменения интерфейса.
Для нового внутреннего API выбрали std::span: функция получила адрес и размер в одном объекте, а вызывающий код перестал вручную синхронизировать два параметра. Для внешнего C-ABI оставили пару «указатель плюс длина» и явно документировали, что длина обязательна.
Нет. Если параметр — ссылка на массив, преобразования не происходит, и размер остаётся частью типа. Это позволяет проверять длину на этапе компиляции или выводить её в шаблоне. Также преобразования нет в выражениях sizeof, decltype и при взятии адреса массива.
Параметр, записанный как массив, в объявлении функции корректируется компилятором до параметра-указателя. Поэтому такая запись обычно лишь делает интерфейс визуально похожим на работу с массивом, но не передаёт его размер и не предотвращает преобразование аргумента.
В общем случае нельзя. Указатель содержит адрес, но стандарт C++ не требует хранить рядом с ним длину или метаданные о выделенном объекте. Размер можно знать только из внешнего контракта, передать отдельным параметром либо сохранить в типе представления, например std::span.