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()
来源:CSDN
作者:worthsen
链接:https://blog.csdn.net/qq_38880380/article/details/103708442