学习Linux网络编程是遇到的大小端字节序的一些问题,以及对printf的一点理解

网络字节序

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

//h表示host,n表示network,l表示32位长整数,s表示16位短整数。

//如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

下面是遇到的问题:
使用htonl做一个大端转换的程序,经测试本电脑为小端字节序。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <arpa/inet.h>
void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

int main(int argc, char *argv[])
{
	
	//注意:网络里用的都是无符号类型的数据,我们要把他们转化从无符号数
	char buf[4] = {192,168,137,1};
	unsigned int num = *(unsigned int *)buf;//将buf转为int*,然后对他进行取值,此时取的值就是从buf起始地址+4个字节内的内容
	printf("%u\n",num);
	unsigned int sum = htonl(num);//将num这个32位数(4个字节)进行大端转换
	printf("%u\n",sum);
	unsigned char* p = (unsigned char*)&sum;//再将sum的地址转换位char*(地址操作距离为一个字节),并赋值给p指针
	
	printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));
	return 0;
}

在这里插入图片描述
过程:当{192,168,137,1}被赋到num中后,按理说,num也有一个4字节的地址,从起始地址分别放着192 ,168,137,1,二进制就是:11000000,10101000,10001001,00000001。他们组成整数应该是3232270593。但程序中读取的num值为25798848。

我们把25798840转成二进制看:

00000001 10001001 10101000 11000000

居然是反过来了。为什么呢?

原因在于,当我们创建buf数组,并初始化为{192,168,137,1}时,数组的首地址(低地址)处必须放的是192(11000000) ,最后(高地址)放的是1(00000001),而不是说因为本电脑是小端字节序,就让1放在低地址,192放在高地址。(原因在于,这是数组而不是一个数,在这个数组中,其实192对应的就是低位,1对应的就是高位),所以最终存放顺序也是符合小端存储的。

当将char数组的类型转化为int*并赋值给num后,num中的内存也和数组是一样的如下图。
在这里插入图片描述
然后,我们打印num时,printf是按照从左往右,从高到低的顺序,(即低字节的数据放在低位,高字节的数据放在高位)打印数据的,即最终打印的数据为:

00000001 10001001 10101000 11000000

所以,最后printf出来的num的值为25798840。

然后htonl函数发现此电脑是小端字节序,故将num进行大端转换,即将num中的数据转换排序方法。然后放到sum中,即最高地址放的是11000000,最低地址放00000001

如下图sum的内存空间:
在这里插入图片描述
然后,我们打印sum时,printf是按照从左往右,从高到低的顺序,即低字节的数据放在低位,高字节的数据放在高位)打印数据的读取数据的,即最终打印的数据为:

11000000,10101000,10001001,00000001

所以,最后printf出来的sum的值为3232270593。

最后,对sum地址转换为char*后,从首地址开始取值,依次为1 ,137,168,192

对printf的一点理解

int64_t a = 1034024421633;
	int h = 3;
	printf("%u,%d\n", a, h);
	printf("%u,%lld\n",a,h);

a的二进制位11110000 11000000 10101000 10001001 00000001

  • 首先,printf打印数据时,是根据第一个参数中的%后面的类型来偏移指针的,如果%后面为d或者u则偏移4个字节,h先被压入栈中,a后被压入。
  • 所以第一个取的值是a,a的值是看起来虽然是5个字节,但其实他是64位int型在栈中共占据了8个字节,前面还有24个0。而%u只能偏移4个字节,故a打印出来是 11000000 10101000 10001001 00000001即3232270593
  • 取第二个值时,指针会从前面偏移的地址开始(由于第二个%后面是d)偏移4个字节,则这4个字节中的内容为00000000 00000000 00000000 11110000。即240
  • 最终,到第一个printf结束,其实也没打印到h的值。因为指针还没有偏移到h的栈空间处。
  • 而第二个printf中,第二个%后面的内容是lld,表示地址偏移了8个字节,这8个字节的内容为
  • 000000000 00000000 00000000 00000011 00000000 00000000 00000000 11110000
  • 即:12884902128
  • 所以最终程序输出结果为
  • 3232270593,240
    3232270593,12884902128
  • 在这里插入图片描述
  • 所以如果要打印出我们原本设的值,那printf的中第一个%后面填llu,第二个%后面填d即可。
printf("%llu,%d\n",a,h);

在这里插入图片描述

printf取值会将先取到的值放在整串数值的低位,然后慢慢增加。
综上:对于printf输出变量值时,对于%后面的类型选取也是要非常小心的,因为一不小心可能就不是我们想要的值。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值