ПрограммированиеСистемный программист

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

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

Ответ.

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

Передача по значению

При передаче скаляра (например, int) функция получает свою копию:

void foo(int a) { a = 10; } int main() { int x = 5; foo(x); // x == 5, не изменится! }

Передача по указателю

Для изменения значения переменной используют указатель:

void foo(int* a) { *a = 10; } int main() { int x = 5; foo(&x); // x == 10, значение изменилось! }

Когда использовать указатель

  • Нужно вернуть несколько значений.
  • Изменить большие структуры (экономия времени и памяти, нет копирования).
  • Для работы с массивами (их всегда передают как указатель).

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

Является ли массив параметром функции, передаваемым по ссылке?

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

Правильный ответ:

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

void foo(int arr[]) { arr[0] = 100; } int main() { int a[3] = {1,2,3}; foo(a); // a[0] будет 100! }

История


В одном проекте функция обновляла значения массива, объявленного как int arr[10], но в вызывающем коде массив был меньшего размера. Из-за того, что функция не знала фактический размер, произошло переполнение буфера и повреждение памяти.


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


Разработчик ожидал, что передача скаляра по "ссылке" (через указатель) гарантирует неизменность исходного значения, но по ошибке изменил по указателю память вне области видимости (ошибка в арифметике указателей), что привело к непредсказуемым результатам.