C--->内存操作的相关函数(附加Canopen 协议应用介绍)
| 大纲
- 内存操作函数介绍:
1.memcpy (memory copy) <string.h> 复制一个变量的值给两一个变量
2.memcmp(memory compare) <string.h> 比较两个变量的值是否相同
3.memset(memory set) <string.h> 初始化相关变量
4.malloc(memory allocation) <stdlib.h> 为指针分配内存大小
5.realloc(reset allocation) <stdlib.h> 扩展指针的内存大小
- void*用法
- 应用: CANopen协议栈的建立
1.memcpy
void* memcpy(void* destiny,void*source,unsigned length);
\brief:拷贝source中前 length的字节到destiny中。
\para:destiny:复制的对象。
source:被复制的对象。
length:字节的长度
\retval: 返回 destiny 的地址
注:在实践中我发现 memcpy(&a[ 0 ] , &b[ 0 ] , LEN),只要数组比较大,LEN可以是无限长,
如len是3,则 拷贝 a[0] = b[0] 、a[1] = b[1] 、 a[2] =b [2] ;
应用:常常用于通讯中数据的收发,如在CAN总线中收发报文,接收和发送都使用同一个数组
把数据拷贝到相应的变量中实现内存块的复制,为什么要复制,因为接受到消息接受的是一个局
部变量,我们想要实现收发,就要把变量发到公共的全局变量之中。
2.memset
extern void *memset(void *buffer, int c, int count);
常常用于结构体或数组的初始化,如在嵌入式行业中,对GPIO等外设参数进行初始化操作
fg1:
struct vary
{
uint8_t a;
uint16_t b;
uint32_t c;
}vary1;
memset(&vary1 , 0l, sizeof(vary1)); // 表示对结构体vary1的所有内存块进行清零操作。
3.memcmp
int memcmp (void* desiny , void*source, unsigned char length);
\brief:比较数据是否相同
\para:destiny 和source 都是比较对象, length表示比较的字节的长度
\retval: 相同返回0,destiny>source ,不同:大于0的数; destiny<source 小于0的数
实际应用:在进行CAN总线通讯时候,CANopen协议层角度,当然节点工作在工作模式heartbeat.buf[0]=0x05,
从机PDO工作在事件触发模式下(event trigger),配合禁止时间(inhibit time),如果数据发生变化就会发送
TPDO报文到上位机,从机如何知道自己数据变化,就是比较原先的数据和现在的数据是否变化。
首先上机节点工作在工作模式(0x05),对TPDO进行初始化拷贝赋值,这个时候记录时间和事件的时间戳。
直接判断时间模式中时间是否到,和事件模式中时间有没有到。一个轮回后,第二次进行模式时候首先判断
数据是否产生变化,然后再判断时间有没有变化。以及考虑时间模式触发下后,需要将所有模式的时间戳重新
更新,并将事件模式的禁止时间重新更新。
以上谈到多变量问题,这个在设计TPDO结构体的时候就需要考虑到。
4.malloc
void* malloc(size_t size);
\brief:为指定变量分配内存空间,并返回一个指向它的指针。
\para:size是内存块大小,以字节为单位。
\retval: 返回一个指针,指向已经分配大小的内存。如果请求失败,则返回NULL.
fg1:
int *a =(char*) malloc(10); //指针指向一个内存为10个字节的地址。
5.realloc(动态内存调整)
extern void *realloc(void *mem_address, unsigned int newsize);
\brief:改变内存大小的指针
\para:
mem_address要改变内存大小的指针名
newsize 给该指针分配新的内存空间的大小
\retval: 如果重新分配成功,则返回指向被分配内存的指针,否则返回空指针NULL
fg1:
#include <stdio.h> //输入输出打印库
#include <string.h> //字符串库
#include <stdlib.h> //标准库
int main()
{
char *str; //定义一个指针
/* 最初的内存分配 */
str = (char *) malloc(15); //为该指针分配内存
strcpy(str, "runoob"); //通过字符串拷贝函数将数据拷贝到相应的指针中
printf("String = %s, Address = %u\n", str, str);
/* 重新分配内存 */
str = (char *) realloc(str, 25);
strcat(str, ".com");
printf("String = %s, Address = %u\n", str, str);
free(str); //释放堆区内存
return(0);
}
结果:

void*用法
Void*表示空类型指针,表示任意类型的指针。
对于C/c++而言,是静态类型的语言,定义变量就会分配内存,但不同类型所占内存空间不同。但所有
指针类型的变量,无论是int*、char*、string*等,其内存空间是相同的,指针的本质是存放变量的地址。
方式:
1.向函数传递void*指针
2.从函数返回void*指针
注意点:void*表示任意类型的指针,主要运用内存操作函数的形参类型和返回值类型
注:内存操作与内存中数据类型无关,即任意类型都可以!!!
应用: CANopen协议栈的建立
//伪代码
CANOPEN_processstack
{
if(启动标志)
{
if(心跳节点.buff[0]==0x00)
{
//1.记录心跳时间戳=心跳时间+设备地址偏移+系统滴答;
//2.发送心跳包
成功继续
不成功return 1;
}
3、选择启动模式
3.1启动
心跳节点.buff[0]=0x05;
TPDO初始化,配置相应的时间戳,状态标志位,以及要发送的信息到buf中
3.2预先启动
心跳节点.buff[0]=0x7f;
}
//TPDO有两种工作模式
//1.inhibit mode 事件触发方式,数据发生变化,并且发送的时间间隔有限制
//2.event time 时间触发,时间到就发送消息
//1.event time mode
1.判断时间到没到
2.到了就发送消息,并且将所有模式的时间置位,并设置标志位,防止禁止时间限制
//2.inhibit time mode
1.第一次的时候判断时间到没到,到了就发送(第一次数据是新的)
2.以后每次都判断数据是否变化,
//这里用到memcmp进行数据块的比较
3.如果发生变化,就拷贝数据给TPDO.buf中,然后发送报文
//心跳模式
1.判断心跳是否工作在0x05状态
2.判断心跳时间是否超出
3.发送报文
4.更新心跳报文的时间戳
return 1;
}
}
}
时间:2021-11-05 20:55:56
修改:2021-11-07 / 2022-11-19

浙公网安备 33010602011771号