Показаны сообщения с ярлыком переменные в Go. Показать все сообщения
Показаны сообщения с ярлыком переменные в Go. Показать все сообщения

пятница, 27 сентября 2024 г.

Где в памяти располагаются переменные в Golang

В Go переменной соответствует область памяти, которая может располагаться либо на стеке, либо в куче, в зависимости от типа данных этой переменной. Рассмотрим оба случая:

На стеке

Переменные размещаются на стеке (stack), когда они локальны для функции или метода. Например, если переменная объявлена внутри функции:

func main() {
    var myVar string // Переменная 'myVar' будет находиться на стеке
}

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

В куче (heap)

Переменная будет находиться в куче, если она является глобальной или создана с использованием ключевого слова new.

Глобальные переменные

package main

var myVar string // Переменная 'myVar' будет находиться в куче

func main() {}

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

Создание переменной через new

package main

import "fmt"

func main() {
    var myVar *string = new(string) // Переменная 'myVar' будет находиться в куче
    fmt.Println(*myVar)             // Результат будет nil
    
    *myVar = "Hello, World!"        // Заполнение значения по адресу переменной
    fmt.Println(*myVar)             // Вывод: Hello, World!
}

При использовании оператора new, создается новая область памяти в куче и возвращается ее адрес.

Таким образом, чтобы определить, где именно в памяти находится переменная, нужно проанализировать её использование в коде:

  • Если переменная локальна для функции, то она будет находиться на стеке.
  • Если она является глобальной, то будет находиться в куче.
  • Если использовалось ключевое слово new, то она также будет находиться в куче.

Читайте также:


пятница, 25 декабря 2020 г.

Go style guides: уменьшайте область видимости переменных

По возможности уменьшите область видимости переменных. Не уменьшайте область видимости, если это противоречит рекомендации "уменьшать вложенность".

Менее удачный пример:

err := ioutil.WriteFile(name, data, 0644)
if err != nil {
    return err
}

Более удачный пример:

if err := ioutil.WriteFile(name, data, 0644); err != nil {
    return err
}

Если вам нужен результат вызова функции вне if, вам не следует пытаться уменьшить область видимости.

Менее удачный пример:

if data, err := ioutil.ReadFile(name); err == nil {
    err = cfg.Decode(data)
    if err != nil {
        return err
    }

    fmt.Println(cfg)
    return nil
} else {
    return err
}

Более удачный пример:

data, err := ioutil.ReadFile(name)
if err != nil {
     return err
}

if err := cfg.Decode(data); err != nil {
    return err
}

fmt.Println(cfg)
return nil


Читайте также:


воскресенье, 17 мая 2020 г.

Затененные (shadowed) переменные в Golang

Почему переменная n не меняется?

func main() {
    n := 0
    if true {
        n := 1
        n++
    }
    fmt.Println(n) // 0
}

Ответ

Оператор

n := 1
объявляет новую переменную, которая затеняет оригинал n во всей области действия оператора if.

Чтобы повторно использовать n из внешнего блока, вместо этого напишите

n = 1

func main() {
    n := 0
    if true {
        n = 1
        n++
    }
    fmt.Println(n) // 2
}

Обнаружение затененных переменных

Чтобы помочь обнаружить затененные переменные, вы можете использовать экспериментальную функцию -shadow, предоставляемую инструментом vet. Он помечает переменные, которые могли быть непреднамеренно скрыты. Передача оригинальной версии кода vet выдает предупреждение.

$ go vet -shadow main.go
main.go:4: declaration of "n" shadows declaration at main.go:2

С версии Go 1.12 это больше не поддерживается. Вместо этого вы можете сделать

go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)

Кроме того, компилятор Go обнаруживает и запрещает некоторые случаи затенения.

func Foo() (n int, err error) {
    if true {
        err := fmt.Errorf("Invalid")
        return
    }
    return
}

../main.go:4:3: err is shadowed during return


Читайте также:


вторник, 4 июня 2019 г.

Спецификация Go: краткие объявления переменных

Краткое объявление переменной использует синтаксис:

ShortVarDecl = IdentifierList ":=" ExpressionList .

Это сокращение для объявления обычной переменной с выражениями инициализатора, но без типов:

"var" IdentifierList = ExpressionList .

