변수(Variable)
프로그래밍에서 변수는 값을 저장하는 메모리 공간을 의미한다. 메모리에 있는 데이터를 조작하는 것은 프로그래밍의 핵심으로 변수를 이용하여 쉽고 효과적으로 메모리에 있는 데이터를 조작할 수 있다.
package main
import "fmt"
func main() {
var a int // a 변수 선언
var msg string // msg 변수 선언
a = 20 // a에 값 20을 대입
msg = "Good Morning" // msg에 문자열 대입
fmt.Println(a, msg) // a와 msg의 값 출력
}
변수 선언
변수를 사용하기 위해서는 먼저 변수를 선언해야 한다. 변수 선언은 컴퓨터에게 값을 저장할 공간을 마련하라고 명령하는 것을 의미한다. 이를 메모리 할당이라고 한다.
변수 선언은 다음과 같이 할 수 있다.
var a int
- var은 variable의 약자로 변수를 선언하기 위한 키워드다.
- a는 변수의 이름이다.
- int는 변수 a의 타입으로 정수형을 의미한다.
- 값을 명시적으로 할당하지 않은 경우 초깃값은 타입별 기본값으로 대체된다.
변수 선언과 동시에 값을 초기화할 수 있다.
var a int = 10
- 변수 a를 int 타입으로 선언함과 동시에 값 10을 대입하여 초기화한다.
다음은 변수 선언과 초기화하는 간단한 예시다.
package main
import "fmt"
func main() {
var num1 int = 10
var num2 int = 20
var multiply int = num1 * num2
fmt.Println(num1, num2, multiply)
}
// 출력
10 20 200
변수의 4가지 속성
변수는 다음 4가지 속성을 가진다.
- 이름 : 프로그래머는 이름을 통해 값이 저장된 메모리 공간에 쉽게 접근할 수 있다.
- 값 : 변수가 가리키는 메모리 공간에 저장된 값이다.
- 주소 : 변수가 저장된 메모리 공간의 시작 주소를 의미한다.
- 타입 : 변숫값의 형태를 의미한다. 정수, 실수, 문자열 등 다양한 타입이 있다.
변수명 규칙
고언어에서 변수명을 지을 때는 다음과 같은 규칙을 따라야 한다.
- 변수명은 문자, 밑줄(_), 숫자를 사용해서 지을 수 있지만 첫 글자는 반드시 문자나 _로 시작해야 한다.
- _를 제외한 다른 특수문자를 포함할 수 없다.
예) number, a123, a_b_c, 하나, _둘
다음은 반드시 지켜야 하는 것은 아니지만, 지키길 권장하는 규칙이다.
- 변수명은 영문자를 제외한 다른 언어의 문자를 사용하지 않는다.
- 변수명에 여러 단어가 이어지면 두 번째 단어부터는 대문자로 시작한다.
- 예) firstName, studentNumber 등
- 변수명은 되도록 짧게 하며, 잠시 사용되는 로컬 변수는 한 글자를 권장한다.
- 밑줄 _은 특수한 경우를 제외하고 일반적으로 사용하지 않는다.
변수의 범위
package main
import "fmt"
var g int = 10 // 패키지 전역 변수
func main() {
var m int = 20 // 지역 변수
{
var s int = 50 // 지역 변수
fmt.Print(g, m, s)
} // 변수 s는 사라짐
m = s + 20 // 에러
}
- g는 전역 변수로 같은 패키지 내 어디서나 접근할 수 있다.
- m는 main() 함수에 속해 있는 지역 변수로 main() 함수가 끝나는 시점까지 접근할 수 있다.
- s는 자신이 속해 있는 중괄호 { }가 끝나는 지점에서 사라진다. 따라서 중괄호 이후 s는 사라졌기 때문에 에러가 발생한다.
타입(Type)
변수는 메모리 주소를 가리킨다면, 타입은 공간 크기를 나타낸다. 변수의 메모리 주소는 메모리 시작 주소를 뜻한다. 즉, 메모리의 끝 주소는 알지 못한다. 하지만, 타입은 메모리 공간의 크기를 나타내므로 타입을 알면 크기를 알 수 있다. 또한, 타입을 알아야 컴퓨터가 데이터를 해석할 수 있다. 데이터가 정수인지, 실수인지 등 타입을 통해 컴퓨터는 무슨 데이터인지 알 수 있다.
고언어에서는 숫자, 불리언, 문자열, 배열, 슬라이스, 구조체, 포인터, 함수, 인터페이스, 맵, 채널 등의 타입을 제공하고 있다.
숫자 타입
다음은 고언어에서 제공하는 숫자 타입 표다.
이름 | 설명 | 값의 범위 |
uint8 | 1바이트 부호 없는 정수 | 0 ~ 255 |
uint16 | 2바이트 부호 없는 정수 | 0 ~ 65535 |
uint32 | 4바이트 부호 없는 정수 | 0 ~ 4294967295 |
uint64 | 8바이트 부호 없는 정수 | 0 ~ 18446744073709551615 |
int8 | 1바이트 부호 있는 정수 | -128 ~ 127 |
int16 | 2바이트 부호 있는 정수 | -32768 ~ 32767 |
int32 | 4바이트 부호 있는 정수 | -2147483648 ~ 2147483647 |
int64 | 8바이트 부호 있는 정수 | -9223372036854775808 ~ 9223372036854775807 |
float32 | 4바이트 실수 | IEEE-754 32비트 실수 |
float64 | 8바이트 실수 | IEEE-754 64비트 실수 |
complex64 | 8바이트 복소수(진수, 가수) | 진수와 가수 범위는 float32 범위와 같음 |
complex128 | 16바이트 복소수(진수, 가수) | 진수와 가수 범위는 float64 범위와 같음 |
byte | uint8의 별칭 1바이트 데이터를 나타낼 때 사용 |
0 ~ 255 |
rune | int32의 별칭 UTF-8로 문자 하나를 나타낼 때 사용 |
-2147483648 ~ 2147483647 |
int | 32비트 컴퓨터에서는 int32 64비트 컴퓨터에서는 int64 |
|
uint | 32비트 컴퓨터에서는 uint32 64비트 컴퓨터에서는 uint64 |
- 크기를 신경 쓰지 않는 경우 일반적으로 int, float64를 사용한다.
그 외 타입
- 불리언 : 참(true) 또는 거짓(false) 두 가지 값만 가지는 타입이다.
- 문자열 : 문자열을 표현하기 위한 타입이다.
- 배열 : 같은 타입의 요소들로 이루어진 연속된 메모리 공간을 나타내는 자료구조이다. 한 번 길이가 정해지면 늘리거나 줄일 수 없다.
- 슬라이스 : 고언어에서 제공하는 가변 길이 배열이다. 슬라이스는 배열과 달리 길이를 늘리거나 줄일 수 있다.
- 구조체 : 필드(변수)의 집합 자료구조이다. 일반적으로 상관관계가 있는 데이터를 묶어놓을 때 사용한다.
- 포인터 : 메모리 주소를 값으로 갖는 타입이다. 포인터를 이용해서 같은 메모리 공간을 가리키는 여러 변수를 만들 수 있다.
- 함수 타입 : 함수를 가리키는 타입이다. 다른 말로 함수 포인터라고도 한다. 사용할 함수를 동적으로 바꿀 때 유용하게 사용된다.
- 인터페이스 : 메서드 정의의 집합이다.
- 맵 : 키(key)와 값(value)을 쌍으로 갖는 데이터를 저장하는 자료구조이다. 키를 사용해 값을 찾을 수 있다.
- 채널 : 멀티스레드 환경에 특화된 큐 형태 자료구조이다.
다양한 변수 선언 방식
package main
import "fmt"
func main() {
var a int // 변수 선언(타입의 기본값)
var b int = 3 // 변수 선언과 동시에 값 초기화
var c = 4 // 타입 생략, 변수 타입은 우변 값의 타입이 됨
d := 5 // 선언 대입문(:=) : var 키워드와 타입 생략
fmt.Print(a, b, c, d)
}
// 출력
0 3 4 5
- 변수 선언 시 값을 지정하지 않으면 기본 값으로 대체된다.
- 우변에 값을 지정하여 선언하는 경우 타입 생략이 가능하다.
- 선언 대입문(:=)을 통해 var 키워드와 타입을 생략하여 선언 및 초기화가 가능하다.
타입별 기본값
타입 | 기본값 |
모든 정수 타입(int8, ...) | 0 |
모든 실수 타입(float32, ...) | 0.0 |
불리언 | false |
문자열 | ""(빈 문자열) |
그외 | nil(정의되지 않는 메모리 주소) |
숫자값 기본 타입
타입을 생략하면 우변의 타입으로 변수의 타입이 결정된다. 만약, 우변이 숫자이면 기본타입으로 결정된다. 정수는 int, 실수는 float64가 기본 타입이다.
선언 대입문 :=
선언과 대입을 한꺼번에 하는 구문으로 var 키워드와 타입을 생략해 변수를 선언할 수 있다.
func main() {
a := 123 // int 타입으로 자동 지정
b := 3.14 // float64 타입으로 자동 지정
c := "hello world" // string 타입으로 자동 지정
fmt.Print(a, b, c)
}
타입 변환
프로그래밍 언어를 구분할 때 타입 검사를 하는가 안하는가에 따라 강 타입 언어와 약 타입 언어로 나뉜다. 고언어의 경우 강 타입 언어 중에서도 가장 강하게 타입 검사를 하는 최강 타입 언어다.
따라서 고언어에서 다음과 같이 연산이나 대입에서 타입이 다르면 에러가 발생한다.
a := 3 // int
var b float64 = 3.5 // float64
var c int = b // 에러 - float64를 int에 대입 불가
d := a * b // 에러 - int와 float64 연산 불가
var e int64 = 7
f := a * e // 에러 - int와 int64는 서로 다른 타입
var g int = b * 3 // 에러 - 실수가 정수로 자동으로 바뀌지 않음
- 같은 숫자값이라도 타입이 다르면 연산이 안되기 때문에 타입을 변환해서 연산해야 한다.
다음과 같이 타입 변환을 통해 위 코드를 에러 없이 작성할 수 있다.
package main
import "fmt"
func main() {
a := 3 // int
var b float64 = 3.5 // float64
var c int = int(b) // float64 -> int 변환
d := float64(a * c) // int -> float64 변환
var e int64 = 7
f := int64(d) * e // float64 -> int64 변환
var g int = int(b * 3) // float64 -> int 변환
var h int = int(b) * 3 // float64 -> int 변환, g와 같이 다름
fmt.Println(f, g, h)
}
// 출력
63 10 9
- 실수 타입에서 정수 타입으로 변환하면 소수점 이하의 숫자가 사라진다.
- 따라서 g와 h의 값은 서로 다른 결과를 나타낸다.
- g는 3.5 * 3이 먼저 계산된 10.5에서 int 타입으로 변환되어 값이 10이 된다.
- h는 3.5가 먼저 int 타입으로 변환되어 3이 되고 3을 곱하여 값이 9가 된다.
큰 범위를 갖는 타입에서 작은 범위를 갖는 타입으로 변환하면 값이 달라질 수 있다.
func main() {
var a int16 = 3456
var c int8 = int8(a) // int16 -> int8 변환
fmt.Println(a)
fmt.Println(c)
}
// 출력
3456
-128
- 2바이트 정수 int16에서 1바이트 정수 int8로 변환하면 상위 1바이트가 없어지기 때문에 값이 달라진다.
- 3456을 2진수로 표현하면 00001101 10000000이다.
- 이를 int8 타입으로 변환되어 상위 1바이트가 사라진 10000000가 되어 -128이 된다.
참고 서적
Tucker의 Go 언어 프로그래밍 - 공봉식님 지음
'Golang' 카테고리의 다른 글
[Golang] 고언어 fmt 패키지 표준 입력 - Scan(), Scanln(), Scanf() (0) | 2023.12.03 |
---|---|
[Golang] 고언어 fmt 패키지 표준 출력 - Print(), Println(), Printf() (0) | 2023.11.26 |
[Golang] 고언어 설치 및 개발 환경 설정 - 윈도우 & GoLand (0) | 2023.11.12 |