C语言零基础教程(memset,memcpy函数,memmove函数)

2023-07-29 17:50:08


前言

本篇文章来讲解一下memset和memcpy函数,这两个函数在C语言中也是比较重要的,这里我们就来学习一下这两个函数的使用方法吧。

一、memset函数

memset 函数是一个C标准库中的函数,用于将一块内存区域的每个字节设置为指定的值。

memset 的定义如下:

void *memset(void *ptr, int value, size_t num);

函数的参数包括 ptr,表示要设置的内存区域的起始地址;value,表示要设置的值,通常以整数表示,但在传给 memset 时会自动转换为 unsigned char 类型;num,表示要设置的字节数。

memset 函数的工作原理是将指定值 value 拷贝到指定内存区域 ptr 所指向的每个字节中,重复拷贝 num 次。

常见的用法是将内存区域初始化为特定值,例如将整个数组清零:

int arr[10];
memset(arr, 0, sizeof(arr));

上述代码将数组 arr 的所有元素设置为零。这是非常高效的一种方式,特别是对于大型数组或者结构体,因为它直接操作内存,而不是逐个元素赋值。

需要注意的是,memset 函数只能设置每个字节的值,因此对于非 char 型的数组,设置的值可能会被截断或产生不可预测的结果。针对非字符类型的数组或结构体,应该使用其他方法来进行赋值。

此外,还需要谨慎使用 memset,因为它没有边界检查,容易导致越界操作或者非法访问内存。

易错点:

当使用 memset 函数给不同类型的数组置为某个值时,确实需要注意不同类型的字节大小和表示范围,以避免出现问题。以下是一个示例:

#include <stdio.h>
#include <string.h>

int main() {
  int intArray[5];
  char charArray[5];

  // 设置 int 类型数组为 1
  memset(intArray, 1, sizeof(intArray));

  // 设置 char 类型数组为 1
  memset(charArray, 1, sizeof(charArray));

  printf("intArray:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", intArray[i]);
  }

  printf("\n\ncharArray:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", charArray[i]);
  }

  return 0;
}

运行上述代码,输出如下:

intArray:
16843009 16843009 16843009 16843009 16843009

charArray:
1 1 1 1 1

可以看到,memset 函数对 int 类型数组的每个字节都设置为 1,并没有按预期将整个 int 类型的元素设置为 1。这是因为 memset 函数按字节拷贝,将 1(int 类型转换为 unsigned char 类型)复制到了每个字节,并没有设置整个 int 类型元素的值。

相比之下,对 char 类型数组使用 memset 函数,每个字节都被设置为 1,包括 ASCII 值为 1 的字符。

因此,在使用 memset 函数时,应注意被设置的值要与数组元素类型相匹配,以避免产生意料之外的结果。如果想将整个 int 类型数组设置为某个值,可以使用循环逐个赋值的方式来确保正确设置。

二、memcpy函数

memcpy 函数是 C 标准库中的一个函数,用于在内存之间进行字节级别的数据拷贝。memcpy 可以将源内存区域的内容复制到目标内存区域,并返回指向目标内存区域的指针。

memcpy 的定义如下:

void *memcpy(void *dest, const void *src, size_t n);

函数的参数包括 dest,表示目标内存区域的起始地址;src,表示源内存区域的起始地址;n,表示要复制的字节数。

memcpy 函数会将源内存区域中的 n 个字节的数据复制到目标内存区域,可能包含原先的内容。函数不会检查边界,因此保证源和目标内存区域的大小至少为 n 是非常重要的。

以下是一个示例,展示 memcpy 的用法:

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, world!";
    char dest[20];

    memcpy(dest, src, strlen(src) + 1);

    printf("Copied string: %s\n", dest);

    return 0;
}

上述代码将源字符串 src 复制到目标字符数组 dest 中。memcpy 函数使用了 strlen(src) + 1 作为要复制的字节数,确保整个字符串被复制到目标数组中,包括字符串的结尾符 ‘\0’。

在运行代码后,输出如下:

Copied string: Hello, world!

可以看到,源字符串 src 成功地复制到了目标字符数组 dest 中。

需要注意的是,memcpy 函数在进行内存拷贝时是按字节级别操作的,不关心内存中保存的是什么类型的数据。这也意味着在使用 memcpy 时,应确保源和目标内存区域之间没有重叠,以免产生意想不到的结果。如果源和目标内存区域有重叠,可以使用 memmove 函数来避免数据被破坏。

三、memmove函数

memmove 函数是一个 C 标准库中的函数,用于在内存之间进行字节级别的数据拷贝。与 memcpy 函数不同的是,memmove 函数可以处理可能发生重叠的内存区域的拷贝。

memmove 的定义如下:

void *memmove(void *dest, const void *src, size_t n);

函数的参数包括 dest,表示目标内存区域的起始地址;src,表示源内存区域的起始地址;n,表示要复制的字节数。

memmove 函数将会将源内存区域中的 n 个字节的数据复制到目标内存区域中,即使源和目标内存区域有部分或完全重叠。函数会自动处理重叠情况,以确保数据被正确复制。

以下是一个示例,展示 memmove 的用法:

#include <stdio.h>
#include <string.h>

int main() {
  char str[] = "Hello, world!";
  memmove(str + 7, str, strlen(str) + 1);

  printf("Moved string: %s\n", str);

  return 0;
}

