题目
- 详细题目
- 题目分析
- 由所给的一组IP地址得出这组IP地址的最小网络地址和子网掩码。
- 子网掩码
- 由1和0组成,1对应网络位,0对应主机位。
- 假设0的个数为m,则2^m为这个子网对应的网络个数。但可用的IP地址数为2^m-2。因为全1和全0有特殊用处,不能作为使用。
- 最小网络号
- 同一子网的一组网络号的最小网络号其后几位为0,0的个数和子网掩码0的个数相同。
思路
- 首先用ans[1005][4]存储这组IP地址
- 按列进行比较,找到第一个不同的列,记录列的索引。
- 对这列进行分析,由于存储的是十进制,所以应将其转化为二进制对每一位进行比较。从最低位开始比较,找出最后一个不同的位,记录其位置。
- 根据之前记录的最高位不同的位置,计算最小网络号和子网掩码。
- (最小网络号)这一位之前都和第一个IP地址的这列的二进制位相同,这一位及其之后的位都为0.并将其转化为十进制。
- (子网掩码)这一位之前都为1,这一位及其之后都为0.并将其转化为十进制。
- 根据记录的列的位置,输出最小网络号和子网掩码。
实现
#include<stdio.h>
const int co = 1 << 7 ;//128
int ans[1005][4]; //输入1-1000个
int main(){
int m, t, k;
while(scanf("%d",&m) != EOF){
int i,j;
for(i = 0; i < m; i++){
scanf("%d.%d.%d.%d",&ans[i][0],&ans[i][1],&ans[i][2],&ans[i][3]);
}
//找到从哪一列开始不同
for(i = 0; i < 4; i++){ //将IP地址分为4部分,每部分一列
t = ans[0][i]; //标记第一个IP地址的各个部分的值
int ok = 1;
k = i; //标记哪一个部分;
for(j = 1; j < m; j++){
if (ans[j][i] != t){ //按列比较IP地址的值
ok = 0;
break; //如果和第一个标记的不同,则退出
}
}
if(!ok) break; //如果找到一个不同的,则退出
}
int s = 1;
int p = 8; //将不一样的部分转化为8个二进制数,记录从第几个二进制开始不一样
for(i = 7; i >= 0; i--){
t = (ans[0][k] / s) & 1; //将ans[0][k]转化为2进制 ,从最低位开始(最右边)。
for ( j = 1; j < m; j++){
if(((ans[j][k] / s) & 1) != t) p=i; //p记录 从左到右数第几个不一样的索引下标
}
s = s * 2; //和上边的ans[0][k]/s 合作完成二进制串的遍历
}
int number = 0;//ip地址
int count = 0;//子网掩码地址
s = 1;
for(i = 0; i < p; i++){ //ans[0][k]*s相当于每次循环扩大二倍,其二进制末尾加0
if ((ans[0][k] * s) & co) //通过移动二进制的位数,比较每位二进制是一还是0 128=1000 0000
number += 1<<(7-i); //如果不为0,则将此位二进制转化为十进制
count += 1 << (7-i);
s = s * 2;
}
if (k == 3) {
printf("%d.%d.%d.%d\n",ans[0][0], ans[0][1], ans[0][2], number);
printf("255.255.255.%d\n", count);
}else if (k == 2)
{
printf("%d.%d.%d.0\n", ans[0][0], ans[0][1], number);
printf("255.255.%d.0\n", count);
}
else if (k == 1)
{
printf("%d.%d.0.0\n", ans[0][0], number);
printf("255.%d.0.0\n", count);
}
else
{
printf("%d.0.0.0\n", number);
printf("%d.0.0.0\n", count);
}
}
}
注意
- scanf("%d.%d.%d.%d",&ans[i][0],&ans[i][1],&ans[i][2],&ans[i][3]); 接收的格式
- s = 1; for循环中 t = (ans[0][k] / s) & 1; s = s * 2;
- & 按位进行与运算,都1才1,否则为0.
- t取得ans[0][k]的二进制的每一位。如ans[0][k] = 178 其二进制1011 0010
- i = 0时 1011 0010 & 0000 0001 = 0000 0000 --->0
- i = 1时 101 1001 & 000 0001 = 000 0001 --->1
- i = 2时 10 1100 & 00 0001 = 00 0000 --->0
- .......
- i = 6时 10 & 01 = 00 ---> 0
- i = 7时 1&1 = 1 ---> 1
- 从下到上,即10110010 类似手算十进制转换为二进制。
- x / 2 相当于x的二进制向右移一位 x /4 相当于x的二进制向右移两位 (比如上边ans[0][k] / s)
- x * 2相当于x的二进制向左移一位,右边补一个0.
- s=1 (ans[0][k] * s) & 128 s=s *2; 和2思想是一样的。
作用区别 t=(ans[0][k] / s) & 1
t被赋予的顺序是从低位到高位 t=(ans[0][k] * s) & 128(2^8) t被赋予的顺序是从高位到低位 -
例子ans[0][k]=178 t=(ans[0][k] / s) & 1时,t=0,t=1,t=0,t=0,t=1,t=1,t=0,t=1
二进制1011 0010 t=(ans[0][k] * s) & 128 时,t=1,t=0,t=1,t=1,t=0,t=0,t=1,t=0。两者正好相反
-
- if ((ans[0][k] * s) & 128) number += 1<<(7-i);
- 1为true,0为false. 如果循环8次,则number=ans[0][k];如果循环5次,则number=2^7+2^5+2^4=176