Skip to content

Go 변수

변수는 값을 저장하는 저장 위치로, 런타임에 저장된 값을 동적으로 변경할 수 있습니다. 변수를 선언할 때마다 해당 타입의 값을 저장할 메모리가 할당됩니다. 더 많은 세부사항은 참조 매뉴얼 - 변수 에서 확인하실 수 있습니다.

선언

Go 에서 타입 선언은 후치되며, 변수 선언에는 var 키워드가 사용되며 형식은 var 변수명 타입명입니다. 변수명 명명 규칙은 식별자 명명 규칙을 따라야 합니다.

go
var intNum int
var str string
var char byte

여러 개의 동일한 타입 변수를 선언할 때는 타입을 한 번만 작성할 수 있습니다.

go
var numA, numB, numC int

여러 개의 서로 다른 타입 변수를 선언할 때는 ()로 묶을 수 있으며, 여러 개의 ()를 사용할 수 있습니다.

go
var (
  name    string
  age     int
  address string
)

var (
  school string
  class int
)

변수를 선언하기만 하고 값을 할당하지 않으면, 변수에 저장된 값은 해당 타입의 영값입니다.

할당

할당에는 연산자 = 가 사용됩니다. 예를 들어

go
var name string
name = "jack"

선언할 때 직접 할당할 수도 있습니다.

go
var name string = "jack"

또는 이렇게 할 수도 있습니다.

go
var name string
var age int
name, age = "jack", 1

두 번째 방식은 매번 타입을 지정해야 하므로, 공식에서 제공하는 문법 설탕인 단변수 초기화를 사용할 수 있습니다. var 키워드와 후치 타입을 생략할 수 있으며, 어떤 타입인지는 컴파일러가 추론합니다.

go
name := "jack" // 문자열 타입 변수.

타입을 지정하지 않아도 되지만, 후속 할당 시 타입은 반드시 일치해야 합니다. 아래 코드는 컴파일을 통과할 수 없습니다.

a := 1
a = "1"

또한 단변수 초기화에는 nil 을 사용할 수 없습니다. nil 은 어떤 타입에도 속하지 않으므로 컴파일러가 타입을 추론할 수 없습니다.

go
name := nil // 컴파일 통과 불가

단변수 선언은 일괄 초기화할 수 있습니다.

go
name, age := "jack", 1

단변수 선언 방식은 이미 존재하는 변수에는 사용할 수 없습니다. 예를 들어

go
// 오류 예시
var a int
a := 1

// 오류 예시
a := 1
a := 2

하지만 한 가지 예외 상황이 있습니다. 기존 변수에 할당하면서 동시에 새 변수를 선언하는 경우입니다. 예를 들어

go
a := 1
a, b := 2, 2

이러한 코드는 컴파일을 통과할 수 있습니다. 변수 a 는 재할당되고 b 는 새로 선언됩니다.

Go 언어에는 하나의 규칙이 있습니다. 함수 내의 모든 변수는 반드시 사용되어야 합니다. 아래 코드는 변수를 선언하기만 하고 사용하지 않았습니다.

go
func main() {
  a := 1
}

컴파일 시 오류가 발생하며, 변수가 선언되었지만 사용되지 않았다고 알려줍니다.

a declared and not used

이 규칙은 함수 내 변수에만 적용되며, 함수 외부의 패키지 레벨 변수에는 제한이 없습니다. 아래 코드는 컴파일을 통과할 수 있습니다.

go
var a = 1

func main() {

}

익명

밑줄을 사용하여 특정 변수가 필요하지 않음을 나타낼 수 있습니다.

go
Open(name string) (*File, error)

예를 들어 os.Open 함수는 두 개의 반환 값을 가지며, 첫 번째만 원하고 두 번째는 원하지 않는 경우 다음과 같이 작성할 수 있습니다.

go
file, _ := os.Open("readme.txt")

사용되지 않는 변수는 컴파일을 통과할 수 없으므로, 특정 변수가 필요하지 않을 때는 밑줄 _ 를 사용할 수 있습니다.

교환

Go 에서 두 변수의 값을 교환하려면 포인터를 사용할 필요 없이 할당 연산자를 사용하여 직접 교환할 수 있습니다. 문법적으로 매우 직관적입니다. 예시는 다음과 같습니다.

go
num1, num2 := 25, 36
num1, num2 = num2, num1

세 변수도 마찬가지입니다.

go
num1, num2, num3 := 25, 36, 49
num1, num2, num3  = num3, num2, num1

아래 코드를 생각해보세요. 이는 피보나치 수열을 계산하는 코드의 일부입니다. 계산 후 세 변수의 값은 각각 무엇일까요?

go
a, b, c := 0, 1, 1
a, b, c = b, c, a+b

정답은

1 1 1

입니다. 다음과 같이 의아해할 수 있습니다.

1 1 2

a 가 이미 b 의 값으로 할당되었는데 왜 a+b 의 결과가 여전히 1 일까요? Go 에서 여러 변수를 할당할 때는 왼쪽에서 오른쪽으로 계산하는 것이 아니라, 값을 먼저 계산한 후 할당합니다.

go
a, b, c = b, c, a+b

다음과 같이展開될 것이라고 생각할 수 있습니다.

go
a = b
b = c
c = a + b

하지만 실제로는 a, b, c 세 변수의 값을 각각 계산한 후 할당합니다. 다음과 동일합니다.

go
a, b, c = 1, 1, 0+1

함수 호출이 포함되면 이 효과가 더 명확해집니다. 두 숫자의 합을 계산하는 sum 함수가 있습니다.

go
func sum(a, b int) int {
  return a + b
}

함수를 통해 두 수를 더합니다.

go
a, b, c := 0, 1, 1
a, b, c = b, c, sum(a, b)

결과는 변함없으며, sum 함수 반환 값을 계산할 때 매개변수는 여전히 0 과 1 입니다.

1 1 1

따라서 코드는 다음과 같이 분리해서 작성해야 합니다.

go
a, b = b, c
c = a + b

비교

변수 간 비교에는 전제 조건이 있습니다. 바로 타입이 반드시 동일해야 한다는 것입니다. Go 언어에는 암시적 타입 변환이 존재하지 않습니다. 아래 코드는 컴파일을 통과할 수 없습니다.

go
func main() {
  var a uint64
  var b int64
  fmt.Println(a == b)
}

컴파일러는 두 타입이 동일하지 않다고 알려줍니다.

invalid operation: a == b (mismatched types uint64 and int64)

따라서 명시적 타입 변환을 사용해야 합니다.

go
func main() {
  var a uint64
  var b int64
  fmt.Println(int64(a) == b)
}

제네릭이 있기 전에는 Go 에서 제공하는 내장 min, max 함수는 부동소수점만 지원했습니다. 1.21 버전에서야 공식은 이 두 내장 함수를 제네릭으로 재작성했습니다. 이제 min 함수를 사용하여 최솟값을 비교할 수 있습니다.

go
minVal := min(1, 2, -1, 1.2)

max 함수를 사용하여 최댓값을 비교합니다.

go
maxVal := max(100, 22, -1, 1.12)

이들의 매개변수는 모든 비교 가능 타입을 지원합니다. Go 에서 비교 가능 타입은 다음과 같습니다.

  • 불리언
  • 숫자
  • 문자열
  • 포인터
  • 채널 (동등성 판단만 지원)
  • 요소가 비교 가능 타입인 배열 (슬라이스는 비교 불가) (동등성 판단만 지원) (동일한 길이의 배열 간 비교만 지원. 배열 길이도 타입의 일부이므로 서로 다른 타입은 비교 불가)
  • 필드 타입이 모두 비교 가능 타입인 구조체 (동등성 판단만 지원)

이 외에도 표준 라이브러리 cmp 를导入하여 판단할 수 있지만, 순서 있는 타입의 매개변수만 지원합니다. Go 에서 내장 순서 있는 타입은 숫자와 문자열뿐입니다.

go
import "cmp"

func main() {
  cmp.Compare(1, 2)
  cmp.Less(1, 2)
}

코드 블록

함수 내부에서는 중괄호를 사용하여 코드 블록을 만들 수 있으며, 코드 블록 간의 변수 스코프는 서로 독립적입니다. 예를 들어 아래 코드와 같습니다.

go
func main() {
  a := 1

  {
    a := 2
    fmt.Println(a)
  }

  {
    a := 3
    fmt.Println(a)
  }
  fmt.Println(a)
}

출력은 다음과 같습니다.

2
3
1

블록 간 변수는 서로 독립적이며 간섭을 받지 않지만 접근할 수는 없으며, 부모 블록의 영향을 받습니다.

go
func main() {
  a := 1

  {
    a := 2
    fmt.Println(a)
  }

  {
    fmt.Println(a)
  }
  fmt.Println(a)
}

출력은 다음과 같습니다.

2
1
1

Golang by www.golangdev.cn edit