
2020年秋招那会儿计算机相关专业的群里每天都在流传新的笔试链接。我印象最深的是奇安信发来的这套Golang方向试卷1原因不是它难到离谱而是它和当时互联网大厂的题库风格明显不同整张卷子带着浓厚的安全厂商底色出题角度不只在考你会不会写Go还在考你会不会用Go去处理真实链路里可能出现的恶意问题。整场考试2小时题型覆盖单选、多选、填空、代码题和开放设计题。知识点集中在Go语言基础、并发调度、内存管理、网络协议、操作系统和基础安全常识。当时我做完整套题最大的感受是如果只会“背八股”或者“闷头刷LeetCode”遇到后面的安全场景题会很吃亏。下面这份复盘是根据我考后整理的回忆版考点写出来的不代表原卷原件但题目背后的知识点都是实打实的。想投Golang方向校招的同学不管目标公司是不是安全厂商这套题都值得认真拆一遍。1. 这套卷子想筛选什么人1.1 安全公司的Golang工程师日常做什么说到奇安信很多人第一反应是安全公司然后就会想安全公司也招Golang工程师吗当然招而且需求不小。安全产品里大量后端服务需要处理高并发请求比如漏洞扫描器、资产探测平台、检测引擎、安全数据平台、客户端Agent这些场景用Go写非常顺手。Go语法简单、部署方便、高并发能力强内存安全特性也适合处理不可信输入。这也决定了笔试的出题方向和普通业务后端不太一样。除了Go语言本身试卷还会考察你是否理解网络请求是怎么被处理的、文件路径能不能被“聪明人”钻空子、日志里的异常数据有没有风险。如果你只按普通互联网公司的套路刷题遇到安全场景题时往往会发虚。1.2 2020年秋招的考查风向2020年前后Golang校招岗位数量明显上涨很多公司把Go作为核心开发语言对候选人的要求也从“会写”变成了“懂原理”。奇安信这套试卷里出现了不少关于GMP模型、GC触发时机、内存逃逸的题目这类题目在几年前很少出现在校招笔试中。出题人想看的不是你会不会背概念而是你能不能把这些机制串起来。比如一个高并发服务为什么内存涨得快为什么GC频繁为什么原子操作比加锁快顺着这个思路复习比死记硬背概念高效得多。1.3 拿到试卷后我的第一反应点开试卷后我做了个动作先把所有题目快速扫一遍。因为有些公司会把难题放在最前面心态不好的人会直接卡死在第一题。这套试卷的选择题、多选题占比很高代码题和设计题合计大概占一半分值整体压力不是单点爆发而是面面俱到。我当时的策略是先做有把握的部分把难题标记出来最后再回头啃。这个策略在后面好几场笔试里都被我反复使用基本没有翻车。2. 试卷结构与时间分配先搞清楚分值再动手2.1 回忆版题型分布根据我的记忆试卷结构大概是这样的题型题量分值占比建议用时单选题20题30%25-30分钟多选题10题20%18-20分钟填空/判断10题10%8-10分钟代码题2题25%30-35分钟系统设计题1题15%20-25分钟这个分布不一定和当年完全一致但基本体现了奇安信笔试“广覆盖、重基础”的特点。单选占比最大说明出题人关注基础知识的扎实程度代码题与设计题加在一起接近一半说明工程能力是真正的分水岭。特别要注意多选题。很多多选要求全选对才得分漏选、多选、错选都算错。做多选时心态要保守拿不准的选项不要选。2.2 做题顺序建议我的建议是四步走第一轮快速完成单选、填空、判断题控制在40分钟以内。第二轮写代码题先用注释列思路再动手实现。第三轮做系统设计题按照“需求-接口-存储-并发-扩展”的顺序展开。最后检查多选和之前标记的难题。为什么要先写代码题而不是死磕多选因为代码题分值高不写完就是0分选择题即使犹豫也很可能猜对。另外在线笔试的代码框一般没有自动补全纯手写代码需要预留出额外时间。2.3 最容易丢分的三个地方第一代码题只写了核心函数没有考虑边界条件。数组为空、字符串超长、链表头节点为nil这些都是低级失误但非常常见。第二系统设计题只写了一个方案没有提到异常处理和高并发下的取舍。面试官后续追问时会很被动。第三安全场景题用普通业务开发思路作答忽略了攻击者视角。比如问如何防止路径遍历你只回答“用正则过滤点”就会被判定为安全意识不够。3. Go基础题看着简单错起来很疼3.1 切片和数组底层数组共享问题切片和数组的题目几乎是Golang试卷必出奇安信这套也不例外。比如这样的代码arr : []int{1, 2, 3, 4, 5} s1 : arr[1:3] s2 : arr[2:5] s1[0] 100 fmt.Println(arr, s1, s2)这里s1[0]和arr[1]共享底层数组元素修改s1[0]会同时改变arr但不会影响s2因为s2是从arr[2]开始的。更常见的陷阱是append扩容当切片长度小于容量时append直接写入底层数组当容量不够时Go会分配新的底层数组。如果题目给你写s1 append(s1, 6)你以为只是给s1追加实际上它可能改写了底层数组的其他位置。这个知识点不算难但非常能区分平时是否真正写过Go。选择题里只要把“共享”和“扩容”这两个关键词抓住基本不会错。3.2 map三个隐藏细节map也是选择题常客而且一考就是三个点。第一map的遍历顺序是随机的。依赖遍历顺序写逻辑属于程序错误。第二读取不存在的key不会报错而是返回value类型的零值。所以判断key是否存在必须写v, ok : m[k]不能只靠v是不是零值来判断。第三多个goroutine并发读写同一个map会直接触发fatal error: concurrent map read and map write而且这个错误无法被recover捕获程序直接崩。解决方案通常是加sync.RWMutex或者用标准库的sync.Map。注意sync.Map适合读多写少、key集合相对稳定的场景不是万能药。3.3 string是只读的别用加号拼个没完string类题目出现频率也很高。Go的string本质上是一个只读的字节序列底层是[]byte但你无法像切片一样修改它。len(s)返回的是字节数不是字符数。如果一个字符串包含中文直接遍历得到的是一个个字节需要先转成[]rune才能按字符处理。拼接字符串时如果用在循环里累加每一次拼接都会创建新的字符串性能很差。正确做法是用strings.Buildervar b strings.Builder for i : 0; i 1000; i { b.WriteString(a) } s : b.String()这也是很现实的工程问题一个日志拼接函数如果写得不讲究热点路径上就会产生大量内存分配。3.4 defer、panic、recover的执行顺序这套组合拳笔试几乎必考。defer的执行顺序是后进先出函数return之前会依次执行所有defer。要注意defer的参数在注册时就已经完成求值而不是在真正执行时求值。看这个例子x : 1 defer fmt.Println(x) x 2输出的是1而不是2。因为fmt.Println(x)的参数x在defer语句执行时就被求值了。但如果defer的是一个闭包闭包捕获的是变量引用结果就会变成2。除此之外recover必须在defer的函数里调用并且只在当前goroutine生效。子goroutine里的panic父goroutine无法用recover捕获。这类题目经常设计成“程序会输出什么”或者“程序会不会panic”把上述规则记熟就能拿分。3.5 接口、nil和类型断言接口题是另一个容易错的地方。Go接口底层由类型和值两部分组成判断接口是否为nil必须类型和值都为nil。一个很经典的题目var p *int nil var i interface{} p fmt.Println(i nil) // falsep明明是nil但赋给interface后接口却不为nil因为接口的type部分是*int。这在真实代码里会导致逻辑判断错误很多同学第一次遇到都会懵。类型断言v, ok : i.(int)也是必考要说明两个返回值的作用一个是断言成功后的具体值另一个是断言是否成功。顺手提一句空接口和any等价能显得你知识更连贯。4. 并发编程与调度模型Golang的招牌菜4.1 goroutine和channel的基础用法Golang岗位并发是躲不开的。奇安信试卷里的并发题可以从选择、填空一直考到代码题。先看channel的基本用法无缓冲channel的发送和接收必须同时就绪否则会阻塞当前goroutine。经典例子ch : make(chan int) go func() { ch - 1 }() fmt.Println(-ch)如果把go func去掉ch - 1会一直阻塞然后死锁。笔试经常问“这段代码会不会死锁”只要你明确说出阻塞条件题目就拿下