服务端接收到客户端的请求,有时候需要对低于控制版本的客户端返回不同的结果。版本号一般是三段式的 "xx.xx.xx",通常会将版本号解析成整数数字保存在 int arr[3] 数组中,然后逐个检查数组中的数字;最常规的做法如下:
int compare_version(int client_version[3], int base_version[3])
{
if (client_version[0] > base_version[0]) {
return 1;
}
else if (client_version[0] == base_version[0]) {
if (client_version[1] > base_version[1]) {
return 1;
}
else if (client_version[1] == client_version[1]) {
if (client_version[2] > base_version[2]) {
return 1;
}
else if (client_version[2] == client_version[2]) {
return 0;
}
else {
return -1;
}
}
else {
return -1;
}
}
else {
return -1;
}
}
上面的代码能够正常运行,且没有冗余的步骤;但是比较步骤较多,if 语句层层嵌套,代码结构较为复杂。
本着“更短、更快、更强”的原则,开始思考能不能将代码优化一下。
正常情况下,我们比较字符串“123”与“124”,都是从百位、十位、个位依次进行比价,就像上面的函数所做的那样。而对于 int a = 123, int b = 124, 我们可以直接用 a > b、 a == b、a < b 来进行比较;所以,如果能够把版本号的三个部分合并成一个更大的变量来进行比较,那么一步就可以比较出版本大小了。如何把三个 int 变量合并成一个更大的整形变量呢?
一个 int 变量占用4个字节,32个bit;如果能够有一个 int96 的基本类型,那么可以这样做:
int96 a,b;
a = *(int96*)client_version; //假设系统是 big-edian
b = *(int96*)base_version;
if (a > b) {
return 1;
}
else if (a == b) {
return 0;
}
else {
return -1;
}
但是C++中没有占用96个bit的整数类型。
那么能不能将三个int放在一个int中呢?
考虑到市场上可以看到的各种软件的版本号,对于主版本号.次版本号.编译版本号,没见过哪个版本号是超过100的;那么,用10个bit(最大值1023)来表示一个版本号是足够的;所以,三段版本号可以分别用10个bit来表示,然后压缩进一个32bit的int中。对于有符号的10个bit,其能表示的数字范围为 -512 ~ 511,足够我们使用的了。
如下两个方法均可:
方法一
int compare_version(int client_version[3], int base_version[3])
{
int client = (client_version[0] << 20) | (client_version[1] << 10) | clien_version[2];
int base = (base_version[0] << 20) | (base_version[1] << 10) | base_version[2];
if (client > base) {
return 1;
}
else if (client == base) {
return 0;
}
else {
return -1;
}
}
方法二
int compare_version(int client_version[3], int base_version[3])
{
int diff = 0;
diff += (client_version[0] - base_version[0]) << 20;
diff += (client_version[1] - base_version[1]) << 10;
diff += (client_version[2] - base_version[2]);
if (diff > 0) {
return 1;
}
else if (diff == 0) {
return 0;
}
else {
return -1;
}
}