memcpy
是 C 语言标准库中的一个重要函数,用于在内存之间复制数据。它定义在 <string.h>
头文件中。
函数原型
void *memcpy(void *dest, const void *src, size_t n);
参数说明
dest
: 目标内存地址,即数据将要被复制到的位置src
: 源内存地址,即要复制的数据来源n
: 要复制的字节数
返回值
返回目标内存地址 dest
的指针
基本用法
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, World!";
char dest[20];
// 复制 src 的内容到 dest
memcpy(dest, src, strlen(src) + 1); // +1 是为了复制字符串结束符 '\0'
printf("源字符串: %s\n", src);
printf("目标字符串: %s\n", dest);
return 0;
}
重要注意事项
内存重叠问题:如果源内存和目标内存有重叠,应该使用
memmove
而不是memcpy
。memcpy
不保证能正确处理重叠内存的情况。char str[] = "Hello, World!"; // 错误的使用方式 - 内存重叠 memcpy(str + 5, str, 7); // 正确的做法 memmove(str + 5, str, 7);
数据类型无关:
memcpy
是按字节复制,不关心数据类型。int src[5] = {1, 2, 3, 4, 5}; int dest[5]; // 复制整个数组 memcpy(dest, src, sizeof(src));
结构体复制:可以用于复制结构体
struct Person { char name[20]; int age; }; struct Person p1 = {"Alice", 25}; struct Person p2; memcpy(&p2, &p1, sizeof(struct Person));
性能考虑
memcpy
通常经过高度优化,比手动实现的逐字节复制要快得多,特别是在处理大块数据时。
与 strcpy 的区别
strcpy
用于字符串复制,遇到 '\0' 停止memcpy
按指定字节数复制,不考虑内容
char src[] = "Hello\0World";
char dest1[20], dest2[20];
strcpy(dest1, src); // 只会复制到第一个 '\0' 前的内容
memcpy(dest2, src, sizeof(src)); // 会复制所有内容,包括中间的 '\0'
实际应用示例
#include <stdio.h>
#include <string.h>
int main() {
// 示例1: 复制数组的一部分
int arr1[10] = {0,1,2,3,4,5,6,7,8,9};
int arr2[5];
memcpy(arr2, arr1 + 3, 5 * sizeof(int)); // 复制arr1[3]到arr1[7]
for (int i = 0; i < 5; i++) {
printf("%d ", arr2[i]); // 输出: 3 4 5 6 7
}
printf("\n");
// 示例2: 复制结构体数组
typedef struct {
int id;
char name[20];
} Employee;
Employee staff1[3] = {{1, "John"}, {2, "Alice"}, {3, "Bob"}};
Employee staff2[3];
memcpy(staff2, staff1, sizeof(staff1));
for (int i = 0; i < 3; i++) {
printf("%d: %s\n", staff2[i].id, staff2[i].name);
}
return 0;
}
memcpy
是 C 语言中非常基础和重要的函数,合理使用可以大大提高数据操作的效率。
另外,也可以直接将结构体数据写入闪存后,直接memcpy
直接读取数据:
1. 确保结构体是紧凑排列的
在写入闪存前,最好确保结构体是紧凑排列的(没有编译器填充的字节),可以使用 #pragma pack
指令:
#pragma pack(push, 1)
typedef struct
{
unsigned char auth_account;
unsigned char auth_autolock;
unsigned char blelockenable;
unsigned char BtnPairModelSwitch;
unsigned char onoff_autolock;
unsigned char dk[12];
unsigned char ds[32];
unsigned char pk[6];
unsigned char ps[16];
unsigned char ak[16];
unsigned char bleKey_unlock;
unsigned char ble_broadcast;
unsigned char ble_bonding;
unsigned char ble_con_num;
unsigned char bleKey_method;
} BLE_Param_struct;
#pragma pack(pop)
2. 计算结构体大小
使用 sizeof
计算结构体的大小:
uint16_t ble_param_size = sizeof(BLE_Param_struct);
3. 定义闪存写入地址
确保你有一个合法的闪存地址(通常是某个闪存扇区的地址,且该地址已擦除):
#define FLASH_STORAGE_ADDR 0x0800F000 // 示例地址,具体根据你的MCU手册确定
4. 写入闪存
调用 fmc_write_noErase_data
函数写入数据:
BLE_Param_struct ble_params = {
.auth_account = 1,
.auth_autolock = 1,
.blelockenable = 0,
// 初始化其他字段...
};
// 写入闪存
fmc_write_noErase_data(
FLASH_STORAGE_ADDR,
sizeof(BLE_Param_struct),
(uint8_t*)&ble_params
);
5. 从闪存读取数据(可选)
如果需要读取数据,可以这样做:
BLE_Param_struct read_params;
memcpy(&read_params, (void*)FLASH_STORAGE_ADDR, sizeof(BLE_Param_struct));
注意事项
闪存擦除:
fmc_write_noErase_data
函数不会擦除闪存,因此在写入前必须确保目标地址已擦除(通常需要调用fmc_page_erase
)。对齐和大小:确保写入的地址和长度符合闪存编程的要求(例如,某些MCU要求按字或半字写入)。
数据备份:闪存写入可能会失败,建议在写入前备份数据并验证写入结果。
生命周期:闪存写入次数有限(通常约10万次),避免频繁写入。
完整示例
#include <string.h>
// 定义闪存地址
#define FLASH_STORAGE_ADDR 0x0800F000
// 写入闪存
void save_ble_params(BLE_Param_struct *params)
{
// 先擦除闪存页(假设擦除函数是 fmc_page_erase)
fmc_unlock();
fmc_page_erase(FLASH_STORAGE_ADDR);
fmc_lock();
// 写入数据
fmc_write_noErase_data(
FLASH_STORAGE_ADDR,
sizeof(BLE_Param_struct),
(uint8_t*)params
);
}
// 读取闪存
void load_ble_params(BLE_Param_struct *params)
{
memcpy(params, (void*)FLASH_STORAGE_ADDR, sizeof(BLE_Param_struct));
}
// 使用示例
int main()
{
BLE_Param_struct params;
load_ble_params(¶ms); // 读取现有数据
// 修改参数
params.auth_autolock = 1;
// 保存到闪存
save_ble_params(¶ms);
}
如果闪存编程需要按字或页对齐,可能需要调整写入方式(例如分多次写入或填充对齐)。