算法题——IP地址分类与子网掩码
从零教你识别有效IP地址和子网掩码并分类
一、题目背景
在计算机网络中,IP地址(IPv4)是用来标识设备的,就像每个房子都有自己的门牌号一样。
而子网掩码(Subnet Mask)是用来划分“街道”和“房子”的边界的。
这道题让我们给定很多行数据,每行都是:
IP地址~子网掩码
比如:
192.168.0.2~255.255.255.0
10.70.44.68~255.0.0.0
1.0.0.1~255.0.0.0
要求:
- 判断IP和掩码是否合法
- 根据IP地址的第一段,把它分成 A / B / C / D / E 类
- 统计私有IP的数量
- 统计非法地址的数量
二、网络基础小课堂
1. IPv4地址格式
IPv4长这样:
192.168.0.1
- 一共有4段数字
- 每段数字范围是 0 ~ 255
- 用
.
分隔 - 本质是 32位二进制,每段是8位
例:
192.168.0.1
= 11000000.10101000.00000000.00000001
2. IP地址的五大类
用第一段数字判断:
类别 | 第一段范围(十进制) | 用途 |
---|---|---|
A类 | 1 ~ 126 | 大型网络 |
B类 | 128 ~ 191 | 中型网络 |
C类 | 192 ~ 223 | 小型网络 |
D类 | 224 ~ 239 | 多播 |
E类 | 240 ~ 255 | 保留 |
注意:
127.x.x.x
是回环地址(测试用,不分类)0.x.x.x
是非法地址
3. 私有IP地址(内网)
这些是家里路由器常用的地址,不能直接访问互联网:
- A类私有:
10.0.0.0 ~ 10.255.255.255
- B类私有:
172.16.0.0 ~ 172.31.255.255
- C类私有:
192.168.0.0 ~ 192.168.255.255
4. 子网掩码
子网掩码也是4段数字,比如:
255.255.255.0
作用是告诉计算机IP地址的哪部分是网络号,哪部分是主机号。
合法掩码的二进制必须是:
- 前面全是1
- 后面全是0
- 不能中间夹杂“1010”这种乱序
例:
255.255.255.0
= 11111111.11111111.11111111.00000000 ✅合法255.0.255.0
= 11111111.00000000.11111111.00000000 ❌非法
三、解题步骤
步骤1:拆分IP和掩码
输入:
192.168.0.1~255.255.255.0
先用 ~
分开:
- 左边是IP地址
- 右边是掩码
步骤2:把每段数字提取出来
比如:
192.168.0.1 → [192, 168, 0, 1]
这样后面就能直接用数字判断范围了。
步骤3:判断掩码是否合法
规则:
- 不能全0:
0.0.0.0
- 不能全255:
255.255.255.255
- 二进制必须是前面全1,后面全0
做法:
- 把掩码转成一个32位整数
- 检查它的二进制是否符合
111...000...
模式
步骤4:判断IP是否合法
- 必须是 4 段数字
- 每段 0~255
- 第一段不能是0或127
步骤5:分类
用第一段数字判断是 A/B/C/D/E 类,并计数。
步骤6:判断是否是私有IP
- 第一段是 10 → A类私有
- 第一段是 172 且第二段 16~31 → B类私有
- 第一段是 192 且第二段是 168 → C类私有
四、完整代码(小白版+行级注释)
#include <iostream>
#include <vector>
#include <string>
using namespace std;// 字符串转数字数组,比如 "192.168.0.1" -> [192,168,0,1]
vector<int> strToIP(const string &s) {vector<int> res;string num;for (char c : s) {if (c == '.') {res.push_back(stoi(num)); // 转成整数并存到数组num.clear(); // 清空临时字符串} else {num.push_back(c); // 累加数字字符}}res.push_back(stoi(num)); // 最后一段数字也要加进去return res;
}// 判断掩码是否合法
bool isValidMask(vector<int> mask) {if (mask.size() != 4) return false;unsigned int m = 0;for (int n : mask) {if (n < 0 || n > 255) return false; // 范围检查m = (m << 8) | n; // 拼成32位整数}if (m == 0 || m == 0xFFFFFFFF) return false; // 全0或全1非法// 检查是否是111...000...模式unsigned int t = ~m + 1;return (t & (t - 1)) == 0;
}// 判断IP是否合法
bool isValidIP(vector<int> ip) {if (ip.size() != 4) return false;for (int n : ip) {if (n < 0 || n > 255) return false;}if (ip[0] == 0 || ip[0] == 127) return false;return true;
}int main() {string line;// 分类计数int A = 0, B = 0, C = 0, D = 0, E = 0;int error = 0, privateIP = 0;while (getline(cin, line)) { // 一行一行读size_t pos = line.find('~');if (pos == string::npos) continue;string ipStr = line.substr(0, pos);string maskStr = line.substr(pos + 1);vector<int> ip = strToIP(ipStr);vector<int> mask = strToIP(maskStr);// 判断合法性if (!isValidMask(mask) || !isValidIP(ip)) {error++;continue;}// 分类if (ip[0] >= 1 && ip[0] <= 126) A++;else if (ip[0] >= 128 && ip[0] <= 191) B++;else if (ip[0] >= 192 && ip[0] <= 223) C++;else if (ip[0] >= 224 && ip[0] <= 239) D++;else if (ip[0] >= 240 && ip[0] <= 255) E++;// 私有IP判断if (ip[0] == 10) privateIP++;else if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) privateIP++;else if (ip[0] == 192 && ip[1] == 168) privateIP++;}// 输出结果cout << A << " " << B << " " << C << " " << D << " " << E << " " << error << " " << privateIP << endl;return 0;
}
五、运行示例
输入:
10.70.44.68~255.0.0.0
1.0.0.1~255.0.0.0
192.168.0.2~255.255.255.0
127.0.0.1~255.0.0.0
0.201.56.50~255.255.255.0
输出:
2 0 1 0 0 2 2
解释:
- A类:
1.0.0.1
,10.70.44.68
- C类:
192.168.0.2
- 错误:
127.0.0.1
(回环),0.201.56.50
(首段为0) - 私有IP:
10.70.44.68
(A类私有),192.168.0.2
(C类私有)
六、总结
- 本题考察字符串处理+网络基础+逻辑判断
- 小白需要特别注意掩码的合法性判断
- 推荐用二进制思路检查掩码,因为更简单、准确