上述代码将字符串 str 移动了 7 个位置,即将字符串的前部分移动到后部分。在这个例子中,memmove 函数被用来处理源和目标内存区域可能重叠的情况。

运行代码后,输出如下:

Moved string: world! Hello,

可以看到,源字符串 str 成功地移动了 7 个位置,并且重叠部分的数据也被正确处理。

需要注意的是,相比于 memcpy 函数,memmove 函数的实现可能会更加复杂和耗时,因为需要处理内存区域的重叠情况。因此,在没有重叠的情况下,推荐使用 memcpy 函数来进行拷贝操作,因为它的实现更简单且通常更高效。只有当存在内存区域重叠的情况时,才需要使用 memmove 函数

总结

本篇文章就讲解到这里,大家看完后可以进行实验验证。

更多推荐

【表格插入小计行】el-table表格,数组对象中根据某字段插入小计行计算数据

前言功能解释:遇到的一个需求,是表格的tabledata数组。里面有科室医生还有很多消费指标等数据。然后需要我排序后把科室放在一起。然后在每个科室下面添加一行数据,是小计行。用于计算上面相同科室的所有数据汇总。然后最下面再来个合计行,加上所有的小计。效果图刚排序后是这样的表格样子,数据是我模拟的然后插入小计行后是这样的

JavaScript事件流:深入理解事件处理和传播机制

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录引言1.事件流的发展流程1.1传统的DOM0级事件1.2DOM2级事件和addEventListener方法1.3W3CDOM3级事件1.4React与VirtualDOM2.事件流的属性2.1事件捕获阶段2.

《从菜鸟到大师之路 Redis 篇》

《从菜鸟到大师之路Redis篇》(一):Redis基础理论与安装配置Nosql数据库介绍是一种非关系型数据库服务,它能解决常规数据库的并发能力,比如传统的数据库的IO与性能的瓶颈,同样它是关系型数据库的一个补充,有着比较好的高效率与高性能。专注于key-value查询的redis、memcached、ttserver。

transformer大语言模型(LLM)部署方案整理

说明大模型的基本特征就是大,单机单卡部署会很慢,甚至显存不够用。毕竟不是谁都有H100/A100,能有个3090就不错了。目前已经有不少框架支持了大模型的分布式部署,可以并行的提高推理速度。不光可以单机多卡,还可以多机多卡。我自己没啥使用经验,简单罗列下给自己备查。不足之处,欢迎在评论区指出。框架名称出品方开源地址Fa

JWT 令牌撤销:中心化控制与分布式Kafka处理

【squids.cn】全网zui低价RDS,免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等令牌对于安全数字访问至关重要,但如果您需要撤销它们怎么办?尽管我们尽了最大努力,但有时代币可能会被泄露。这可能是由于编码错误、意外记录、零日漏洞和其他因素造成的。令牌撤销是现代安全性的一个重要方面,确

并发编程——synchronized

文章目录原子性、有序性、可见性原子性有序性可见性synchronized使用synchronized锁升级synchronized-ObjectMonitor原子性、有序性、可见性原子性数据库事务的原子性:是一个最小的执行的单位,一次事务的多次操作要么都成功,要么都失败。并发编程的原子性:一个或多个指令在CPU执行过程

【无公网IP内网穿透】 搭建Emby媒体库服务器并远程访问「家庭私人影院」

目录1.前言2.Emby网站搭建2.1.Emby下载和安装2.2Emby网页测试3.本地网页发布3.1注册并安装cpolar内网穿透3.2Cpolar云端设置3.3Cpolar内网穿透本地设置4.公网访问测试5.结语1.前言在现代五花八门的网络应用场景中,观看视频绝对是主力应用场景之一,加上移动网络技术的发展,随时随地

vue3.2+ts封装axios

1.创建utils文件夹/server下面创建index.ts,代码如下:importaxios,{AxiosRequestConfig}from"axios";import{BASE_URL,TIMEOUT}from"@/config/axios";/***@说明接口请求返回信息(按照自己的实际情况分配基础请求格式)

解决WSL2占用内存过多问题(Docker on WSL2: VmmemWSL)

解决WSL2占用内存过多问题(DockeronWSL2:VmmemWSL)一、问题描述二、问题解决2.1创建`.wslconfig`文件2.2重启wsl2一、问题描述安装完WSL2后,又安装了Docker,使用了一段时间,发现电脑变卡,进一步查看,发现CPU和内存占用过大,如下图:docker仅仅运行了mysql和zk

【LLM】Prompt tuning大模型微调实战

noteprompttuning可看做是prefixtuning的简化版本,在输入层加入prompttokens,并不需要加入MLP进行调整来解决难训练的问题,作者实验表明随着预训练模型参数量的增加,prompttuning效果逼近finetuning效果文章目录note一、Propmttuning1.peft库中的t

热点探测技术架构设计与实践

1.概述说到热点问题,首先我们先理解一下什么是热点?热点通常意义来说,是指在一段时间内,被广泛关注的物品或事件,例如微博热搜,热卖商品,热点新闻,明星直播等等,所以热点产生主要包含2个条件:1.有限时间,2流量高聚。而在互联网领域,热点又主要分为2大类:1.有预期的热点:比如在电商活动当中推出的爆款联名限量款的商品,又

热文推荐