go, golang, slice,array
GODOC
“Arrays have their place, but they’re a bit inflexible, so you don’t see them too often in Go code. Slices, though, are everywhere. They build on arrays to provide great power and convenience.”
slice는 make라는 constructure 역할을하는 init함수로 그 사이즈가 결정된다.
make는 다양한 collection에서 쓰이는데 slice에서 len, capacity 로 초기화된다.
//lendl 1이이고 cap이 5인 슬라이스를 만든다.
data := make([]string,1,5)
fmt.Printf("data len:%v cap:%v %v\n", len(data), cap(data),data)
일반적으로 var clice []int
와 같이 사용되는 slice를 nil slice
라고 말한다.
이유는 아래처럼 nil인 상태기때문이다
nil slice는 len:0 capa:0 인상태이고, 주소값이 0x0으로 되어있다.
append할경우 init이 되며 메모리에 실제 값이 할당된다.
실제 특정 structrue에 append작업이 들어가지 않은 nil slice를 넣게되면 nil값으로 그 값이 할당된다.
그러나 make([]string,0)
로 초기화게되면 [] 비배열이 할당된다.
var s []int
if s== nil //=> 이값은 true이다.
append는 slice에 값을 추가한다.
data := make([]string,1,3) data = append(data,”test1”)
append가 확장 될때는 capa안에서는 같은 주소값으로 지속적으로 추가된다.
capa를 넘어가면 어떻게될까
data := make([]string,0,3)
fmt.Printf("data addr:%p len:%v cap:%v %v\n", data, len(data), cap(data),data)
data = append(data,"test1")
data = append(data,"test1")
data = append(data,"test1")
fmt.Printf("data addr:%p len:%v cap:%v %v\n", data, len(data), cap(data),data)
data = append(data,"test1")
fmt.Printf("data addr:%p len:%v cap:%v %v\n", data, len(data), cap(data),data)
////////
//output
data addr:0x446280 len:0 cap:3 []
data addr:0x446280 len:3 cap:3 [test1 test1 test1]
data addr:0x43c0c0 len:4 cap:6 [test1 test1 test1 test1]
data := make([]string,0,3)
fmt.Printf("data addr:%p len:%v cap:%v %v\n", data, len(data), cap(data),data)
data = append(data,"test1")
fmt.Printf("data[0] addr:%p \n",&data[0])
type Timi struct {
ti []string
}
func main() {
var a []string
var timi []Timi
a = a[:0]
fmt.Printf("a adrr:%p \n", a)
fmt.Printf("a len:%v cap:%v %v\n", len(a), cap(a),a)
a = append(a, "hi")
fmt.Printf("a adrr:%p len:%v cap:%v %v\n", a, len(a), cap(a),a)
timi = append(timi, Timi{ti: a})
fmt.Printf("timi.ti:%v addr:%p \n", timi,timi)
a = a[:0]
// !@ 참고: 놀랍게도 a를 0인 배열로 초기화 시킨것 같지만 사실 아래를 보면 실제 값이 남아있다.
// 즉 a[:0]은 실제 내부 배열을 초기화한것이 아니라 그냥 length를 0으로 바꾼것일 뿐 나머지 값들은 존재하게 되는것이다.
// 하여 [:1]을 이용해 앞에서부터 1번째까지 범위를 잡고 찍게되면 결과가 출력된다.
// 그래서 여기에는 안나오지만 a=a[:1]등을 실행하게되면 다시 값들이 보이기시작한다.
fmt.Println(a[:1]) // [hi]
fmt.Printf("a adrr:%p len:%v cap:%v %v\n", a, len(a), cap(a),a)
a = append(a, "nic1")
fmt.Printf("a adrr:%p len:%v cap:%v %v\n", a, len(a), cap(a),a)
fmt.Printf("timi.ti adrr:%p %v \n", timi,timi)
a = append(a, "nice2")
fmt.Printf("a adrr:%p len:%v cap:%v %v\n", a, len(a), cap(a),a)
timi = append(timi, Timi{ti: a})
fmt.Printf("timi.ti adrr:%p %v \n", timi,timi)
}
//////////////
// ouput
a adrr:0x0
a len:0 cap:0 []
a adrr:0x40c140 len:1 cap:1 [hi]
timi.ti:[{[hi]}] addr:0x40a100
[hi]
a adrr:0x40c140 len:0 cap:1 []
a adrr:0x40c140 len:1 cap:1 [nic1]
timi.ti adrr:0x40a100 [{[nic1]}]
a adrr:0x40a1c0 len:2 cap:2 [nic1 nice2]
timi.ti adrr:0x43e280 [{[nic1]} {[nic1 nice2]}]
Timi{ti: a}
식으로 추가되면 해당 a의 값이 structure를 만드니까! value값으로 박제(?), 사진(?) 찍듯이 들어갈것처럼 느껴졌기 때문이다.(문제는 이 느낌
이다.. 그냥 될거같아.. 아무 이유없이..)[:0]
의 코드를 make로 바꾸어 새로이 할당하는 방식으로 작동하게 하여야한다.// 화살표 오른쪽과 같이 수정되어야한다.
a = a[:0] => a = make(0,0)
최초 이 글을 쓰게된 에러를 발생한 경우가 실은 이와 관련없는 다른 질문을 직속 선배에게 물어보다가.. 우연찮게 얻어걸렸는데 위와 같이 레퍼런스를 이용한 코드였다. 잘못된 데이터가 지속적으로 들어갈 치명적인 결함이 될 수 있었다.
원인에는 전형적인 필자의 문제가 있는데.. 코드의 동작 결과를 눈여겨 확인하지 않는데
그 이유가 있다.. 즉 너무나도 잘 동작할 것 같아 대략적으로 결과를 스쳐지나가듯 본것이다.
동작하는 최소한의 bestcase만 눈여겨 확인했어도 위와 같은 오작동 코드는 나오지 않았으리라..
두 가지 Action item이 있어야 겠는데
누가 그랬다.
잘 짠 코드는 프로그램의 high performance를 보장하는 것처럼 보이지만, 실제로는 기대하는 input에 기대하는 output이 나오는 코드라는 것.