数据复制方法直接分配与c中的memcpy
Data copy method direct assign vs memcpy in c
我想从不同的变量中复制数据并将它们放在一个数组中,以便我可以进一步处理它。
我研究了直接赋值和 memcpy 方法,认为 memcpy 用于复制整个缓冲区而不是单个元素。此外,我认为对单个字节使用 memcpy 可能会花费时间并浪费 CPU 个周期。
你能从下面的例子中告诉我在这种情况下应该使用什么吗,因为它是 运行 在多线程环境中(不同的例子)并且变量可能会改变?
#include <stdio.h>
#include <stdint.h>
int main()
{
printf("Direct assign method\n");
uint8_t pack_id = 123;
uint8_t pack_age = 76;
uint8_t pack_cmd = 30;
uint8_t cus_data[3] = {0};
cus_data[0] = pack_id;
cus_data[1] = pack_age;
cus_data[2] = pack_cmd;
printf("Memcpy method\n");
pack_id = 112;
pack_age = 89;
pack_cmd = 25;
memcpy(&cus_data[0], &pack_id, sizeof(pack_id));
memcpy(&cus_data[1], &pack_age, sizeof(pack_age));
memcpy(&cus_data[2], &pack_cmd, sizeof(pack_cmd));
return 0;
}
现代计算机的地址大小大多为8字节,即64位。
您只是不将8字节的地址分配给1字节(8位)存储变量。
例如
memcpy(&cus_data[0], &pack_id, sizeof(uinit8_t*));
但是,对于一个序列,您执行了以下 3 次。
memcpy(&cus_data[0], &pack_id, sizeof(uinit8_t));
memcpy() 函数。是这样的
void memcpy(void *dest, void *src, size_t size) {
char *c_src = (char *)src;
char *c_dest = (char *)dest;
for (int i=0; i<size; i++)
c_dest[i] = c_src[i];
}
它将src 1byte 1byte复制到dest。等等..
所以,我喜欢使用 struct, case 1 而不是上面的
进一步的情况 2 代码复制一次指针。
struct PACK{
uint8_t id;
uint8_t age;
uint8_t cmd;
};
int main() {
struct PACK pack = { .id = 123, .age = 76, .cmd = 30 };
uint8_t cus_data[3];
// case 1
memcpy(cus_data, &pack, 3);
// case 2
uint8_t *p = (uint8_t*)&pack;
for(int i=0; i<3; i++)
printf("%d\n", p[i]);
return 0;
}
cus_data[0] = pack_id;
永远不会比 memcpy(&cus_data[0], &pack_id, sizeof(pack_id));
慢。然而,如果编译器内联 memcpy 调用,它们可能同样快,这是很有可能的。
您应该做的不是担心微优化,而是编写尽可能易读的代码。当您遇到实际性能问题时,担心优化。
因为 cus_data[0] = pack_id;
是最易读的,那是你应该做的。
in such case since it is running in multithreaded environment
没有区别。您需要保护变量免受重入错误的影响,或者您不需要保护它们。这与简单赋值与 memcpy 无关,因为它们都不能保证是原子的。
变量如何 small/large 并不重要,因为 C 语言中没有任何东西可以保证原子访问,除非您使用 C11 _Atomic
和类似的。
几乎每个优化编译器(当然是 gcc、clang 和 icc)都将 memcpy
(以及少数其他 libc 函数,例如 memcmp
)视为由真正的支持的内置函数同名的 libc 函数。
编译器通常只会调用真正的函数,前提是使用内联汇编完成工作看起来工作量太大。
复制角色当然不属于这一类。这两个生成 same assembly with gcc/clang/icc:
#include <string.h> //compilers generally know about memcpy
//but C still requires that it should be prototyped
void assign_char(char *X)
{
*X = 'x';
}
void memcpy_char(char *X)
{
memcpy(X,"x",1);
}
x86_64 输出:
assign_char:
mov BYTE PTR [rdi], 120
ret
memcpy_char:
mov BYTE PTR [rdi], 120
ret
使用哪一个都没有关系。 memcpy
版本更通用(也适用于数组,即使不能在 C 中为数组赋值 to/from)并允许您规避严格的别名,但它的编写也有点冗长。
我想从不同的变量中复制数据并将它们放在一个数组中,以便我可以进一步处理它。
我研究了直接赋值和 memcpy 方法,认为 memcpy 用于复制整个缓冲区而不是单个元素。此外,我认为对单个字节使用 memcpy 可能会花费时间并浪费 CPU 个周期。
你能从下面的例子中告诉我在这种情况下应该使用什么吗,因为它是 运行 在多线程环境中(不同的例子)并且变量可能会改变?
#include <stdio.h>
#include <stdint.h>
int main()
{
printf("Direct assign method\n");
uint8_t pack_id = 123;
uint8_t pack_age = 76;
uint8_t pack_cmd = 30;
uint8_t cus_data[3] = {0};
cus_data[0] = pack_id;
cus_data[1] = pack_age;
cus_data[2] = pack_cmd;
printf("Memcpy method\n");
pack_id = 112;
pack_age = 89;
pack_cmd = 25;
memcpy(&cus_data[0], &pack_id, sizeof(pack_id));
memcpy(&cus_data[1], &pack_age, sizeof(pack_age));
memcpy(&cus_data[2], &pack_cmd, sizeof(pack_cmd));
return 0;
}
现代计算机的地址大小大多为8字节,即64位。 您只是不将8字节的地址分配给1字节(8位)存储变量。
例如 memcpy(&cus_data[0], &pack_id, sizeof(uinit8_t*));
但是,对于一个序列,您执行了以下 3 次。 memcpy(&cus_data[0], &pack_id, sizeof(uinit8_t));
memcpy() 函数。是这样的
void memcpy(void *dest, void *src, size_t size) {
char *c_src = (char *)src;
char *c_dest = (char *)dest;
for (int i=0; i<size; i++)
c_dest[i] = c_src[i];
}
它将src 1byte 1byte复制到dest。等等..
所以,我喜欢使用 struct, case 1 而不是上面的 进一步的情况 2 代码复制一次指针。
struct PACK{
uint8_t id;
uint8_t age;
uint8_t cmd;
};
int main() {
struct PACK pack = { .id = 123, .age = 76, .cmd = 30 };
uint8_t cus_data[3];
// case 1
memcpy(cus_data, &pack, 3);
// case 2
uint8_t *p = (uint8_t*)&pack;
for(int i=0; i<3; i++)
printf("%d\n", p[i]);
return 0;
}
cus_data[0] = pack_id;
永远不会比 memcpy(&cus_data[0], &pack_id, sizeof(pack_id));
慢。然而,如果编译器内联 memcpy 调用,它们可能同样快,这是很有可能的。
您应该做的不是担心微优化,而是编写尽可能易读的代码。当您遇到实际性能问题时,担心优化。
因为 cus_data[0] = pack_id;
是最易读的,那是你应该做的。
in such case since it is running in multithreaded environment
没有区别。您需要保护变量免受重入错误的影响,或者您不需要保护它们。这与简单赋值与 memcpy 无关,因为它们都不能保证是原子的。
变量如何 small/large 并不重要,因为 C 语言中没有任何东西可以保证原子访问,除非您使用 C11 _Atomic
和类似的。
几乎每个优化编译器(当然是 gcc、clang 和 icc)都将 memcpy
(以及少数其他 libc 函数,例如 memcmp
)视为由真正的支持的内置函数同名的 libc 函数。
编译器通常只会调用真正的函数,前提是使用内联汇编完成工作看起来工作量太大。
复制角色当然不属于这一类。这两个生成 same assembly with gcc/clang/icc:
#include <string.h> //compilers generally know about memcpy
//but C still requires that it should be prototyped
void assign_char(char *X)
{
*X = 'x';
}
void memcpy_char(char *X)
{
memcpy(X,"x",1);
}
x86_64 输出:
assign_char:
mov BYTE PTR [rdi], 120
ret
memcpy_char:
mov BYTE PTR [rdi], 120
ret
使用哪一个都没有关系。 memcpy
版本更通用(也适用于数组,即使不能在 C 中为数组赋值 to/from)并允许您规避严格的别名,但它的编写也有点冗长。