一、类函数的宏定义
初步接触AUTOSARS架构代码时,发现其中用了很多类函数的宏定义以及宏定义拼接等一些技巧来进行模块化,如果对C语言掌握不够熟练则可能会觉得比较难看懂,下面简单介绍一下几种见到的类函数宏定义使用方式。
大致的类函数的宏定义如下:
cpp
// memclass type
#define AUTOMATIC
#define USERMAITC
// ptrclass type
#define SOC_CODE
#define HVIL_CODE
// Function like macro
/* memclass is not used */
#define VAR(type, memclass) type
#define CONST(type, memclass) const type
#define FUNC(rettype, memclass) rettype
/* ptrclass is not used */
#define P2FUNC(rettype, ptrclass, fctname) rettype (* fctname)
#define CONSTP2FUNC(rettype, ptrclass, fctname) rettype (* const fctname)
/* memclass and ptrclass is not used */
#define FUNC_P2CONST(rettype, ptrclass, memclass) const rettype *
#define FUNC_P2VAR(rettype, ptrclass, memclass) rettype *
#define P2VAR(ptrtype, memclass, ptrclass) ptrtype *
#define P2CONST(ptrtype, memclass, ptrclass) const ptrtype * //常量指针: 地址可改,内容不可改
#define CONSTP2VAR(ptrtype, memclass, ptrclass) ptrtype * const //指针常量: 地址不可改,内容可改
#define CONSTP2CONST(ptrtype, memclass, ptrclass) const ptrtype * const
简单概述下类函数的宏定义,其实相当于把我们正常变量和函数声明给宏定义化,并且在宏定义中加上额外的标识来指明是内存是自动分配,以及变量或者函数归属于哪个模块。
例子1 变量的定义:
用一个变量的定义来举例:
cpp
#define AUTOMATIC
#define USERMAITC
#define VAR(type, memclass) type
其中:
cpp
VAR(int, AUTOMATIC) value;
等效:
cpp
int value;
通过宏定义的方式我们可以通过自定义的标识,来了解该变量 的类型 ,内存分配方式 。并且假如不同APP层的变量的类型别名不同,我们可以直接搜索全局VAR关键字,来修改适配对应APP的不同类型别名(而不是通过搜索类型或者变量名来一个个对应修改)
例子2 函数的定义:
用一个函数的声明来举例:
cpp
// memclass type
#define AUTOMATIC
#define USERMAITC
// ptrclass type
#define SOC_CODE
#define HVIL_CODE
/* memclass is not used */
#define FUNC(rettype, memclass) rettype
其中:
cpp
FUNC(void, SOC_CODE) APP_SOC_TASK(void);
等效于:
cpp
void APP_SOC_TASK(void);
通过宏定义的方式我们可以通过自定义的标识,来了解该函数 的返回类型 ,归属模块。
例子3 较复杂的模块化定义:
cpp
#define RTE_E_OK (0x00)
// memclass type
#define AUTOMATIC
#define USERMAITC
// ptrclass type
#define SOC_CODE
#define HVIL_CODE
#define RTE_CODE
/* ptrclass is not used */
#define FUNC(rettype, memclass) rettype
/* memclass and ptrclass is not used */
#define P2VAR(ptrtype, memclass, ptrclass) ptrtype *
FUNC(void , CDD_CODE) RTE_Get_Fault_int_TO (P2VAR(int, AUTOMATIC, RTE_APPL_DATA) Returned_Value)
{
/* do something */
}
#define RTE_Get_Fault_int( Returned_Value ) ( RTE_Get_Fault_int_TO( Returned_Value ), ((VAR(int, AUTOMATIC)) RTE_E_OK))
其中:
cpp
int result = 0;
int test_flag = 0;
result = RTE_Get_Fault_int(&test_flag);
等效于:
cpp
// Step 1
FUNC(void , CDD_CODE) RTE_Get_Fault_int_TO (P2VAR(int, AUTOMATIC, RTE_APPL_DATA) Returned_Value)
// 等效于
void RTE_Get_Fault_int_TO ( int *Returned_Value )
// Step 2
#define RTE_Get_Fault_int( Returned_Value ) ( RTE_Get_Fault_int_TO( Returned_Value ), ((VAR(int, AUTOMATIC)) RTE_E_OK))
result = RTE_Get_Fault_int(&test_flag);
// 等效于
result = ( RTE_Get_Fault_int_TO(&test_flag), ( (int) 0u ) )
通过这种方式来把不同功能的接口进行模块化整合
例子4 较复杂的类型定义:
cpp
// memclass type
#define AUTOMATIC
#define USERMAITC
// ptrclass type
#define SOC_CODE
#define CDD_CODE
#define HVIL_CODE
/* memclass is not used */
#define CONST(type, memclass) const type
/* memclass and ptrclass is not used */
#define P2CONST(ptrtype, memclass, ptrclass) const ptrtype * //常量指针: 地址可改,内容不可改
typedef struct
{
int a;
int b;
}Rte_APP_SOC;
其中:
cpp
typedef P2CONST(struct Rte_APP_SOC, AUTOMATIC, RTE_CONST) Rte_SelfType_APP_SOC;
CONST(struct Rte_APP_SOC, RTE_CONST) Rte_Inst_APP_SOC = {0};
CONST(Rte_SelfType_APP_SOC, RTE_CONST) Rte_Self_APP_SOC = &Rte_Inst_APP_SOC;
等效于:
cpp
// Step1
typedef P2CONST(struct Rte_APP_SOC, AUTOMATIC, RTE_CONST) Rte_SelfType_APP_SOC;
// 等效于
typedef const struct Rte_APP_SOC * Rte_SelfType_APP_SOC;
// Step2
CONST(struct Rte_APP_SOC, RTE_CONST) Rte_Inst_APP_SOC = {0};
// 等效于
const struct Rte_APP_SOC Rte_Inst_APP_SOC = {0};
// Step3
CONST(Rte_SelfType_APP_SOC, RTE_CONST) Rte_Self_APP_SOC = &Rte_Inst_APP_SOC;
// 等效于
const Rte_SelfType_APP_SOC Rte_Self_APP_SOC = &Rte_Inst_APP_SOC;
// 等效于
const struct Rte_APP_SOC * Rte_Self_APP_SOC = &Rte_Inst_APP_SOC;
二、宏定义的拼接用法
宏定义拼接的用法也是有助于功能的模块化。
例如:接收到不同零部件的CAN信号后, 对不同零部件的CAN信号进行解析
例子1 零部件模块化CAN信号解析:
cpp
#define Com_GetValue(ELEMENTTYPE, PROP, ByteValue) Com_GetValue_##ELEMENTTYPE##PROP(ByteValue)
#define Com_GetValue_RXSIG_FILTRALG(ByteValue) ((uint8)(ByteValue) >> 1)
其中:
cpp
uint8 sig = 1;
uint8 value = 0;
value = Com_GetValue(RXSIG, _FILTRALG, sig);
等效于:
cpp
// Step1
value = Com_GetValue_RXSIG_FILTRALG(sig);
// 等效于
value = ((uint8)(sig) >> 1);
三、C语言中常量指针与指针常量的识别与记忆小技巧
1.常量指针
我们可以通过文字的排序来记住常量指针与指针常量的对应格式
如:常量指针 ,先是常量 ,然后是指针 ;即先是const ,然后才是指针
既然是常量的指针 ,那么指向的地址可以改变 ,指向的是对象为常量 ,则内容不可以改变
所以常量指针 :地址可改,内容不可改
cpp
const int *p
2.指针常量
如:指针常量 ,先是指针 ,然后是常量;即先为指针,然后才是const
既然是指针的常量 ,那么指向的地址不可改 ,但是内容可以修改
所以指针常量 :地址不可改,内容可改
cpp
int * const p