[Go] 변수와 데이터 타입에 대한 개념

김무연·2025년 1월 20일

변수 및 타입

빌트인 타입

  • 타입은 두 가지의 질문을 통해 완전성과 가독성을 제공한다.
  1. 할당한 메모리의 크기는 얼마인가? (32bit, 64bit..)
  2. 이 메모리는 무엇을 의미하는가? (int, uint, bool...)

이와 같은 질문을 통해 명확한 이름을 가진 타입이 존재한다. int32, int64

  • uint8은 1 바이트 메모리에 10진수 숫자를 가지고 있다.. (1byte = 8bit)
  • int32는 4 바이트 메모리에 10진수 숫자를 가지고 있다.. (4byte = 32bit)

이와 같이 명확한 타입이 존재하는 반면, uint, int같이 메모리의 크기를 선언하지 않게 된다면, 아키텍쳐에 따라 크기가 달라진다. 64bit-OS라면 int = int64 과 같은 타입이 될것이고, 32bit-OS라면 int = int32 와 같은 크기가 된다.

워드 크기

  • 워드란 컴퓨터 설계시 기본적으로 정해지는 메모리의 기본 단위를 의미
  • 예를 들어, 애플과 같은 8비트 컴퓨터에서는 1바이트가 1워드이며, 16비트 컴퓨터에서는 16비트, 즉 2바이트 1워드가 될것이다.
  • 64bit 아키텍쳐에서는 당연히 워드 사이즈는 64bit = 8byte로 당연히 8byte가 되겠지?, 다라서 메모리 주소의 크기도 64비트가 되어 int = int64bit가 된다.

문자열은 uint8 타입의 연속이다

  • 이 말인 즉슨 문자열을 "가나다" 와 같은 경우 하나의 문자를 보면 "가", "나", "다"가 된다. 그렇다면 각각의 문자는 한글이라서 3바이트를 가지게 되는데 어떻게 uint8 과 같이 1바이트의 연속이라 할 수 있을까?
  • "가"를 보자면 UTF-8로 인코딩하면 '0xEA 0xB0 0x80'로 표현이 된다. 각각의 0xEA와 같은 1바이트의 시퀀스로 저장되게 되는것이다. 따라서 1바이트는 uint8 의 타입이 되니 결국은 uint8타입의 연속이란 말이 맞게 된다.
  1. 문자열 : uint8 타입의 연속이고 이는 UTF8 인코딩 된 문자를 나타낸다.
  2. rune : unit32의 별칭이고, 주로 Unicode 코드포인트를 표현하는 데 사용된다.

다시 말하자면 utf8 인코딩 은 다양한 길이의 바이트 시퀀스를 이용해 유니코드 문자를 나타낸다. 
어떤 문자는 1바이트, 어떤 문자는 2,3,4 바이트로 나타내야 할 수도 있다. 그렇기 때문에 for 문을 이용해서 range 처리를 하게 되면
uint8로 표현을 하게 되면 올바르게 utf-8 을표현할 수가 없다. 따라서 rune을 이용해 반환하게 된다. 

  • 또한 문자열은 두 개의 워드로 된 데이터 구조체이다. 첫 번째 워드는 뒤에 숨겨져 있는 배열을 가리키는 포인터이고, 두 번재 워드는 문자열의 길이이다. 문자열의 제로값은 첫 번째 워드는 nil, 두 번째 워드는 0이다.
  • 여기서의 길이란 것은 단순히 몇 글자인지가 아닌, 바이트 단위로 길이를 계산한다. 따라서 "가나다"의 경우에는 첫 번재 워드에는 주소값인 (ex) 0x12345678)등이 들어갈 것이고, 두 번째 워드에는 9 가 들어가게 된다.

변수

