前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言strcpy(),memcpy(),memmove() | 数组赋值给数组

C语言strcpy(),memcpy(),memmove() | 数组赋值给数组

作者头像
CtrlX
发布2023-03-21 11:48:23
3K0
发布2023-03-21 11:48:23
举报
文章被收录于专栏:C++核心编程C++核心编程

一个数组赋值给另一个数组的方法

代码语言:javascript
复制
int arr1[5] = {1, 2, 3, 4, 5};
int arr2[5];
arr2 = arr1;  // 错误,不能直接赋值

方法一

使用循环遍历数组中的每一个元素

代码语言:javascript
复制
int arr1[5] = {1, 2, 3, 4, 5};
int arr2[5];
for (int i = 0; i < 5; i++) {
  arr2[i] = arr1[i];
}

使用std::array时,可以使用赋值运算符来复制:

代码语言:javascript
复制
std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
std::array<int, 5> arr2;
arr2 = arr1;

方法二

使用C语言库函数解决

1.使用strcpy()和strncpy()函数处理字符串数组

strcpy()函数用于复制一个字符串到另一个字符串。该函数的语法如下:

代码语言:javascript
复制
char *strcpy(char *dest, const char *src);

它从源字符串src复制字符到目标字符串dest,包括NULL字符(即字符串结束标志)。

strncpy()函数与strcpy()类似,但是其复制的字符数可以限制。该函数的语法如下:

代码语言:javascript
复制
char *strncpy(char *dest, const char *src, size_t n);

它从源字符串src复制字符到目标字符串dest,但是最多复制n个字符。

举个例子:

代码语言:javascript
复制
#include <iostream>
#include <cstring>

int main()
{
    char source[] = "Hello, World!";
    char destination[50];

    // using strcpy
    strcpy(destination, source);
    std::cout << "After using strcpy: " << destination << std::endl;

    // using strncpy
    strncpy(destination, source, 5);
    destination[5] = '\0';
    std::cout << "After using strncpy: " << destination << std::endl;

    return 0;
}

输出:

代码语言:javascript
复制
After using strcpy: Hello, World!
After using strncpy: Hello

PS:详见C Primer Plus P351

2.使用string.h库中memcpy()和memmove()函数处理任意类型的数组

memcpy() 和 memmove() 是 string.h 库中的两个常用的内存复制函数。这两个函数可以用来处理任意类型的数组,并复制从源数组到目标数组。

memcpy() 函数把一块内存复制到另一块内存,但是不会去处理内存是否重叠。它可以被用来处理整个数组或仅仅一部分。它的语法如下:

代码语言:javascript
复制
void *memcpy(void *dest, const void *src, size_t n);

memmove() 函数的作用与 memcpy() 类似,可以处理内存重叠的情况。它的语法如下:

代码语言:javascript
复制
void *memmove(void *dest, const void *src, size_t n);

如果你想复制一个整数数组,例如:

代码语言:javascript
复制
int source[5] = {1, 2, 3, 4, 5};
int target[5];

memcpy(target, source, sizeof(source));

这两个函数是 C 语言中非常有用的内存复制函数,可以用来处理任意类型的数组,也可以用来处理字符数组。

PS:详见C Primer Plus P558

c和c++使用的内存拷贝函数,memcpy函数和memmove函数的功能都是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。

要使用这两个库函数需要引用头文件 <string.h> 函数原型: void* memcpy(void* destination,const void* source,size_t num); voidmemmove(void destination,const void* source,size_t num); destination:目的地指针(首地址) source:源头指针(首地址) num:需要复制的字节数

memcpy和memmove都是C语言标准库函数,用于内存拷贝。两者的主要区别在于:

  • memcpy是直接在内存中复制数据,不会考虑内存重叠的情况。如果源区域和目标区域有重叠的部分,可能会出现未定义的行为。
  • memmove是在内存中复制数据,考虑内存重叠的情况。如果源区域和目标区域有重叠的部分,会先把数据复制到临时空间,再从临时空间复制到目标区域,保证数据不会被覆盖。

因为memcpy的运行速度比memmove快,所以memcpy常常被用于内存拷贝。在程序员能确保源区域和目标区域没有重叠或者能够接受重叠部分数据被覆盖的情况下memcpy是很好的选择。但是在不能确定源区域和目标区域是否重叠或者不能接受重叠部分数据被覆盖的情况下,应该使用memmove以保证数据完整性。

举个例子,假设我们有一个字符数组char arr[10],我们想把arr[2]arr[5]这4个字符移动到arr[6]arr[9]这4个位置上。 如果使用memcpy(arr+6, arr+2, 4),结果就会是这样的:

代码语言:javascript
复制
原数组: | A | B | C | D | E | F | G | H | I | J |
拷贝后: | A | B | C | D | E | C | D | E | C | J |

可以发现,原本arr[6]~arr[9]的数据被覆盖了。 如果使用memmove(arr+6, arr+2, 4),结果就会是这样的:

代码语言:javascript
复制
原数组: | A | B | C | D | E | F | G | H | I | J |
拷贝后: | A | B | C | D | E | C | D | E | F | J |

可以发现,原本arr[6]~arr[9]的数据没有被覆盖,源区域的数据也没有丢失。 在这个例子中,memmove 保证了数据完整性,而memcpy没有。 简而言之,memcpy是一个快速的内存拷贝函数,memmove是一个安全的内存拷贝函数,当你不能确保源区域和目标区域是否重叠或者不能接受重叠部分数据被覆盖的情况下,应该使用memmove来保证数据完整性。

