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

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

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

Ответ.

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

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

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

Пример корректной передачи массива:

void print_array(const int* arr, size_t size) { for (size_t i = 0; i < size; ++i) printf("%d ", arr[i]); } int main() { int nums[] = {1,2,3,4,5}; print_array(nums, sizeof(nums)/sizeof(nums[0])); return 0; }

Ключевые особенности:

  • В функцию передаётся адрес массива, а не копия элементов.
  • Размер массива необходимо передавать явно.
  • Изменения элементов внутри функции изменяют внешний массив.

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

Может ли функция узнать длину переданного массива, если он был объявлен как int arr[10]?

Ответ: Нет, внутри функции выражение sizeof(arr) вернёт размер указателя, а не всего массива. Размер нужно передавать отдельно.

Передача массива как int arr[] и как int arr в функции — это одно и то же?*

Ответ: Да, в сигнатуре функции это эквивалентно — в обоих случаях передаётся указатель на int. Различия есть только в синтаксисе.

Изменяя элементы массива внутри функции, изменится ли исходный массив?

Ответ: Да, поскольку передан указатель, функция изменяет память, на которую он указывает.

Типовые ошибки и анти-паттерны

  • Не передавать размер массива как аргумент (выход за границы).
  • Использовать sizeof(arr) для определения размера массива в функции (неправильный результат).
  • Путать int arr[10] с int* arr в разных контекстах.

Пример из жизни

Негативный кейс

Проект реализовал функцию инициализации массивов, определяя размер внутри функции через sizeof(arr) / sizeof(arr[0]). На этапе тестирования функция работала, но при обработке других массивов — затирала чужую память или работала некорректно.

Плюсы:

  • Упрощалась сигнатура функции.

Минусы:

  • Программа падала или работала некорректно при других данных.

Позитивный кейс

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

Плюсы:

  • Гарантия отсутствия выхода за границы.
  • Более надёжный, безопасный и переносимый код.

Минусы:

  • Необходимость явно передавать размер, чуть длиннее вызов.