← Назад к вопросам

Как по дефолту ведет себя аргумент при передаче в функцию?

2.2 Middle🔥 131 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Аргументы в Go: передача по значению

В языке Go все аргументы передаются в функции по значению (by value). Это фундаментальное правило, определяющее поведение параметров при вызове функций.

Механизм передачи по значению

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

Пример с базовыми типами

Рассмотрим простой пример с целым числом:

package main

import "fmt"

func modifyValue(x int) {
    x = 42 // Изменяем локальную копию
    fmt.Println("Inside function:", x)
}

func main() {
    original := 10
    fmt.Println("Before call:", original)
    modifyValue(original)
    fmt.Println("After call:", original) // Значение не изменилось
}

Вывод:

Before call: 10
Inside function: 42
After call: 10

Очевидно, что переменная original в main() осталась неизменной, поскольку функция modifyValue() работала лишь с своей локальной копией.

Особенности работы с указателями

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

package main

import "fmt"

func modifyViaPointer(p *int) {
    *p = 42 // Модифицируем данные по адресу
    fmt.Println("Inside function:", *p)
}

func main() {
    original := 10
    fmt.Println("Before call:", original)
    modifyViaPointer(&original)
    fmt.Println("After call:", original) // Значение изменилось!
}

Вывод:

Before call: 10
Inside function: 42
After call: 42

Здесь мы передаем не значение 10, а адрес переменной original. Функция получает копию этого адреса, но использует его для доступа к оригинальной памяти.

Сложные типы: структуры и массивы

Для структур (structs) и массивов (arrays) правило передачи по значению также применяется, что может быть неэффективно для крупных объектов:

package main

import "fmt"

type LargeStruct struct {
    data [1000]int
}

func processStruct(s LargeStruct) LargeStruct {
    s.data[0] = 999 // Работает с копией
    return s
}

func main() {
    original := LargeStruct{}
    fmt.Println("Original before:", original.data[0])
    
    result := processStruct(original)
    fmt.Println("Original after:", original.data[0]) // Не изменился
    fmt.Println("Result:", result.data[0])           // Изменился
}

Для оптимизации часто используют передачу указателей на структуры (*LargeStruct) или работают со структурами через слайсы (slices) и карты (maps).

Важное исключение: слайсы и карты

Слайсы (slices) и карты (maps) передаются по значению, но их внутреннее поведение отличается. Они являются ссылочными типами (reference types) под капотом:

  • Слайс — это небольшое структурное значение (указатель на массив, длина, емкость)
  • Карта — аналогично содержит указатель на внутреннюю структуру данных

При передаче копируется эта структура, но не сами данные. Поэтому изменения элементов слайса или карты внутри функции видны вне её:

package main

import "fmt"

func modifySlice(s []int) {
    if len(s) > 0 {
        s[0] = 99 // Изменяет элемент исходного массива
    }
}

func main() {
    original := []int{1, 2, 3}
    fmt.Println("Before:", original)
    modifySlice(original)
    fmt.Println("After:", original) // Изменился!
}

Вывод:

Before: [1 2 3]
After: [99 2 3]

Однако изменение самого слайса (добавление/удаление элементов без изменения емкости) может потребовать работы через указатель *[]int.

Ключевые выводы

  1. Базовый принцип: Все аргументы в Go передаются по значению — всегда создается копия.
  2. Для указателей: Копируется адрес, что позволяет изменять оригинальные данные.
  3. Для ссылочных типов (слайсы, карты): Копируется небольшая структура с ссылкой на данные, поэтому изменения элементов видны вне функции.
  4. Эффективность: Передача крупных структур по значению может быть затратной — в таких случаях используют указатели.

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

Как по дефолту ведет себя аргумент при передаче в функцию? | PrepBro