关于char型与unsignedchar型数据使⽤格式化输出注意点
  在使⽤ 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:FFFFFF
E2: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型。printf输出格式char