图 1:本文封面,第三字节的二进制对齐推导。
先说明属性:这是一篇历史笔试真题复盘,题目出自本地历年笔试题集,与任何公司的当前招聘无关。CIDR 路由聚合是网络笔试的稳定考点,四个选项里往往既有「看着对」的 10.1.0.0/16,也有「方向反了」的 10.1.10.0/24。这类题三分钟能做完,前提是你手里有一把「二进制对齐」的尺子。顺带一提,多可用区部署时的内网网段规划、安全组规则配置,靠的也是同一套聚合计算。
先给结论,再给推导。
正确答案是 10.1.8.0/23。推导只需要盯住第三字节,因为前两个字节 10.1 完全相同。8 的二进制是 00001000,9 是 00001001——前七位 0000100 一模一样,只有最后一位 0 与 1 不同。也就是说两个地址的公共前缀是 16 + 7 = 23 位,把第 24 位让出来作主机位,得到汇总网段 10.1.8.0/23。验算覆盖范围:/23 的网络位 23 位、主机位 9 位,每个网段含 512 个地址,10.1.8.0/23 正好覆盖 10.1.8.0 到 10.1.9.255,即原来的两个 /24,一个不多一个不少。
为什么不能随手写成 10.1.9.0/23?
这是本题的暗坑。两个 /24 能合并成 /23 的前提是低位网段的第三字节落在 /23 边界上,也就是必须是偶数。/23 的网段在第三字节上以 2 为粒度对齐:8.0/23 覆盖 8 和 9,10.0/23 覆盖 10 和 11。9 不是 /23 边界,「10.1.9.0/23」这个写法本身就是非法网段(主机位没清零),而且若硬按 9 起始算,覆盖的是 9 和 10,把别人的网段包进来了。所以聚合的第一条法则:先对齐再合并——能直接合成 /23 的两个 /24,第三字节必须是 2k 与 2k+1。
换一对数字,感受方向的变化。
若题目改成 10.1.9.0/24 和 10.1.10.0/24 呢?9 是 00001001,10 是 00001010,公共前缀只剩 6 位,总前缀 22 位,只能汇总成 10.1.8.0/22——而 /22 覆盖 8 到 11 四个 /24,多包进了两个本来不在题目里的网段。路由聚合里这叫「宁大勿错」:对不齐就只能放长到公共前缀对应的掩码,同时必须确认多覆盖的网段可以接受。笔试里如果选项只有 /22 而没有 /23,答案就得选它;但本题 /23 恰好对齐,选 /22 就是白扔精度。
用代码把位运算固化下来。
下面这段程序实现通用聚合:异或找不同位、算最长公共前缀、按掩码清零主机位,并对两组网段分别验算(Node.js 22.15.0,node cidr.js):
function toInt(ip) {
return ip.split('.').reduce((acc, o) => (acc << 8) + Number(o), 0) >>> 0;
}
function aggregate(ip1, ip2) {
const a = toInt(ip1), b = toInt(ip2);
const x = (a ^ b) >>> 0; // 不同的位
const diffBits = x === 0 ? 0 : 32 - Math.clz32(x);
const prefix = 32 - diffBits; // 最长公共前缀
const mask = prefix === 0 ? 0 : (0xFFFFFFFF << (32 - prefix)) >>> 0;
return [a & mask, prefix];
}
console.log('8 的二进制 :', (8).toString(2).padStart(8, '0'));
console.log('9 的二进制 :', (9).toString(2).padStart(8, '0'));
console.log('10 的二进制:', (10).toString(2).padStart(8, '0'));
console.log(aggregate('10.1.8.0', '10.1.9.0')); // 期望 /23
console.log(aggregate('10.1.9.0', '10.1.10.0')); // 只能 /22
实测输出:
8 的二进制 : 00001000
9 的二进制 : 00001001
10 的二进制: 00001010
[ 167802880, 23 ] // 即 10.1.8.0/23,第三字节范围 8~9
[ 167802880, 22 ] // 即 10.1.8.0/22,覆盖 8~11,多包进两个网段
167802880 换回点分十进制正是 10.1.8.0,与手工推导一致;9 与 10 的组合确实只能给到 /22。
图 2:位对齐法则与「9+10」的边界反例对照。
顺手排掉另外两个干扰项。
10.1.0.0/16 确实包得住两个网段,但它覆盖 256 个 /24,把没出现在题目里的 254 个网段也圈了进去,聚合精度全丢;10.0.0.0/8 同理,包得更狠。至于 10.1.10.0/24,它与任一原网段都不存在包含关系,属于凑数项。选择题里「包得住」从来不是标准,「恰好覆盖的最长前缀」才是。
聚合与划分是同一套功夫的两个方向。
子网划分是自顶向下:一个大网段切成若干小网段,主机位变多;路由聚合是自底向上:相邻小网段合成超网,前缀变短。考场上的统一动作都是「把变化的那个字节写成二进制,数公共前缀,检查边界对齐」。常见失分有三:忘了低位网段必须落在目标掩码边界上;把「能覆盖」当成「恰好覆盖」;以及把 10.1.0.0/16 这种「肯定包得住但精度全丢」的选项当答案。你在网段规划或安全组配置里算过聚合吗?有没有被边界对齐坑过?欢迎分享。