Unix/C/C++--数据类型转换、格式化、cpy、精度

1 简介

各类场景下,需要各种数据转化。

2 等号赋值与memcpy

  • strcpy等函数的逐字节拷贝,memcpy是按照机器字长逐字进行拷贝的,一个字等于4(32位机)或8(64位机)个字节。CPU存取一个字节和存取一个字一样,都是在一条指令、一个内存周期内完成的。显然,按字拷贝效率更高。
  • 原来两者都是通过逐字拷贝来实现的。但是“等号赋值”被编译器翻译成一连串的MOV指令,而memcpy则是一个循环。“等号赋值”比memcpy快,并不是快在拷贝方式上,而是快在程序流程上。测试发现,“等号赋值”的长度必须小于等于128,并且是机器字长的倍数,才会被编译成连续MOV形式,否则会被编译成调用memcpy。而同样的,如果memcpy复制的长度小于等于128且是机器字长的整数倍,会被编译成MOV形式。所以,无论你的代码中如何写,编译器都会做好优化工作。
  • 循环展开也是应该有个度的,并不是越展开越好(即使不考虑对空间的浪费)。因为CPU的快速执行很依赖于cache,如果cache不命中,CPU将浪费不少的时钟周期在等待内存上(内存的速度一般比CPU低一个数量级)。而小段循环结构就比较有利于cache命中,因为重复执行的一段代码很容易被硬件放在cache中,这就是代码局部性带来的好处。而过度的循环展开就打破了代码的局部性。如果要拷贝的字节更多,则全部展开成连续的MOV指令的做法未必会很高效。
  • 综上所述,“等号赋值”之所以比memcpy快,就是因为它省略了CPU对于判断与跳转的处理,消除了分支对CPU流水的影响。而这一切都是通过适度展开内存拷贝的循环来实现的。

3 各类转换

3.1 unsigned char 2 float

#include <iostream>
#include <string.h>

int main()
{
	unsigned char arr[5] = {0};
	arr[0] = 0x34;
	arr[1] = 0x32;
	arr[2] = 0x5B;
	arr[3] = 0x3D;
	arr[4] = 0xD1;
	float test;
	memcpy(&test, arr, 4);
	std::cout << test << std::endl;
}

3.2 unsigned char 2 string

#include <iostream>

std::string ToString(unsigned char *msg, int len)
{
	std::string str;
	for (int i = 0; i < len; i++)
	{
		str += msg[i];
	}
	return str;
}

int main()
{
	unsigned char msg[20] = {0};
	msg[0] = 0x66;
	msg[1] = 0x23;
	msg[2] = 0x7B;
	msg[3] = 0xFF;
	std::string str;
	str += ToString(msg, 4);
	std::cout << str << std::endl;
	unsigned char *data = (unsigned char *)str.c_str();
	std::cout << data << std::endl;
	std::cout << (int)data[3] << std::endl;
}

3.3 float 2 char

在这里插入图片描述

3.4 float 2 unsigned char

在这里插入图片描述

4 指针传递

4.1 数组指针

4.1.1 示例一

#include <stdio.h>

int get_name(char *sname)
{
	int ret;
	printf("%s\n", sname); 	
	return 0;
}
int main()
{
	char *name = "abc";
	printf("%s\n", name);   
	get_name(name);      
	return 0;
}

4.1.2 示例二

#include <iostream>
#include <stdio.h>

void func3(unsigned char *buf)
{
	for (int i = 0; i < sizeof(buf); i++)
	{
		printf("%x\n", *buf);
		buf++;
	}
}

void func2(unsigned char *buf)
{
	func3(buf);
}

void func1(unsigned char *buf)
{
	func2(buf);
}

int main()
{
	unsigned char test[20] = {0};
	for (int i = 0; i < 20; i++)
	{
		test[i] = 6;
	}
	func1(test);
}

在这里插入图片描述

5 格式化

5.1 格式化输出

#include <iostream>
#include <iomanip>   
using namespace std;
int main()
{
	double s=12.345;
	cout<<setiosflags(ios::fixed)<<setprecision(2);
	cout<<s<<endl;   //12.35
	
	float pi=3.14159;
	cout<<pi<<endl;   //3.14
	
	return 0;
}

5.2 设置小数点后面的位数

  • C 库函数 int sprintf(char *str, const char *format, …) 发送格式化输出到 str 所指向的字符串。
int sprintf(char *str, const char *format, ...)
#include <iostream>
#include <string.h>
#include <stdio.h>

int main()
{
	unsigned char arr[10] = {0};
	arr[0] = 0xDE;
	arr[1] = 0x05;
	arr[2] = 0xCB;
	arr[3] = 0xBE;
	float test;
	memcpy(&test, arr, 4);
	char buffer[5] = {0}; 
	sprintf(buffer, "%.6f", test);
    std::cout << test << std::endl;
}

6 精度

6.1 float

在这里插入图片描述

参考

1、C–数据类型
2、Unix/C/C+±–字符串 偏 string
3、c语言,memcpy内存拷贝和=直接赋值有什么区别
4、等号赋值与memcpy的效率问题
5、C / C++ 保留两位小数(setprecision(n)的一些用法总结)
6、float2char
7、C 库函数 - sprintf()

展开阅读全文
©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读