在使用 socket(AF_INET, SOCK_DGRAM, 0); 打开一个套接字流,通过 ioctl(s, SIOCGIFHWADDR, &ifr) 获取网卡的mac地址的时候,需要将 struct ifreq ifr; 结构体中 ifr.ifr_hwaddr.sa_data 数组中的信息按照十六进制提取到字符串中去,程序中使用到了
sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", ifreq.ifr_hwaddr.sa_data[0], ifreq.ifr_hwaddr.sa_data[1], ifreq.ifr_hwaddr.sa_data[2], ifreq.ifr_hwaddr.sa_data[3], ifreq.ifr_hwaddr.sa_data[4], ifreq.ifr_hwaddr.sa_data[5]);
提取到mac中,但是原本Mac地址为 48:4d:7e:b1:e2:7e 存在结果字符输出 48:4D:7E:FFFFFFB1:FFFFFFE2:7E 。这明显不正常,如果使用如下的方式获取则没有问题
unsigned char mac[6];
memcpy(mac, ifr.ifr_hwaddr.sa_data, sizeof(mac)); sprintf(buffer, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
当时考虑到是否是因为不同Linux内核中的结构体 struct ifreq ifr; 中存在差异才导致问题,后来查找到.ifr_hwaddr.sa_data原型为char sa_data[14]。经过相关搜索,问题的原因如下:
有符号char型的数值范围是-128-127因此大于0x7F的数在char型下其符号位则为1,因此当以%X格式化输出的时候,会把这个类型的值拓展,通常拓展到int型的32位,那么会对char类型的最高位进行拓展,%x期望的数据类型应该是unsigned int型,因此char转换到unsigned int也会拓展符号位,也就是强制类型转换了。
当为char类型的时候,如果最高位是1,意思是超过了0x7F的数则会被拓展为32位的FFFFFFB1。所以会出现这种情况。但是第二种方法则是直接指定从指针指向的值提取,因此不会出现这种带有符号位的情况。即,使用%x格式化输出的时候,一般char数据会被拓展到int型大小,一般为32位。
可以通过(unsigned char)ifreq.ifr_hwaddr.sa_data[5]来解决问题。使用%x输出格式的时候需要将所需要的数据转换为无符号类型,因为%x期望对应的参数应该为unsigned int型。