생성되는 모든 변수는 초기화되어야 한다. 어떤 값으로 초기화할지 명시하지 않는다면, 제로값으로 초기화된다. 할당된 메모리의 모든 비트는 0으로 리셋된다.

  1. 변수란 특정 값을 저장하는 메모리 공간을 뜻한다. 변수를 지정하는 법은 언어마다 조금씩 다른데, go에서는 var x int 형식으로 선언한다. 이렇게 선언을 하며 값을 초기화 하지 않으면 자동으로 Zero Value 즉 숫자형은 0으로, 문자열형은 빈 공백으로, 논리형은 false로 초기화 된다.

    정수 = 0
    실수 = 0.0
    문자열 = ""
    bool = false
    배열 = 변수 타입에 따른 값으로 제로값으로 설정
    슬라이스 = nill
    포인터 = nil

  2. 만일 선언과 동시에 초기화 하고 싶으면 var x int = 5 또는 x := 5로 축약해서 선언할 수도 있다. 이러할 경우 타입이 지정되지 않았지만, 5는 정수이기 때문에 자동으로 판별해 x는 자동으로 정수형 타입이 되게 된다.

  3. 즉 첫 번재 방법은 타입을 직접 선언하고 값을 초기화 하지만, 축약문을 써서 선언할 경우, 타입 추론을 통해 변수를 선언하게 된다.

  4. 축약 선언은 함수 바깥에서는 쓸 수가 없다

  1. Var (변수)
package variable

import "fmt"

//① 변수를 하나 선언
var num1 int

//② 같은 타입을 가지는 변수를 여러 개 선언
var num2, num3 int 

//③ 여러 변수에 한 번에 값을 초기화 : 선언과 동시에 값을 초기화하면 타입을 명시할 필요가 없습니다.
var num4, num5, str1 = 4, 5, "example" 

// var, const를 생략하고도 가능하다
message, hi, who, goodBye := "Hello, Go!", "Hi", "Who R U", "GoodBye"

//④ 함수 밖에서는 :=를 쓸 수 없습니다.
errorvar := str1

//⑤ 다른 타입을 가지는 변수를 여러 개 선언
var (
    i int
    b bool
    s string
)

// 여러 값 초기화 및 동시 선언
const (
	a int = 1
    b int = 2
    c int = 3
)

func main(){
    fmt.Println("①", num1)
    fmt.Println("②", num2, num3)
    fmt.Println("③", num4, num5, str1)
    
    //④ 함수 안에서는 :=를 쓰면 var과 타입을 지정하지 않고 변수를 선언과 동시에 초기화할 수 있습니다.
    num6 := 6
    fmt.Println("④", num6)
    
    fmt.Println("⑤", i, b, s)
}

변수 재할당과 선언

  • 기본적으로 변수는 재선언이 불가능하다.
package main

func main() {
	var a int = 1

	var a int 

	print(a)

}

---------------
error : a redeclared in this block

위처럼 이미 선언 되었다고 오류가 출력이 된다. 하지만 여기서 여러개 선언으로 변경이 된다면?

package main

func main() {
	var a int = 1

	a,b := 2,3

	print(a, b)

}


// another Code
package main

func main() {
	var a int = 1

	a := 2
    
    print(a)
    
}

-------
error : no new variables on left side of :=

첫 main함수를 보면 분명히 a를 축약으로 재선언 하고 있는데, 오류가 나지않고 출력이 된다. 하지만 두 번째 main 함수를 보면 오류가 나온다.

이는 단축 변수 선언의 특성인데, 하나라도 새로운 변수가 있으면, 이는 단축 변수 선언 문법을 사용하더라도 기존 변수에 대해서는 재선언이 아닌 재할당이 이루어 지게 된다.

즉 첫 번재 main함수의 단축 선언 a,b 할당에서 a는 재할당이 이루어진 것이고, b는 선언이 되게 된 것이다 풀어서 쓰면
a = 2
b := 1
이렇게 된 것이라 볼 수 있다.

  1. Const (상수)

상수는 변수와 비슷하지만, 한 번 선언 후 값의 재선언이 불가능하다. const 키워드를 사용하여 선언 가능

자료형 명시는 컴파일러가 자동으로 추론해주기 때문에 선택적으로 해주면 된다. 또한 전역 범위, 지역 범위 상수 모두 컴파일 타임에 초기화 된다.

