1. 到了第19天,复习重点应该往哪放
昨天收到一条私信,问我“复试编程现在刷了多少题才够”,我愣了一下,发现这个问题本身就问偏了。今天是我开始系统准备某高校网安复试编程的第19天,老实说,数据结构的经典题我大概过了六十多道,但真正让我觉得能摸到复试门槛的,反而是最近三天做的几道“看起来很简单”的协议解析类题目。如果你也在准备网安方向的复试上机,我强烈建议你把复习重心从“刷题数量”切换到“代码落地质量”上。
网安复试编程和考研初试的算法题有一个非常明显的区别:初试算法题看重的是思路和复杂度分析,很多题用伪代码就能拿大部分分;复试上机则完全不一样,它会给你一份残缺的样例代码、一组奇怪的输入格式,甚至直接让你从一个十六进制报文字符串里提取字段,编译不通过或者输出格式差一个空格,分数就没了。换句话说,复试考的不是“你会不会算法”,而是“你能不能在一个小时内写出干净、可运行、抗造(健壮)的C代码”。
Day19这个时间点,其实是一个很微妙的阶段。量已经攒了一些,但熟练度还没到肌肉记忆的程度。这时候最适合做的事,就是把那些“看着会、一写就废”的题目类型集中攻克。我自己的感觉是,网安方向的复试编程,高频考察方向有三个:数据结构基础、字符串与协议报文处理、Linux下的文件与进程操作。今天重点讲字符串和协议报文处理,因为这一块最贴近网安的实际场景,也最容易出现各种隐蔽的边界坑。
1.1 复试编程和初试算法题的区别
先帮你把认知调整过来。复试编程的判分标准通常是“程序能跑出正确结果”优先,算法优雅程度反而排在后面。我模拟过几所高校的复试环境,发现上机考试的系统多半是一个很朴素的在线评测平台,只支持C/C++,有时候甚至没有代码补全和格式化工具。你平时在本地IDE里跑得好好的代码,复制到评测机上可能因为头文件缺失、声明顺序不对就直接编译失败。
另外,复试题目里经常出现的输入不是标准的美观格式,而是带着陷阱的:可能有多余的换行、字符串中间有连续空格、十六进制字符既有大写又有小写、数据长度在题目描述里故意不写清楚。这些都是在初试刷题时很少遇到的。初试算法题的输入输出都是被规范好的,你只需要关心核心逻辑;复试就不一样了,输入数据的解析往往占了一半的工作量,甚至比算法本身还容易出错。
1.2 为什么网安方向的复试上机偏爱“协议解析题”
网安方向的复试命题老师,其实就是在模拟他们日常工作中会遇到的任务。安全工具要解析流量包、要提取恶意代码的特征串、要计算数据包的校验和,这些都是最基本的技能。所以题目很容易包装成“给定一个Hex格式的报文,提取某个字段并计算校验和”这样的形式。它考察的不是某个复杂的数据结构,而是你对数据底层表示的理解:字节和字符的区别、大小端序、补码运算、边界对齐。
我一开始觉得这类题很简单,直到有一次在本地测试通过、交上去却死活不对,后来排查了半小时,发现是题目里的报文数据是“字符串形式”而不是“字节流形式”,我直接用strlen去算长度,导致整个解析偏移量全错了。从那以后我就明白,网安复试的编程题,核心不一定是算法难度,而是“你有没有按真实协议处理的方式来写代码”。今天的文章,就是把这类题从原理到代码彻底讲透,帮你把这一类送分题稳稳抓住。
2. 协议报文解析三兄弟:十六进制转换、校验和、Base64
如果说网安复试编程有一个“题库三兄弟”,那就是十六进制字符串与字节数组互转、校验和计算、Base64编码。这三道题看起来各自独立,实际上背后都指向同一个能力——对数据在内存和文本之间的转换有清晰的理解。我自己的备考节奏是:先用三天把这些题反复做到条件反射,再去刷二叉树和图论,因为前者是网安专业的“底色”,后者是所有计算机考试的公共基础。
2.1 十六进制字符串转字节数组:先搞清楚输入边界
这道题的常见描述是:“输入一个字符串,里面是十六进制表示的字节数据,例如 '1A2B3F',将其转换为字节数组输出。”很多人上来就写一个for循环,每两个字符一组,用strtol或者手动判断字符范围来转换。但题目真正的坑不在这里,而在以下几点:
第一,字符串长度可能是奇数。题目如果不说明,你得自己判断并做处理。第二,字符串里可能偶然出现空格,比如粘贴报文时换行符没去掉。第三,字符大小写不固定,1A和1a是同一个字节,但如果你直接拿字符减'A'去算数值,不把大小写都处理掉就等着出错吧。第四,也是最容易被忽略的——目标缓冲区是unsigned char数组,处理时要时刻记得它是0到255的数值,而不是字符。
我第一次做这个转换题的时候就踩了第二个坑。题目给的样例报文是带换行的多行文本,我图省事直接scanf("%s")读取,结果只读到了第一行。后来改用fgets循环读取,手动过滤空白字符才算稳定通过。从那以后我总结了一条经验:遇到十六进制字符串转换,第一步永远是“清洗输入”,把除十六进制字符以外的所有字符全部过滤掉,再判断长度奇偶,最后再做转换。
转换的核心逻辑其实很直白。每两个字符对应一个字节,第一个字符作为高位,第二个字符作为低位。字符转数字时,如果是数字字符0到9直接减'0';如果是字母,统一转成大写再减'A'加10。我在代码里会专门写一个hex_val函数,避免在主逻辑里重复判断。实际写出来就是:
#include <stdio.h> #include <string.h> #include <ctype.h> int hex_val(char c) { if (c >= '0' && c <= '9') return c - '0'; c = toupper(c); if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } size_t hex_string_to_bytes(const char *input, unsigned char *out, size_t out_size) { size_t len = strlen(input); size_t olen = 0; int high = -1; for (size_t i = 0; i < len && olen < out_size; i++) { int v = hex_val(input[i]); if (v < 0) continue; // 跳过空格、换行等垃圾字符 if (high < 0) { high = v; } else { out[olen++] = (unsigned char)((high << 4) | v); high = -1; } } return olen; // 返回实际转换后的字节数 }注意输出缓冲区的长度参数,复试评测机不会留情面的:如果缓冲区只有10字节,你硬塞进11个字节,它不报错,但下一个变量就被你踩坏了。所以代码里加了out_size边界检查,这是拿分的关键细节。实战中我建议把out_size默认传成数组长度的两倍,因为两个十六进制字符最多生成一个字节,提前算好能省掉许多麻烦。
2.2 IP首部校验和:从“按规矩办事”到真正理解补码
IP校验和这类题是网安复试里很有“专业感”的一道题。题面通常会给你一段IP报文头部,要求计算校验和。算法本身是固定的:把所有16位字按二进制求和,进位回卷(把高16位的进位加回低16位),最后取反得到校验和。但很多人第一次写这题时都会卡在“为什么进位要回卷”上。
这个知识点要从校验和的设计初衷说起。IP协议没有复杂的安全机制,校验和的目标只是快速检测数据在传输过程中有没有被改坏。如果只是把所有16位字简单相加,一旦低位进位溢出了,这部分信息就丢了。校验和算法为了让溢出也能被纳入校验范围,就把加法产生的进位回卷叠加到低位,这样所有位的权重都被考虑到了。换句话说,不是“规则规定必须回卷”,而是“回卷之后校验才有效”。理解这个原理后,你写代码时就不用死记硬背了,哪怕这次记不住流程,顺着“检测所有位”的意图也能推回来。
计算步骤拆开是这样的:先把数据按每16位切分,也就是每两个字节拼成一个16位整数;然后全部累加,得到一个最多32位的和;把高16位不断右移16位回卷,直到高16位为0;最后把结果取反,得到校验和。
但是这个流程里还有两个隐蔽点。第一个是如果报文总长度是奇数,最后一个落单的字节需要当作高八位、低八位补0来参与计算;第二个是校验和字段原本在报文里是0,计算完之后再把值写回去,考试时题目会明确说“当成0计算”。不少同学没注意第一个点,奇数长度报文算出来的校验和永远是错的。
C语言实现时,为了处理补0问题,我习惯先把数据读到内存后再逐字节构造16位整数。代码可以参考这样写:
#include <stdio.h> #include <stdint.h> #include <string.h> uint16_t checksum_ip(const unsigned char *data, size_t len) { uint32_t sum = 0; size_t i = 0; // 每两个字节拼一个16位数 while (i + 1 < len) { sum += (uint16_t)((data[i] << 8) | data[i + 1]); i += 2; } // 如果还剩一个字节,作为高8位参与计算 if (i < len) { sum += (uint16_t)(data[i] << 8); } // 进位回卷 while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } return (uint16_t)(~sum); }这段代码里,data[i]是unsigned char类型,左移操作自动整型提升,不会有符号扩展带来的诡异结果。如果你偷懒声明成char,遇上最高位是1的字节(比如0x80)左移就出大问题。这个细节我在后面第4节还会专门展开。
2.3 Base64编码:三个字节一组到底怎么切
Base64与其说是编码题,倒不如说是一道“位操作手熟题”。网安的很多工具输出、恶意软件样本分享、甚至部分认证协议都会用到Base64,所以复试编程考它的概率不低。算法不难:把每3个字节共24位拆成4组,每组6位,分别映射到64个可打印字符。
但这里有一个“新手觉得理所当然、老手也在犯错”的细节:3的余数处理。原始数据长度不一定是3的倍数。剩下1个字节时,补两个=作为填充,最终产生4个Base64字符;剩下2个字节时,补一个=。正确切分的时候,最后一个Base64字符的有效位可能只有2位或4位,高位后面是补0的。
我给你举个例子。字符串“A”的ASCII是0x41,二进制是01000001。按3字节一组切的时候只有1个字节,所以前6位是010000(补0到6位),后4位是01,再补4个0变成000100,也就是字符索引16和4,分别对应“QQ”,最后补两个=,得到“QQ==”。如果你对位操作不熟,很容易把补位逻辑写成“直接右移6位”,导致结果错得离谱。
我在现场做过这个题,当时用了一个比较笨但绝对稳妥的方法:先把整个输入读入缓冲区,然后按每3个字节一个循环处理,每轮循环内部用位掩码提取6位。这样避开复杂的静态表映射逻辑,纯粹用移位运算,减少出错率。核心片段如下:
static const char b64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; void base64_encode(const unsigned char *in, size_t in_len, char *out) { size_t i = 0, o = 0; while (i + 3 <= in_len) { uint32_t v = (in[i] << 16) | (in[i + 1] << 8) | in[i + 2]; out[o++] = b64_table[(v >> 18) & 0x3F]; out[o++] = b64_table[(v >> 12) & 0x3F]; out[o++] = b64_table[(v >> 6) & 0x3F]; out[o++] = b64_table[v & 0x3F]; i += 3; } size_t remain = in_len - i; if (remain == 1) { uint32_t v = in[i] << 16; out[o++] = b64_table[(v >> 18) & 0x3F]; out[o++] = b64_table[(v >> 12) & 0x3F]; out[o++] = '='; out[o++] = '='; } else if (remain == 2) { uint32_t v = (in[i] << 16) | (in[i + 1] << 8); out[o++] = b64_table[(v >> 18) & 0x3F]; out[o++] = b64_table[(v >> 12) & 0x3F]; out[o++] = b64_table[(v >> 6) & 0x3F]; out[o++] = '='; } out[o] = '\0'; }注意out容量至少是(in_len + 2) / 3 * 4 + 1,这个计算很多人考试时现场推导会懵,我建议提前背下来。换算逻辑是:3字节变4字符,如果不足3字节也按一组算,所以先(长度+2)/3向上取整得到组数,再乘4就是编码后字符数,加1放字符串结尾符。每次分配缓冲前先算好,宁可多分一两个字节,不要赌评测机的边界。
3. 手撕代码现场:三道题的实现与细节打磨
光讲原理不够,我把Day19当天的练习内容完整复盘一遍。这三道题分别是我说的十六进制报文解析、IP校验和、HTTP请求行解析。第三道题看起来像“格式字符串解析”,其实本质也是协议处理,很适合放在一起练习。每一道题我都贴出当时写出的版本,附上讲解和踩坑记录。
3.1 题目一:十六进制报文解析
题目描述是:用户输入一行字符串,代表一个网络报文的数据部分,格式为十六进制,可能存在换行符和空格,请解析成字节数组并输出十进制数值,每字节用空格分隔。输入长度不超过4096。
我的写法很直白:先读入整行,清洗字符,然后转字节。核心代码就是这个函数:
#include <stdio.h> #include <string.h> #include <ctype.h> int hex_val(char c) { if (c >= '0' && c <= '9') return c - '0'; c = toupper(c); if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } int main() { char line[5000]; unsigned char bytes[2048]; int byte_len = 0; int high = -1; fgets(line, sizeof(line), stdin); size_t len = strlen(line); if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0'; for (size_t i = 0; line[i] != '\0'; i++) { int v = hex_val(line[i]); if (v < 0) continue; if (high < 0) { high = v; } else { bytes[byte_len++] = (unsigned char)((high << 4) | v); high = -1; } } if (high >= 0) { // 说明有奇数个有效字符,题目要求这种情况下最后一位当作高4位处理 bytes[byte_len++] = (unsigned char)(high << 4); } for (int i = 0; i < byte_len; i++) { if (i) printf(" "); printf("%u", bytes[i]); } printf("\n"); return 0; }有一件事我必须提醒你:虽然上面代码里我在清洗时丢弃了非法字符,但正规的在线评测不会给你乱来的输入。如果题目严格约定“输入只含十六进制字符”,那你是否过滤空格就不影响结果。但万一题目没有明说,测试用例里却悄悄放了一个换行或Tab,你若不处理就直接转换,得分就是零。所以宁可多写几行过滤代码,换取稳定性。另外,字符数组长度我用的是5000,意思是可以容纳读入的整行;转换后的字节数组长度2048,已经能覆盖4096个十六进制字符解析出的2048字节。这种容量上的“故意富余”不是浪费,而是给边界情况留的保险。
3.2 题目二:IP校验和计算
这道题我当时做的时候,题目给的报文是长度40字节的IPv6头部构造样例,要求输出校验和。不过我们这里不纠结具体是IPv4还是IPv6的头部,重点是算法通用性。我写了一个通用的校验函数,然后用样例数据验证:
#include <stdio.h> #include <stdint.h> #include <string.h> uint16_t checksum_ip(const unsigned char *data, size_t len) { uint32_t sum = 0; size_t i = 0; while (i + 1 < len) { sum += (uint16_t)((data[i] << 8) | data[i + 1]); i += 2; } if (i < len) { sum += (uint16_t)(data[i] << 8); } while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } return (uint16_t)(~sum); } int main() { // 示例:1A2B3C4D... 解析后作为报文内容 const unsigned char data[] = {0x1A, 0x2B, 0x3C, 0x4D, 0x00, 0x01}; size_t len = sizeof(data); uint16_t ck = checksum_ip(data, len); printf("checksum: 0x%04X\n", ck); return 0; }这个实现看上去简单,但有几个容易被忽视的点。第一,所有参与运算的数据必须是无符号字节类型,因为补码计算和符号位息息相关。第二,循环回卷用while (sum >> 16)而不是if,是因为累加多个16位字之后,sum的高16位本身可能又会产生进位,需要反复回卷到高位为0为止。第三,最后取反使用~而不是(uint16_t)-sum,虽然结果相同,但从语义上“取反码”更清晰,避免别人阅读时产生误解。
如果你在某个平台的评测记录里发现“答案错误”,优先怀疑的应该是输入数据的读取方式。很多题目给出的数据是十六进制字符串,你要先把字符串转成字节数组,再调校验计算函数。如果你直接把字符'1'当作字节0x01去参与计算,结果就完全对不上。这也是我为什么总是说“协议解析题是连环套”:第一步转换错了,后面全是白算。
3.3 题目三:HTTP请求行解析
HTTP请求行格式是方法 空格 路径 空格 协议版本,例如GET /index.html HTTP/1.1。题目要求从输入的字符串中提取这三个字段,分别输出到三行。乍看简单,但复试上机喜欢在这里挖坑:方法可能有大写,路径可能带空格(虽然标准不太允许,但测试用例偏要这么干),也可能用户输入首尾有空格。
我写了一个不依赖sscanf自动化匹配的方法,因为格式不固定时,sscanf会把空格切分得很僵硬。我选择手动扫描,方法如下:先跳过开头所有空格;然后一直读非空格字符作为方法;再跳过空格;再读路径直到下一个空格;最后剩余部分作为协议版本。同时把尾部换行和回车清理掉。
#include <stdio.h> #include <string.h> #include <ctype.h> void parse_request_line(const char *line) { const char *p = line; char method[16], path[256], version[16]; size_t i; // 跳过行首空格 while (*p == ' ' || *p == '\t') p++; i = 0; while (*p && *p != ' ' && *p != '\t' && i < sizeof(method) - 1) { method[i++] = *p++; } method[i] = '\0'; while (*p == ' ' || *p == '\t') p++; i = 0; while (*p && *p != ' ' && *p != '\t' && i < sizeof(path) - 1) { path[i++] = *p++; } path[i] = '\0'; while (*p == ' ' || *p == '\t') p++; i = 0; while (*p && *p != '\r' && *p != '\n' && i < sizeof(version) - 1) { version[i++] = *p++; } version[i] = '\0'; printf("method: %s\n", method); printf("path: %s\n", path); printf("version: %s\n", version); } int main() { char line[512]; while (fgets(line, sizeof(line), stdin)) { if (line[0] == '\n' || line[0] == '\r') continue; parse_request_line(line); break; } return 0; }这段代码有两个细节值得说。第一个是sizeof(method) - 1这种边界保护,目的是防止路径过长把数组写爆。复试评测不会因为你用缓冲溢出而扣分,但知道“这里需要保护”本身就是工程素养的体现。第二个是我用fgets加上手动清空回车换行的方式,而不是scanf("%s"),这样即使请求行带空格也能正确处理路径中的空格。实际上很多“标准做法”在这里就会崩溃,而手动扫描可以从源头避开问题。
4. 踩坑实录:Day19之前我犯过哪些低级错误
把这三道题做完,我在本子上记了好几个低级错误。有些是自己犯的,有些是看别人代码时发现的。这些坑非常典型,网安复试上机的评测数据就喜欢在这些地方给考生“上强度”。我整理了四条,每一条都是我亲眼见过或者踩过的。
4.1 字符和整数的暧昧关系:char到底有没有符号
这是C语言里最经典的坑,没有之一。很多同学写代码时喜欢把字节数据放在char数组里,然后直接左移、右移、比较大小。但如果平台默认char是signed char,那么一个字节的最高位是1时,这个值就是负数。举例来说,字节0x80在char里是-128,不是128。你拿它去左移8位,得到的根本不是0x8000,而是一堆多余的符号位。
我在做十六进制转字节时,第一次就是因为用了char类型,解析FF这个字节时,结果变成了0xFFFFFF再被截断,导致校验和全部错误。后来我统一改成unsigned char,问题立刻消失。这不算什么高级技巧,但很重要:所有“当作字节”的数据,一律用unsigned char,不要为了一时方便省掉修饰符。
4.2 feof函数的坑:用错了会让最后一个包重复
有一次练习读取文件里的多行十六进制报文,我写了类似while (!feof(fp)) { fgets(...); process(...); }的代码。本地测试时看着没问题,但后来发现每次处理都会把最后一行重复一遍。原因很简单,feof是在你尝试读过文件末尾之后才会置位,不是提前预知“下一个不会再有了”。所以读完最后一行后,循环条件判断时feof还是假,于是又进入循环,fgets这时读不到数据,却把上一次残留的缓冲区内容又处理了一次。
正确写法是用fgets的返回值:只要它还能返回非NULL,就说明有数据;直到它返回NULL,结束循环。改成while (fgets(buf, sizeof(buf), fp))之后,这个重复问题就再没出现过。复试如果考文件读取,这种小细节就是隐性扣分点。
4.3 缓冲区大小和字符串结尾的'\0':宁可多分一字节
我见过太多同学在分配输出缓冲区时,算出(len + 2) / 3 * 4就结束,结果还没写完字符串结尾符,缓冲区就满了。如果是在评测机上也就算了,但如果评测机对越界比较严格,这可能会引发运行时错误,直接判0分。我个人的习惯是:算出的结果再加一,专门留给'\0'。哪怕题目要求输出就是“不带结尾的字符”,你也先加上,后续截断无妨,但漏掉就可能引发连锁错误。
类似问题还有字符串末尾多出来的\r。Windows环境下的文本文件换行是\r\n,Linux下是\n。如果你在本地Windows写代码,测试时fgets读到的最后一行末尾带个\r,你解析协议版本时就会莫名其妙匹配失败。解决方式很干脆:处理每行前,手动把末尾的\r和\n都清掉。这也是我上面HTTP请求行解析代码里,循环条件加上*p != '\r'的原因。
4.4 读入长数据时,scanf的%s有致命问题
复试上机里的输入读取,我最不建议用scanf("%s", buf),因为它在遇到空格时就停止,无法读取完整的一行报文。尤其是在十六进制转换和HTTP解析场景,字符串中间通常会有空格,用%s等于自断一臂。更稳妥的是fgets配合手动去除换行,或者用getchar循环读取。
在一次模拟练习中,我尝试用scanf("%[^\n]")读一整行,虽然能处理空格,但这个格式说明符在遇到输入流末尾时行为容易让人迷惑。如果评测平台不按常理出牌,这种代码出错时很难排查。所以在复试准备阶段,我建议大家统一把所有“读取一行”的操作固化成:fgets(buf, sizeof(buf), stdin);然后再处理。这个模式虽然多两行代码,但是最大程度规避了输入格式的物理陷阱。
5. 接下来几天怎么安排:从会写到稳写
Day19之后,我给自己重新排了一张计划表。核心思路是:不再追求新题的数量,而是把已做过的每种题型,按“限时30分钟+极端测试用例+无IDE环境”的标准重新过一遍。这个阶段的目标不是“这道题我做过”,而是“这道题我能在考场上稳定拿满分”。
5.1 给代码“挑刺”:边界和异常输入才是拉分点
复现做完一道题后,我会花5分钟故意设计刁钻输入。比如把十六进制字符串改成奇数长度,或者在每个字符之间插入空格,把Base64的输入长度改为1、2、3、4、5字节,把HTTP请求行改成路径为空的情况。每一个输入都先凭直觉推演期望输出,再运行代码对比。这个过程能快速暴露思维盲区,比闷头多刷十道题有价值得多。
我举一个具体的例子。Base64编码,输入长度为0时,输出应该是什么?很多人可能觉得“没有输入就没有输出”,于是代码里不写空输入分支。但实际标准Base64对空输入产生的是空字符串。评测如果恰好给了空文件,代码可能直接崩溃。这种极端用例,只有在“故意找茬”时才会想到。复试上机拿高分的同学,通常不是算法最强的人,而是这种边界情况想得最周全的人。
5.2 模拟考试环境:限时手写+无IDE+测试驱动
我从第15天开始,用某高校网安复试的模拟系统练习。它只有一个简单的网页编辑器,自动保存编译按钮,几乎没有智能提示。第一次用的时候非常不适应,连头文件顺序都要想一会儿。但正是这种不舒适感,逼着我把常用代码结构背下来。到第19天时,我已经能做到不看参考写出十六进制转换和校验和的完整代码,而且误差时间控制在两分钟以内。
具体做法分享给你:每道题先自己限时写一遍,然后不急着提交,先在本地用自己设计的测试用例验证,最后再把代码粘贴到模拟系统里跑。如果提交失败,先看是不是输入输出格式问题,再看是不是边界数据处理问题,最后才考虑算法正确性。这个排查顺序能节省大量时间,因为网安复试的评测系统往往只报“答案错误”,不告诉你错在哪个样例。
至于接下来的几天,我的安排是:Day20做文件操作与进程通信的基础题,Day21做二叉树遍历的递归和迭代实现,Day22做综合模拟套题。每三天一个大专题,每个专题结束都要回头把前一天的代码重新写一遍。这种滚动复习法很笨,但确实有效——因为复试编程最容易出现的问题是“做过但忘了细节”,而不是“没见过题”。
最后再分享一点个人体会
Day19这一天,最大的收获不是三道题的解法,而是我终于建立了对“干净代码”的直觉。以前写代码,只要本地跑过一遍就觉得自己会了;现在我会主动追问:如果输入里有空格、如果长度是奇数、如果缓冲区少一位、如果换行符是\r\n,我的代码还能扛住吗?网安复试编程的分数差距,其实就藏在这些追问里。
准备复试的过程挺枯燥的,尤其是每天对着同样的编译器、同样的题目类型,很容易产生“我刷够了”的错觉。但当你真正坐在考场上,面对一份陌生格式的报文解析题时,你会感激现在这种“偏执狂”式的自查。我个人练下来的感受是:与其焦虑“我还有多少题没做”,不如踏实问自己“这道题我能现场写出几行不慌的代码”。后者才是复试编程真正考察的东西。