目录
一、位求和运算
计算二进制位的求和,如bsum(7&0x0000FFF);
```c
int bsum(int arg)
{
int res = 0;
for (int i = 0; i<32; i++)
{
res += arg & (int)1;
arg >>= (int)1;
}
return res;
}
二、判断是否>1
short sh=1;
if(sh&1){
do something......
}
三、位操作相关变量
对于一系列又相互联系的bool类型变量通过| &位操作来综合起来,最后一起处理
水是否烧开,面条是否准备好,人是否到起,是否已洗手 -------> 0 0 0 0,分别表示为二进制1000 , 100,10,1表示为16进制为0x8,0x4,0x2,0x1;计算时可以与0进行 |操作得出最后的结果。
四、结构体类型、函数名命名及注释
一般在.h中定义该模块的结构体类型和函数(外部接口函数及内部函数)
命名规则: 模块名简写+下划线+具体含义英文
如结构体的标识符后面typedef struct HMMY_config_s{}HMMY_config_T;
如函数名字,void HMMY_config();HMMY_getWhatYouNeed();
/*function description:
*[param][in]:
*return:
*/
结构体命名规则:
typedef enum LEFT_LANE_COLOUR_TYPE{
LS_INVALID = 0U,
LS_YELLOW = 1U,
LS_RED = 2U,
LS_BLUE = 3U,
LS_WHITE = 4U
}LeftLaneColour_E;
局部变量命名为:类型+实际含义 如unsigned char ucRet=0; bool bIsRight = false;
五、嵌入式C语言中没有定义bool布尔类型
typedef unsigned char bool_T
typedef unsigned char boolean
typedef enum BOOL{ false = 0,true =1,FALSE =0,TRUE=1 } bool;
六、如何把C模块文件变得“像”面对对象编程
目的:变得简洁,方便维护
6.1 私有函数在头文件注释的internal说明并声明,私有函数在源文件上面说明并声明
6.2 公有对外接口,设计为模块名+下划线+getXXXX(void * 指针pointer),公有变量设计为结构体模块名_public_variable_T,然后在头文件用static声明定义。对于函数传进来的指针,一般在函数体内在创建一个该类型的指针变量并将指针参数赋值给它,如类型是TPoint,则 TPoint * ptemTp = (TPoint *) pointer;
6.3 每个源文件包含两个头文件,如CAN.c, CAN.h,CAN_pdf.h,pdf表示项目预定义的宏定义和类型变量定义,再CAN.h中不希望看到任何类型和宏定义,只有函数和变量
6.4 一般包含common.h 和平台_pdf.h等
头文件及源文件定义模块
//
// Project:
//
//! \file
//! \author
//! \date
//!
//! \brief
//!
//!
//!
//
// This program is the confidential and proprietary product of xxxxxxxxxx
// Any unauthorised use, reproduction or transfer ofthis program is strictly prohibited.
// Copyrightxxxxxxxxxxxxxxx (Subject to limited distribution and restricted disclosure on//ly.) All rights reserved.
//!
// \brief 备注:
//
//--------
//includes
//-------
//--------
//defines and Macros
//-------
//--------
//Typedefs
//-------
//--------
//Constants
//-------
//--------
//local function prototypes
//-------
//--------
//data
//-------
//--------
//Functions
//-------
{
//------------------------
//! \brief:
//!
//! \param[in] xxx: ooo
//! \return xxx ooo
}
//-----------
// End of file
//------------
七、先文档(思路)后编码,先仿真后真实场景
命名规范: 名词空间 3个小写字母组成;类名首字母大写其余小写;文件名小写+下划线组成;
状态机描述表: 本状态---名字-----转移状态----转移事件------触发行为
八 C++常用“伎俩”
8.1友元的使用
借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 public,protected,private 成员。友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。友元类通常表示为“另一个类是我的朋友,他可以访问我”。
友元模板类:
friend class Pal<HasFriend>; 将类模板的一个实例声明为普通类的友元;
template <typename T> friend class Pal;这个类模板的所有实例都是类的友元
8.2派生大法
virtual 析构函数;
做好多态准备virtual,子类通用功能函数;
a.private成员只能被本类成员(类内)和友元访问,不能被派生类访问;
b.protected成员可以被派生类访问。
8.3强制类型转换大法
static_cast<type> 向上转换,向下转换
reinterpret_cast<type>无视种族隔离,用在任意指针(或引用)类型之间的转换;以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。
8.4函数实现
首先要检测传入参数是否正常; 不能返回局部变量指针或引用。
8.5define and macros
全局配置参数常量定义使用static const <type> xxx = 1U;
8.6 记录
#define logger_log (lvl,mod,msg,...)do { \
_logger_log(lvl,mod,1,__FILE__,(char*)__FUNCTION__,__LINE__,msg,##_VA_ARGS__);\
}while(0)
#define DBG_LOCAL_FILENAME "main.cpp"
unsigned short Dbg_validate_line_number = 0U;
const char *Dbg_validate_filename = NULL;
//写一个验证有效性的函数
#define valiedate(exp) \
/*I want to use do ... while(FALSE)*/ \
do{ \
if(!(exp)) /*exp == 0*/ \
{ \
dbg_validate_fail(文件名"kkk.cpp",(uint16)__LINE__);\
} \
}while(FALSE)
void DBG_validate_fail(
const char *filename,
unsigned short line) {
// Dbg_validate_line_number = line;
// Dbg_validate_filename = filename;
#ifdef DbgPro_
printf("%c, %d has a problem",filename,line )
#endif
}
8.7名词空间
宏定义在名词空间前定义;头文件源文件均需要名词括起来。
8.8 模板类的使用
当你希望你定义的类能“传”进来各种各样的类(具有类属性),也就是说你希望你的程序实体所操作的数据类型不一样,但是程序完成的功能一样。
使用类模板的好处:
1)可以用来创建动态增长和减小的数据结构
2)它是类型无关的,具有高度的可复用性
3)它在编译时而不是运行时检查数据类型,保证了类型安全
4)与平台无关,可移植性
5)可用于基本数据类型
用法很简单,在描述程序实体前,写下template <class T1, classT2,...>
函数模板与类模板的区别:函数模板的实例化是由编译程序在处理函数调用时自动完成的;而类模板的实例化必须由程序员在程序中显式地指定
8.9 回调大法
实现两个类之间的通信(https://blog.csdn.net/qq_29924041/article/details/74857469)
回调四步曲:
- 定义一个回调接口
- 调用类去实现这个接口,重写方法
- 被调用者将接口做为参数传递进去,并且在某个时刻进行出发
- 调用者在调用的时候去实例化这个回调接口(如上述案例中因为已经实现了接口,所以将类本身对应的对象作为参数传递进去)
8.10 一个类中保证只有一个单一的对象
在类成员函数中使用static变量定义一个该类的对象,使用时用该函数来调用类成员及初始化等。
//dbm.h
namespace dbm{
class DbmClass{
public:
DbmClass(){;}
/*保证只有一个单一的类对象*/
static DbmClass* getInstance(void);
};
}
//dbm.cpp
DbmClass* DbmClass::getInstance(void){
static DbmClass dbmClassInstance;
return &dbmClassInstance;
}
8.11 多线程必备之线程池
https://www.cnblogs.com/ailumiyana/p/10016965.html出自(https://www.cnblogs.com/ailumiyana/)
九 C工程常用“伎俩”
9.1预编译指令#Pragma 指令作用是设定编译器的状态或者是指示编译器完成一些特定的动作
9.2参数预定义:如定义长宽高,用于函数传参
#define SHAPE_DATA \
3,\
2,\
2
9.3enum使用场景
表示状态,功能类型,使用模式,标识符ID,错误ID ,状态机类型,用户模式
enum CANError_E{
CAN_STATUS_OK =0U,
CAN_READ_FAILED,
CAN_WRITE_FAILED,
CAN_NO_DEVICE,
CAN_NO_SPACE,
CAN_NO_ACCESS
};
9.4使用掩码的位操作
使用场景:当一个变量表示状态量或者标志位,可以define定义一系列16进制的宏,然后对于获取的值进行位操作
//define WIFI mask
#define WIFI_CONNECTION_STATUS_MASK 0x000001
//获取该WIFI的状态
#define GET_WIFI_CONNECTION_STATUS(x) (x& WIFI_CONNECTION_STATUS_MASK)
#define SET_WIFI_CONNECTION_STATUS(x) x =(x| WIFI_CONNECTION_STATUS_MASK)
事实上这跟使用位域是同样的效果
/* 定义位域结构 */
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2;
9.5问号冒号表达式的学问
对于简单的逻辑判断,何不采用简单利索的表达式
#define CONNECT_WIFI_ID(x) (x==0)?WIFI_ID1: \
((x==1)?WIFI_ID2: \
((x==2)?WIFI_ID3))
是不是比if ----elseif---elseif-------else, switch ----case----case--default 要简洁许多
9.6 全局变量:extern,static,const
1. extern "C" void fun(int a, int b); 则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的
2.当一个全局函数声明或者定义时:int foo(int arg1, char arg2);编译器认为是这样的:extern int foo(int arg1, char arg2);
结论:对外部接口函数声明为extern,内部函数声明为static,显示声明易于理解
3.函数或变量在声明时,并没有给它实际的物理内存空间,只有当函数或变量定义的时候,它就在内存中有了实际的物理空间,
建议:对于全局变量,如果使用extern 在.h声明一个全局变量,extern int a,必须在对应的.c文件定义,使用该全局变量只需包含该头文件即可。如果声明和定义放在一起: extern char g_str[] = "123456"; // 这个时候相当于没有extern
4.static修饰的全局变量声明与定义同时进行,static修饰全局变量的作用域 只能是本身的编译单元(唯一的)
5.const修饰的全局常量据有跟static相同的特性,即它们只能作用于本编译模块中,但是const可以与extern连用来声明该常量可以作用于其他编译模块中
参考这里:
https://www.geeksforgeeks.org/understanding-extern-keyword-in-c/
https://www.cnblogs.com/debuging/p/3158742.html
9.7 使用restrict
C语言关键字restrict,只用于修饰指针,功能是帮助编译器进行优化!值得注意的是,优化需要就事论事,经过分析:如果发现确实优化不了,就不优化了。不加restrict关键字修饰的指针,编译器一定不进行优化。
9.8 保存最大值或者最大值索引
curEnergy =0; maxEnergy =0;
for(cnt= 0; cnt < 16; cnt++)
{
/**Check between 0 and 32 */
curEnergy = E1*E2;
maxFlag = curEnergy > maxEnergy;//1
maxEnergy = curEnergy * maxFlag + maxEnergy * (1-maxFlag);
maxIdx = cnt* maxFlag + maxIdx * (1-maxFlag);
}
9.9 二维数组声明定义与使用
数组指针与指针数组,关键看后面两个字,前面代表修饰符;数组指针联想整形指针/浮点指针表面是指向数组的指针,它其实还是一个指针,只不过是指向数组而已,表示为int (*a)[10],10表示每个指针指向的数组有10个元素;指针数组表明里面装的是指针的数组int *a[10],都是int*。
如何应用数组指针来表示二维数组:
int a2dimArray[3][2] = { { 1, 2 }, { 4, 5 }, {7,8} };
int(*p2dimPointer)[2];
p2dimPointer = (int(*)[2])&a2dimArray[0][0];
for (int i = 0; i < 3; i++)
{
//最直观的表示
// for (int j = 0; j < 2;j++)
// printf("a2dimArray= %d\n", p2dimPointer[i][j]);
//仅仅把p2dimPointer[i]做为行的首地址
// printf("a2dimArray= %d\n", *(p2dimPointer[i]+0));
// printf("a2dimArray= %d\n", *(p2dimPointer[i]+1));
//仅仅把p2dimPointer做为二维数组第一个元素指针的首地址
printf("a2dimArray= %d\n", *(*(p2dimPointer + i)+0));
printf("a2dimArray= %d\n", *(*(p2dimPointer + i) + 1));
}
时刻牢记,二维数组的a[i]表示该行的首地址;如果是&a+1则跨越整个数组;*(a+i) = a[i]
9.10 异或操作,效率不错
for (int i=0;i<128;i++)
{
(i^ ((i>> 5U) * 32))
//不断输出0-31,循环4次
}
9.11 全局变量瓜分大数组
unsigned char gastratchBuff[28880];
unsigned int *pu32FirstOne = (u32*)&gastratchBuff[0];
struct_Some1_T *pSome1 = (struct_Some1_T*)((u8*)pu32FirstOne);
struct_Some2_T *pSome2 = (struct_Some2_T*)((u8*)pSome1+sizeof(struct_Some1_T));
9.12 数组元素偏移
保存第一个元素,然后第一个元素等于后一个元素,以此类推
9.13定义通用TASK函数
#define OS_EXTERN_C
#define TASK(x) OS_EXTERN_C void OS_TASK_##x(void)
#define PT_12T 12
TASK(PT_12T ){}
9.14直接在.h中定义函数
比如说一个功能模块BSD,定义一个BSD_Debuger.h将函数声明定义写在一个头文件里面,但是只能被一个源文件引用,一旦文件被2个cpp引用,就会有multiple definition错误。
十 如何团队协作
文档管理:腾讯文档或者Google文档
代码管理:git工具,Gerrit
统一内存分配,统一软件架构,统一接口设定:硬件接口、软件接口
统一接口包括:接口变量(结构体),接口函数;指定接口的输入输出,开发别的模块的同事不需要知道细节是怎么实现的。
之前写的git博文:https://blog.csdn.net/qinze5857/article/details/107287329
十一 工程架构
1.多核工程定义
common文件夹(通用核间通信文件夹general core communication moduleGCCM,通用头文件文件夹general include Module GINM,第三方文件夹SDK编译器脚本等,通用时间模块general time module GTMM,工程配置XXX_Config.h,工程配置常量XXX_Config_const.h,工程结构体XXX_structures.h,工程定义XXX_define.h,工程全局变量XXX_GData.h/.c等等)
2.软件版本管理
#define MAJOR_VERSION 1 /*有没有重大更新*/
#define MINOR_VERSION 0 /*主版本下更新几次,根据测试*/
#define BugFIX_VERSION 20 /*解决的bugs*/
#define Dev_VERSION 0 /*软件开发标识,代表不同含义*/
/*****************************************/
/*History */
/*****************************************/
/*Author |Date |Version |Requests | Modification */
/*========================================*/
/*========================================*/