#include <stdio.h>
int main()
{
char a[6] = {'H', 'N', 'U', 'e', 'r', '\0'};
char b[6] = {72, 78, 85, 101, 114, 0};
int c[2]={(101<<24)+(85<<16)+(78<<8)+(72), 114};
short d[2]={(101<<24)+(85<<16)+(78<<8)+(72), 114};
float e[2]={(101<<24)+(85<<16)+(78<<8)+(72), 114};
printf("%d, %d, %d, %d\n", sizeof(char), sizeof(int), sizeof(short), sizeof(float));
printf("%s\n", a);
printf("%s\n", b);
printf("%s\n", c);
printf("%s\n", d);
printf("%s\n", e);
return 0;
}
这段代码实现了什么?仿照以上代码,应用GCC,定义短整型数组打印出结果,并比较分析。如果是定义为浮点数组,打印出什么样的结果?
为了解答这些问题,首先我们要明确的是,printf中%s究竟是如何运作的。事实上, %s的原理是从首地址开始,逐字节地将存储单元中的数据转化为ASCII字符输出,直到存储单元中存储的数据为'\0',即0x00。
对于字符数组a
printf("%s\n", a);中a代表字符数组的首地址。于是,printf从'H'开始打印,逐字节打印出'N', 'U', 'e', 'r',遇到'\0'时停止打印,此时,打印出的所有字符为"HNUer"。
对于字符数组b
其中存储的值为ASCII码的十进制表示,通过查表可以发现,{72, 78, 85, 101, 114, 0}对应的ASCII字符为{'H', 'N', 'U', 'e', 'r', '\0'},与数组a相同,打印的结果同样为"HNUer"。
对于整型数组c
先看第一个元素(101<<24)+(85<<16)+(78<<8)+(72)。由于其左移的位数都是8的倍数,我们把101,85,78,72转换为十六进制来观察,分别为0x65,0x55,0x4e,0x48。0x65的二进制数左移24位,相当于0x65左移6位,即0x65000000。同样的,对剩下几个数进行类似的移位,相加后得到数0x65554e48。对于第二个元素114,我们知道,int的大小为4个字节,将整型的114转换为十六进制,得到的数为0x00000072。
按照常规想法,内存中的这两个数的排列方式为65554e4800000072,通过%s逐字节地输出,理论上输出的应该是"eUNH",然而实际上,输出的却是"HNUer"!
这就牵扯到了CPU内存中数据的排列方式。在CPU中,数据的存储模式是小端模式,即数据的低字节存储于内存的低地址中。
![](https://img-blog.csdnimg.cn/20190309165031976.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hOVUNTRUVfTEpL,size_16,color_FFFFFF,t_70)
于是,当用%s输出整型数组c时,传给%s的地址为数组c的首地址0000,%s逐字节地输出0x48, 0x4e, 0x55, 0x65, 0x72所代表的ASCII字符(遇到0x00时停止),输出的即是"HNUer"。
对于短整型数组d
短整型short的大小为2个字节。由上文可知,(101<<24)+(85<<16)+(78<<8)+(72)的十六进制数是0x65554e48,而这个数长度已达4个字节,若用short来存储,则会发生溢出——得到0x4e48。而114同理得到0x0072。根据小端的原理,在内存中数组d的数据排列为484e7200, 输出得到"HNr"。
对于浮点型数组e
其在Windows下输出"潽蔔",而在Linux下输出"???N"。我也不知道为什么..................待填。
可以看出,%s这种逐字节、遇'\0'停止的输出方式,很容易造成内存泄露等问题,需要谨慎使用。