未解决:不同类型的数据用memcpy然后打印(printf)出问题

引子:

本来是搜memcpy在拷贝两个数组时两数组下标类型不同的问题,即,若一个数组是很长,其下标用long型,要将此数组的一小段拷贝到另一个数组中,这另一个数组的下标只需要用int型,不确定会不会出问题。

int i;

long j;

unsigned n = 2;

memcpy( &des_buf[i], &ori_buf[j], n*sizeof(des_buf[0]) );

测试代码:

short i = 2;

int j = 65537;

int n = 2;
float des_buf[10] = { 0 };
float ori_buf[100000] = {0};
ori_buf[j] = 0.1;
ori_buf[j+1] = 0.2;
memcpy(&des_buf[i], &ori_buf[j], n * sizeof(des_buf[0]));

des_buf的内存:

是没有问题的,哪怕n的类型是int而不是unsigned int。

问题:

然后搜到:

memcpy,复制内存,但是字节数和参数给定的不一样_百度知道 (baidu.com)

是这个帖子的问题。

看了二楼的答案,试了不行,还是打印一样的结果。

暂时我也还没找到问题在哪,只不过做了一些试验,得到一些结论,先记在这里了。

先将代码改成这样标准点,将value用二进制打印出来,看下其占几个字节,每个字节的值:


#include<string.h>
#include<stdio.h>
#include<stdlib.h>

void main()
{
	char* des = NULL;
	des = (char*)malloc(2);
	int value = 0x8547;

	char buf[128];
	_itoa(value, buf, 2);
	printf("二进制: %s\n", buf);

	memcpy(des, &value, 2*sizeof(char));

	char* test = NULL;
	for (test = des; test < des + 2; test++)
	{
		printf("0x%02X,", *test); // 打印十六进制的ASCII码 两位表示
	}

	free(des);
}

结果:

分析、测试过程:

二进制值,0x8547,确实是按照字节来转的,即85对应10000101,47对应01000111。

按说,vaule是32位的,即有四个字节,但是这里转化后只有两个字节,认为是最低的两个字节,拷贝时,也是拷贝低两个字节,且,默认数据是按照小端对齐存储,即两个字节在内存中是按照47 85来存的,所以拷贝时,先拷贝47这个字节,再拷贝85这个字节。

对应打印时,先打印47这个字节,再打印85这个字节,顺序是对上的,只是85这个字节打印得不对。

其次,我们来看内存,即拷贝情况:

1、

des申请内存后的值:

前两个字节,是有初值的。

执行memcpy语句后,des值:

可见,两个字节是拷贝成功了,顺序也是对的。

des地址再往前看一点:

前面都是fd 00之类。没有ff之类。

2、

将test指针指向des,看test:

和des一样。往前看一点:

也是一样的,都是fd 00之类。没有ff之类。

3、

打印第一次,即打印第一个字节:

打印第二个字节:

先看内存,此时地址变为:

即地址确实是加了一个字节,按说应该打印85。打印:

打印不对了!

4、做其他尝试

怀疑这个打印值有问题,因为正常0x85值是133,133用有符号的一个字节是表示不了的,即超出表示范围,或者说,int的四个字节,不应该用倒数第二个字节表示0x85,而是要向左再多一位?但是这里貌似每个十六进制字节就是对应二进制8位表示,比如如果value = 0x668547,相应拷贝3个char数据并打印:

可见,value的倒数第三个字节,只会保存字节数据0x66。且,这里打印也是对的。那么,是数值0x85不能正确打印了。将value值改为3587,再走一遍上面1 2 3流程:

des:

test:

打印:

明明是要打印一个字节的87,却打印出4个字节的0Xffffff87.

看des前的字节:

也不是ff这种啊。那么是打印时出错?

第一次打印test前test前的字节:

前面字节也没有ff这种。

test++:

打印:

35这一个字节正常打印。

所以还是打印问题,拷贝应该是没有问题的。

5、但是——直接打印0x87:

加一句打印:

printf("0x%02X,", 0x87);

这里,好像0x87是可以打印的!因为就是一个字节啊......

那么,是偏移地址不对???

看此时的监视窗口:

71即是0x47的十进制值,对应的ASCII码是G。所以打印47是没问题的。然而,此时test指针指向的字节的值,实际是-123,即133-256,因为0x85=133超过128,所以是负值?实际是-123?那么printf按照0x格式打印-123时,就导致打印错误?

直接:printf("0x%02X,", -123);

结果,确实是打印出:

将原程序打印改为十进制:printf("%d,", *test); 

结果:

其实就是打印3个字节,且顺序也是对的。所以——

6、最终结论:

是格式转换的问题!要将一个字节数据转换为0x即十六进制时,如果这个字节数据是有符号的,那么可能会出现负值,此时转十六进制会出问题,会打印出前面加3个FF字节,至于为什么是这样加,目前确实还没想明白。

7、后续探索

想到类似的会不会也有问题,比如unsigned short型,打印会不会超出范围呢。下面程序:


#include<string.h>
#include<stdio.h>
#include<stdlib.h>

int main()
{
	unsigned short* des = NULL;
	des = (unsigned short*)malloc(2);
	int value = 0x95669947;

	memcpy(des, &value, 2*sizeof(unsigned short));

	unsigned short* test = NULL;
	for (test = des; test < des + 2; test++)
	{
		printf("0x%02X,", *test); 

	}
	free(des);
	
	return 0;
}

将一个32位的int型,拷贝到两个unsigned short型内存,再依次打印unsigned short型数据,按说,0x9566和0x9947都超过了unsigned short的最大值32767:

 

打印出来应该也是负数,即前面加很多FF?然而——

竟然打印的是对的?超出unsigned short类型的最大值都打印出来了,是怎么回事呢?

将0x9566自行转换为有符号的16位,即short型,是38246-65536 = -27290

直接打印这个值:

printf("0x%X,", -27290);

就打印出前面带FF的32位值。

但是,为什么这种拷贝的就不能直接复现之前char型的错误呢?

而且,此程序,跑到free那句,就会报错。查了原因是memcpy那句导致的:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值