i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe()  // os.Pipe() возвращает связанную пару Files и error (ошибку), если она есть
_, y, _ := coord(p)   // coord() возвращает три значения; заинтересованы только в y координате

В отличие от обычных объявлений переменных, краткое объявление переменных может переопределять переменные при условии, что они были первоначально объявлены ранее в том же блоке (или списках параметров, если блок является телом функции) с тем же типом и по крайней мере одна из непустых переменных является новой. Как следствие, объявление может появиться только в кратком объявлении с несколькими переменными. Переопределение не вводит новую переменную; оно просто присваивает новое значение оригиналу.

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset)  // переопределяет offset
a, a := 1, 2                              // недопустимо: двойное определение a или нет новой переменной если a была определена еще где-либо

Краткие объявления переменных могут появляться только внутри функций. В некоторых контекстах, таких как инициализаторы для операторов "if", "for", или "switch", они могут использоваться для объявления локальных временных переменных.


Читайте также:


понедельник, 3 июня 2019 г.

Спецификация Go: объявления переменных

Объявление переменной (variable declaration) создает одну или несколько переменных, связывает с ними соответствующие идентификаторы и присваивает каждому тип и начальное значение.

VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .

var i int
var U, V, W float64
var k = 0
var x, y float32 = -1, -2
var (
  i       int
  u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)

// поиск по карте (map); заитересованность только в "found"
var _, found = entries[name]  

Если указан список выражений, переменные инициализируются с выражениями в соответствии с правилами для назначений. В противном случае каждая переменная инициализируется своим нулевым значением.

Если тип присутствует, каждой переменной присваивается этот тип. В противном случае каждой переменной присваивается тип соответствующего значения инициализации в присваивании. Если это значение является не типизированной константой, оно сначала неявно преобразуется в тип по умолчанию; если это не типизированное логическое значение, оно сначала неявно преобразуется в тип bool. Предварительно объявленное значение nil нельзя использовать для инициализации переменной без явного типа.

var d = math.Sin(0.5)  // d это float64
var i = 42             // i это int
var t, ok = x.(T)      // t это T, ok это bool
var n = nil            // недопустимо

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


Читайте также:


вторник, 21 мая 2019 г.

Спецификация Go: переменные (variables)

Переменная - это место хранения для значения. Набор допустимых значений определяется типом переменной.

Объявление переменной или, для параметров и результатов функции, подпись объявления функции или литерала функции резервирует хранилище для именованной переменной. Вызов встроенной функции new или получение адреса составного литерала выделяет память для переменной во время выполнения. На такую анонимную переменную ссылаются через (возможно, неявный) указатель косвенности (pointer indirection).

Структурированные переменные типа array, slice и struct имеют элементы и поля, которые могут быть адресованы индивидуально. Каждый такой элемент действует как переменная.

Статический тип (или просто тип) переменной - это тип, указанный в ее объявлении, тип, указанный в new вызове или составном литерале, или тип элемента структурированной переменной. Переменные типа интерфейса также имеют отдельный динамический тип, который является конкретным типом значения, назначенного переменной во время выполнения (если только это значение не является предварительно объявленным идентификатором nil, который не имеет типа). Динамический тип может изменяться во время выполнения, но значения, хранящиеся в интерфейсных переменных, всегда можно назначить статическому типу переменной.

var x interface{}  // x это nil и имеет статический тип interface{}
var v *T           // v имеет значение nil, статический тип *T
x = 42             // x имеет значение 42 и динамический тип int
x = v              // x имеет значение (*T)(nil) и динамический тип *T

Значение переменной извлекается путем обращения к переменной в выражении; это самое последнее значение, присвоенное переменной. Если переменной еще не присвоено значение, ее значением является нулевое значение для ее типа.


Читайте также:


воскресенье, 17 марта 2019 г.

Go Code Review Comments: Имена переменных

Имена переменных в Go должны быть короткими, а не длинными. Это особенно верно для локальных переменных с ограниченной областью видимости. Предпочитайте с вместо lineCount. Предпочитайте i вместо sliceIndex.

Основное правило: чем дальше от декларации используется имя, тем более информативным должно быть имя. Для получателя метода достаточно одной или двух букв. Общие переменные, такие как индексы цикла и читатели (readers), могут состоять из одной буквы (i, r). Более необычные вещи и глобальные переменные требуют более описательных имен.


Читайте также: