ПрограммированиеC разработчик

Расскажите подробно о механизме передачи массивов в функции в языке C. Каковы риски, связанные с этой моделью, и как правильно организовать безопасную работу с массивами внутри функций?

Проходите собеседования с ИИ помощником Hintsage

Ответ

В языке C массивы не передаются в функции по значению. Если передать массив как аргумент, в функцию реально поступает указатель на первый элемент массива. Это приводит к тому, что функция модифицирует исходный массив, а не его копию.

Например:

void fillArray(int arr[], int n) { for (int i = 0; i < n; ++i) arr[i] = i*i; } int main() { int myarr[5]; fillArray(myarr, 5); // ok }

Внутри функции:

  • невозможно узнать размер исходного массива через sizeof(arr) — это вернет размер указателя, а не массива.
  • Работать можно только с тем кусочком памяти, чей размер мы передали явно.

Работать правильно и безопасно с массивами можно так:

  • всегда явно передавайте размер массива отдельным аргументом;
  • используйте константы или макросы-обертки для размеров;
  • если нужно защитить массив от изменений, передавайте const int *arr.

Вопрос с подвохом

Вопрос: Возможно ли узнать размер исходного переданного массива внутри функции по аргументу int arr[] через sizeof(arr)?

Ответ: Нет, нельзя! sizeof(arr) внутри функции вернет размер указателя на тип (например, 4 или 8 байт), а не размер массива.

Пример:

void f(int arr[]) { printf("%zu ", sizeof(arr)); // размер указателя, не массива! } int main() { int x[10]; f(x); // Обычно выведет 8 (x86_64) или 4 (x86) }

Примеры реальных ошибок


История

В промышленном проекте функции копирования массивов пытались вычислять длину массива на лету, используя sizeof(arr)/sizeof(int) внутри функции. На бою это приводило к копированию только части массива, потому что размер всегда равнялся 1 (8/8), а данные затирались непредсказуемым образом.


История

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


История

При написании библиотеки для работы с изображениями программист не снабдил функцию раскрашивания массива изображений обязательным параметром размера. Это привело к переполнению буфера и краху программы на определённых входах, что обнаружилось только в интеграционном тесте на большом изображении.