借一下图:https://blog.csdn.net/m0_66363962/article/details/126903690

image
image
image
image

通过上图的变化路程可知:memcpy总是从低地址开始往高地址复制的,但是当dest>src,并且目的地与源头有重叠时,使用memcpy会覆盖掉源头后面的数据,导致结果出错。这个时候就需要进行判断,如果再次出现上面的事件,我们应当从高地址往低地址开始复制但是memcpy函数在设计时没有这种判断。所以使用memmove函数解决。

memcpy从高地址往低地址复制不会受内存重叠时的问题的影响。 从低地址往高地址复制时需要使用memmove函数。

memcpy比循环赋值快,原因如下:

1.在 C 语言中,使用 memcpy 函数进行内存复制通常比使用循环赋值更快。这是因为 memcpy 是一个底层的函数,它可以直接操作内存,而不需要进行额外的运算。它使用缓存和高级的内存管理技术来提高性能。

2.另外,memcpy 函数可以并行执行,因此多核处理器上能够更高效地运行,而循环赋值串行执行的,所以性能更差。

下面是一个使用 memcpy 函数进行内存复制的示例代码:

代码语言:javascript
复制
#include <string.h>

int main() {
    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5];
    memcpy(arr2, arr1, sizeof(arr1));
    // arr2 is now {1, 2, 3, 4, 5}
    return 0;
}
 

请注意,当源和目标内存块有重叠时,memcpy 函数可能会出现不确定的行为,因此在这种情况下应该使用 memmove 函数代替。

PS:并行执行是指多个任务在同时进行,也就是多个任务同时执行。串行执行是指一个任务执行完成后再执行下一个任务,也就是一个任务一个任务地执行。

memcpy 函数是如何实现并行执行的?

memcpy 函数通常是通过硬件加速来实现并行执行的。例如,许多现代处理器都具有内置的存储器控制器,可以并行地从一个地址拷贝数据到另一个地址。这些控制器可以利用多个通道和多个缓存来并行执行数据拷贝操作,从而大大提高了数据拷贝的速度。

memcpy本身不支持多核并行,它是由系统底层实现的,通常使用的是硬件级的指令来提高复制效率。

关于memcpy并行,通常指的是在单核内部使用SIMD(单指令多数据)指令来并行执行复制操作。SIMD指令可以在一个时钟周期内处理多个数据。例如,128位的AVX指令可以同时处理8个32位整数或4个64位整数。这样可以减少数据处理时间,提高复制效率。

所以memcpy并行指的是在单核内部使用SIMD指令来并行执行复制操作。

memcpy 函数也可以使用多线程和多核处理器来实现并行执行。 例如,如果将数据分成若干块,每个线程分别处理一块数据,这样就可以并行执行数据拷贝操作。

如果要在多核并行的情况下使用memcpy,可以使用多线程或多进程的方式,将大块数据分割成多个小块,分别在不同的核上进行复制。这样可以利用多核的计算能力来提高复制效率。

具体实现的方式可以使用pthread库或OpenMP来实现多线程,或者使用MPI来实现多进程。其中使用OpenMP是目前并行计算中比较流行的方式。

但是这种方式需要更多的编程工作量和空间复杂度,而且对于小块数据来说反而会增加开销,所以通常在复制大块数据的场景下使用。

总之,memcpy函数实现并行执行的方式有很多,具体实现取决于具体环境。

关于线程亲和度

线程可以设置亲和度,这样可以指定它运行在哪个 CPU 核上。这样做的目的是为了提高性能,因为在同一个 CPU 核上运行的线程可以共享缓存和其他硬件资源,而在不同核上运行则不能。不过,这需要程序员手动设置,并且需要操作系统的支持。

在不同的操作系统和编程语言中,设置线程亲和度的方式可能有所不同。下面给出一些常见的例子:

  • Linux: 可以使用 sched_setaffinity() 函数来设置线程的亲和度。
  • Windows: 可以使用 SetThreadAffinityMask() 函数来设置线程的亲和度。
  • Java: 可以使用 Thread.setAffinity() 方法来设置线程的亲和度。
  • C++11: 可以使用 std::thread::set_affinity() 方法来设置线程的亲和度。

在设置亲和度之前,需要先确定系统中可用的 CPU 核数量,并将线程亲和度设置为对应的核的标识。一般来说,亲和度是一个位图,每个位对应一个 CPU 核。

需要注意的是, 设置线程亲和度可能会导致系统性能变差,因为这需要额外的上下文切换.

PS:上下文切换是指 CPU 从一个线程切换到另一个线程时所需要进行的操作。在切换过程中,需要保存当前线程的环境(如寄存器的值),并将新线程的环境加载到 CPU 中。这个过程会消耗一定的时间,如果频繁发生,会导致系统性能下降。

在设置线程亲和度时,如果线程频繁地在不同的 CPU 核之间切换,就会导致上下文切换频繁发生,从而导致系统性能变差。因此,在设置线程亲和度时,需要谨慎考虑,确保它对系统性能的影响是最小的。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一个数组赋值给另一个数组的方法
    • 方法一
      • 方法二
        • 1.使用strcpy()和strncpy()函数处理字符串数组
        • 2.使用string.h库中memcpy()和memmove()函数处理任意类型的数组
    相关产品与服务
    GPU 云服务器
    GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档