数据复制方法直接分配与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)并允许您规避严格的别名,但它的编写也有点冗长。