「Golang」遇到的有趣的Go赋值问题并附带以下不负责任的解析

今天在做一道题的时候发现了这么一个有趣的题目:

func main() {
   
	index := 1
	a := []string{
   "f", "ff", "fff"}
	index, a[index-1] = 88888, "ffff"
	fmt.Println(a)
}

这个输出是什么呢?我第一眼看的时候感觉会输出panic,因为下标超界,但是运行之后发现没这么简单,运行结果如下

运行之后我惊了,不知道为什么会这样,然后我就输出了汇编看了一下(去掉了一些多余的汇编代码,我们只看赋值那行的汇编):

本篇文章基于go1.15+版本以及mac os 操作系统

//把index的值复制到DX寄存器中
0x00f2 00242 MOVQ    "".index+48(SP), DX 
 // DX寄存器的值-1 在此能看出是先对其进行了-1
0x00f7 00247 DECQ    DX 
// 把DX寄存器里的值复制autotmp_4+56这个地址上
0x00fa 00250 MOVQ    DX, ""..autotmp_4+56(SP) 
// 把88888赋值给index+48地址即赋值88888给index
0x00ff 00255 MOVQ    $88888, "".index+48(SP)
// 把刚才-1了的值放在AX寄存器中
0x0108 00264 MOVQ    ""..autotmp_4+56(SP), AX
// 把a就是那个数组的偏移104个地址的值放入DX寄存器
0x010d 00269 MOVQ    "".a+104(SP), DX
// 把a就是那个数组的偏移112个地址的值放入DX寄存器
0x0112 00274 MOVQ    "".a+112(SP), CX
// 比较AX寄存器和CX寄存器 
0x0117 00279 CMPQ    AX, CX
// 如果没溢出就跳到293
0x011a 00282 JCS     293
// 否则
0x011c 00284 NOP
// 跳到605
0x0120 00288 JMP     605
// 逻辑移位(好像是)
0x0125 00293 SHLQ    $4, AX
// 把DX的AX*1的地址给CX Ax就是被-1后的那个值存放的地方
0x0129 00297 LEAQ    (DX)(AX*1), CX
0x012d 00301 LEAQ    8(CX), CX
0x0131 00305 MOVQ    $4, (CX)
// 反正就是之前做了一堆复制和传地址操作把DX的AX*1的地址给了DI
0x0138 00312 LEAQ    (DX)(AX*1), DI
·····
// 把ffff这个字符串放到AX寄存器内
0x014a 00330 LEAQ    go.string."ffff"(SB), AX
// 从AX寄存器复制出ffff复制到DI就是要修改的数组的那个位置
0x0151 00337 MOVQ    AX, (DI)

大概就是这样,其实最根本的原因就是

index-1这个操作早于给index赋值这个操作,并且提前将其放到了某块地址中所以后续的数组下标赋值操作是根据这个放在某个地址中的index-1的值进行的赋值。

总结一下:

这种多重赋值分为两个步骤,有先后顺序:
计算等号左边的索引表达式和取址表达式,接着计算等号右边的表达式

作者对汇编不甚了解,如果哪里有错误请大佬指正。

全部评论

相关推荐

upLuck:比我强查看图片
点赞 评论 收藏
分享
1. 自我介绍2. 项目都是自己写的吗?3. 我看你用 koa2 写后端,为什么选择它,能讲讲吗?4. 那你提到 koa2 它是不提供中间件的,你是怎么解决的?5. 中间件的原理是什么?(洋葱模型)6. 你刚刚说碰到 next() 就进入下一个中间件,那 next 只能执行同步,如果是异步的话,你是怎么处理的?(async/await,但是我发现,有的中间件需要在异步中间件之前执行,所以我用 try/catch 来处理异步中间件的异常)7. JS 异步发展史,以及它们的优缺点说一下 (回调函数--Promise--Generator--async/await)8. 你刚刚说 Promise 状态不能更改,那如果我要设计一个能修改 Promise 状态的函数,你会怎么设计?9. CSS 水平垂直居中的方法(flex、grid、绝对定位 + margin:auto、绝对定位 + 负 margin、绝对定位 + transform、table-cell)10. 你刚刚说到 flex 布局,那 flex:1 是什么意思?(flex: flex-grow  flex-shrink  flex-basis;等价 flex:1 1 0%表示元素可以均分剩余空间,可拉伸、可压缩,不依赖内容宽度,自动自适应填充布局。)11. 父容器宽是 500px,然后它左右各有两个子容器是 100px,如果设置 flex: 1,那它的宽度是多少?(500-100-100=300px)12. 说说你对浏览器缓存的理解(强缓存、协商缓存)13. 如果一个用户,他怎么去刷新都无法刷到最新版的代码,你能说下可能的原因吗?(版本号、hash等)还有吗?(我说我不知道了,面试官说还有 CDN 没有同步,我说企业才会这么干,自己写项目一般不会,我知道 cdn 是用来解决高并发的手段)14. React你熟吗?说下 React 函数组件和类组件的区别15. 怎么避免 Hooks 导致组件重新渲染?(使用 useCallback、useMemo、React.memo、useRef等等)16. 谈一下我对 React 的状态管理的理解(Redux、Mobx、Zustand,我说 Zustand 用的最多)17. React 常见的 hooks 有哪些?(useState、useEffect、useRef、useCallback、useMemo、useReducer、useContext、useImperativeHandle、useLayoutEffect、useDebugValue)18. TS 你熟吗?我们引进 TS 的目的是为什么?19. interface 和 type 的区别20. 说下 TS 里的泛型21. 我现在有十个字段,比如十个字段就要 A B C D E F G 这种。那我现在另有另外一个方法,这个方法接受的参数呢,必须是这个 interface A 里面的这个 K。就比如说你可以是 A B C 可以 A B C D 任何组合都可以,但是必须是这个 interface 里面的 A 里面的定义的。这个 K 这种类型的话是怎么去定义呢?(说实话我有点不太理解啥意思,反正我说了 keyof)``` TypeScriptinterface Obj {A: stringB: stringC: stringD: stringE: string// 其他字段...}```22. vite 用过吗?说说和 webpack 的区别。vite 的优缺点是什么23. 说说 Tree shaking(树摇) 和 Code Splitting (代码分割)的区别24. Git 你熟吗?说说 git merge 和 git rebase 的区别,什么时候用 git merge,什么时候用 git rebase?25. web3 你熟吗?(不太熟,听说过而已)26. 我看你自我介绍说了 AI,你是怎么用的?27. 除了提示词,还有什么能让 AI 更聪明?28. AI 的优缺点你说一下29. AI 发展这么快,你觉得我们以后会扮演什么角色?30. 反问基本都答上来了。面了我80分钟,我还以为稳过的
查看29道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务