Golang切片slice存储微探索

问题

不断将获取的信息存储进切片slice中,为了方便更新信息中的某些字段,建立了一个key和切片项地址的map,一边在切片中存储信息,一边建立Key-Value关系,导致通过key取到的值并不正确,也导致需要更新的字段没有更新,代码类似于:

var dataSlice []interface{}

var keyToData = make(map[string]interface{})

var id = 0

for info := fetchInfo(key) {

    dataSlice = append(dataSlice, info)

    // keyToData[key] = &info 

    keyToData[key] = &dataSlice[id]

    id++

}

info := keyToData[key] // 获取的info不是正确的data,更新的信息同步到dataSlice中

分析

map中保存的指针没有指向dataSlice中的项

测试


type test struct {
    ID string
}

func main() {
    strArr := []string{}
    testArr := []test{}
    intArr := []int{}

    strA := "info"
    strB := "mation"

    fmt.Printf("string: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &strA, &strB, strArr, &strArr)

    strArr = append(strArr, strA)
    fmt.Printf("string array: \t arr: %v, pointer: %p, A: %p\n", strArr, &strArr, &strArr[0])
    strArr = append(strArr, strB)
    fmt.Printf("string array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", strArr, &strArr, &strArr[0], &strArr[1])

    testA := test{ID: strA}
    testB := test{ID: strB}
    fmt.Printf("test: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &testA, &testB, testArr, &testArr)

    testArr = append(testArr, testA)
    fmt.Printf("test array: \t arr: %v, pointer: %p, A: %p\n", testArr, &testArr, &testArr[0])
    testArr = append(testArr, testB)
    fmt.Printf("test array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", testArr, &testArr, &testArr[0], &testArr[1])

    intA := 0
    intB := 1
    fmt.Printf("int: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &intA, &intB, intArr, &intArr)

    intArr = append(intArr, intA)
    fmt.Printf("int array: \t arr: %v, pointer: %p, A: %p\n", intArr, &intArr, &intArr[0])
    intArr = append(intArr, intB)
    fmt.Printf("int array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", intArr, &intArr, &intArr[0], &intArr[1])
}

输出结果

string:      A: 0xc42000e1f0, B: 0xc42000e200, Arr: [], ArrPtr: 0xc42000a060
string array:    arr: [info], pointer: 0xc42000a060, A: 0xc42000e210
string array:    arr: [info mation], pointer: 0xc42000a060, A: 0xc42000a100, B: 0xc42000a110



test:    A: 0xc42000e250, B: 0xc42000e260, Arr: [], ArrPtr: 0xc42000a080
test array:      arr: [{info}], pointer: 0xc42000a080, A: 0xc42000e270
test array:      arr: [{info} {mation}], pointer: 0xc42000a080, A: 0xc42000a180, B: 0xc42000a190



int:     A: 0xc420014068, B: 0xc420014080, Arr: [], ArrPtr: 0xc42000a0a0
int array:   arr: [0], pointer: 0xc42000a0a0, A: 0xc420014088
int array:   arr: [0 1], pointer: 0xc42000a0a0, A: 0xc4200140a0, B: 0xc4200140a8

讨论

  1. 需要注意的是,通过append在切片slice中存储时,进行的是值传递;
  2. 每次调用append时,都可能会造成整个切片存储内存上的重新调整,通过元素地址可以反映出,每次append时,golang都会尽力保证一段连续的内存进行元素存储;
  3. 所以前面map保存的元素地址,可能不会指向最终slice中的元素;
  4. 不管内存上如何调整,都保证了切片入口地址的不变,而且这个地址并不等同于第一个元素的地址,所以这个节点应该和C链表中头节点的功能类似;
  5. 那么如果通过make创建定长的切片,这时的切片性质上和数组等同,请看补充测试:

补充测试:

    test2Arr := make([]test, 2, 2)
    test3Arr := make([]interface{}, 2, 2)
    var test4Arr [2]interface{}
fmt.Printf("test 2: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &testA, &testB, test2Arr, &test2Arr)

    test2Arr = append(test2Arr, testA)
    fmt.Printf("test 2 array: \t arr: %v, pointer: %p, A: %p\n", test2Arr, &test2Arr, &test2Arr[0])
    test2Arr = append(test2Arr, testB)
    fmt.Printf("test 2 array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", test2Arr, &test2Arr, &test2Arr[0], &test2Arr[1])

    fmt.Printf("test 3: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &testA, &testB, test3Arr, &test3Arr)

    test3Arr[0] = &testA
    fmt.Printf("test 3 array: \t arr: %v, pointer: %p, A: %p\n", test3Arr, &test3Arr, test3Arr[0])
    test3Arr[1] = &testB
    fmt.Printf("test 3 array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", test3Arr, &test3Arr, test3Arr[0], test3Arr[1])

    fmt.Printf("test 4: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &testA, &testB, test4Arr, &test4Arr)

    test4Arr[0] = &testA
    fmt.Printf("test 3 array: \t arr: %v, pointer: %p, A: %p\n", test4Arr, &test4Arr, test4Arr[0])
    test4Arr[1] = &testB
    fmt.Printf("test 3 array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", test4Arr, &test4Arr, test4Arr[0], test4Arr[1])

结果输出

test 2:      A: 0xc420066220, B: 0xc420066230, Arr: [{} {}], ArrPtr: 0xc420086080
test 2 array:    arr: [{} {} {info}], pointer: 0xc420086080, A: 0xc420072080
test 2 array:    arr: [{} {} {info} {mation}], pointer: 0xc420086080, A: 0xc420072080, B: 0xc420072090



test 3:      A: 0xc420066220, B: 0xc420066230, Arr: [<nil> <nil>], ArrPtr: 0xc4200860c0
test 3 array:    arr: [0xc420066220 <nil>], pointer: 0xc4200860c0, A: 0xc420066220
test 3 array:    arr: [0xc420066220 0xc420066230], pointer: 0xc4200860c0, A: 0xc420066220, B: 0xc420066230



test 4:      A: 0xc420066220, B: 0xc420066230, Arr: [<nil> <nil>], ArrPtr: 0xc420086100
test 3 array:    arr: [0xc420066220 <nil>], pointer: 0xc420086100, A: 0xc420066220
test 3 array:    arr: [0xc420066220 0xc420066230], pointer: 0xc420086100, A: 0xc420066220, B: 0xc420066230

结论

  1. test 2 验证了如果make定长的切片,即使通过append添加存储,也不会引起元素内存的重新分配
  2. test 3 和test 4 验证了make定长的切片和数组等同;
  3. 虽然性质等同,但是append还是可以为slice添加元素,通过下标进行赋值时,不能进行容量扩展,报越界错误。

@author liu shuohui
@date 2019-05-23

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容