package main

import "fmt"

func main() {
	const MAX int = 10
    
  	fmt.Println(MAX)
}

iota

상수값을 0부터 순차적으로 부여하기 위해 iota라는 identifier를 사용할 수 있다. 이 경우 첫 번째 변수에 0이 할당되고, 나머지 상수들이 순서대로 1씩 증가된 값을 부여받는다

const (
	a = iota // 0
    b		 // 1
    c		 // 2
    )

데이터 타입

데이터 타입(자료형)이란 프로그램에서 실수, 정수, 문자열 등 데이터 식별 타입

Golang은 강타입언어이다.

Golang은 강타입 중에서도 강타입 언어이다. 약타입이냐, 강타입이냐에 따라 연산을 할 수 있는 방법이 확장될 수도, 제한이 될 수도 있다.

약타입

  • 프로그래밍 언어에는 타입이 존재하는데, 약타입 언어 같은 경우는 정수와 실수간의 계산을 허용하기도 한다. 예를 들면 javascript 에서 '1' + 2 와 같은 연산이 가능한데 결과는 '12'로 나오게 된다.
  • 이러한 허용을 하게 되면, 연산결과가 정확하지 않고 예측하기가 어려워져, 실수나, 에러 버그로 이어질 수 있다. 쓰기 편하다는 장점은 있지만 높은 자유도가 독이 될 수 있다.

강타입

  • 강타입 언어는 약타입과는 다르게, 타입이 다른 변수 간의 연산을 허용하지 않는다. 물론 숫자에 한해서는 정수 + 실수 연산을 허용하기도 한다 (c언어). 하지만 Golang은 강타입중의 강타입 언어이기 때문에 이 또한 허락하지 않는다. 정수 + 실수도 안된다.
  • 강타입 언어의 장점은 타입을 정확하게 인지하고 사용하기 때문에 컴파일 과정을 거치며 에러를 발생시켜, 의도치 않은 계산 결과를 도출하지 않을 수 있다.

문자열은 uint8타입의 연속이다.

  1. 정수형

    singed, unsigned가 있다. singed는 음수를 표현할 수 있지만, unsigned는 0부터 시작하는 수이다. 비트에 따른 표기도 가능하다. 기본적으로 타입 추론으로 숫자가 들어가면 대표 타입인 int가 된다. int는 signed integer 이고 uint는 unsigned integer

    int, uint, int8, uint16, int16.... 등이 있다.

  2. 실수형

    실수형은 unsigned가 없다. complex64와 complex128은 복소수 타입이라서 거의 쓸 일이 없고, float32, float64를 주로 쓴다. 다만 float 32는 정밀도가 낮아, 소수점 몇 째 자리에서는 의도하지 않은 결과가 나올 수 있어, float64를 주로 쓴다. 대표타입은 float64

    float32, float64, complex64, complex128

  3. 문자열 형

    string은 다른 언어와는 조금 다르게 반드시 쌍 따옴표(double quota)안에 내용이 있어야 한다. 홑 따옴표(single quota)는 다른 데이터 타입이다

    str := "hello go"
    var str string = "q"

  4. 논리형

    true / false

    b := true
    var bool bool = false

  5. 문자형

    byte형은 uint8의 별칭이며, rune는 int32의 별칭이다. 즉 int32로도 utf-8문자 표현이 가능하다는 뜻이다. go 언어에서는 다른 언어와는 다르게 하나의 문자를 표현하는 char 같은 데이터 형이 없고 byte와 rune가 대신한다.

    rune 데이터 타입은 utf-8 형식의 문자 하나를 표현하는데 사용되며, byte 타입은 ascii 형식의 문자 하나를 표현하는데 사용한다. 문자열 형이 쌍 따옴표를 사용하는데 반면, 문자형인 byte와 rune는 홑 따옴표를 사용한다. 대표타입은 rune

    byte, rune

profile
Notion에 정리된 공부한 글을 옮겨오는 중입니다... (진행중)

0개의 댓글