1. 打印宏名
打印枚举时,一般只能打印对应的整型值,以下两种方法可以打印枚举对应的字符串
#include<stdio.h>
enum color
{
red,
green,
black,
yellow
};
#define ENUMNAME(n) case n: return #n
const char* print_enum_name(int n)
{
switch(n) {
ENUMNAME(red);
ENUMNAME(green);
ENUMNAME(black);
default: return "unknow enum";
}
}
int main(void)
{
printf("%s\n",print_enum_name(red));
printf("%s\n",print_enum_name(green));
printf("%s\n",print_enum_name(black));
printf("%s\n",print_enum_name(yellow));
}
代码地址:
https://github.com/lupingguang/c_test/tree/master/enum_name_print
tips: red -> ENUMNAME(red);字符串快速转可借助sed
2. 通用接口
在制作so文件时,对使用者开放的接口一般都是固定的,若需要新添加接口,就必须同步修改所有该库的使用者,否则使用者与so不匹配时,编译都无法通过(在so提供的头文件中添加接口,也不会编译通过,因为链接时,链接器需要定位到所有应用程序使用到的接口,以便运行时,根据接口符号表分配运行地址)。
可以在so中导出一个形参为字符串的接口,字符串代表实际调用的函数,这样使用者在调用某新添加接口时,只需要传递该接口的字符名称,so内部自行遍历一个函数指针结构体来判断是否有该接口,即使没有,也不会引起其它问题,仅仅返回代表调用失败的返回值即可。
//common.h
#ifndef MYSTATICLIB
#define MYSTATICLIB
typedef int (*FuncApi)(void *);
struct CommonApiInfo
{
FuncApi FuncApiPtr;
const char *FuncApiStrName;
int FuncApiNR;
};
int Common_call_Interface(char *name, void *InOutParams);
#endif
//common.c
#include <stdio.h>
#include "my_shared_lib.h"
#include <string.h>
static int NewFuncApi(void *InOutParams)
{
printf("[so]use first params as char %c\n", ((char *)InOutParams)[0] );
printf("[so]use first params as int %d\n", ((int *)InOutParams)[1] );
((int *)InOutParams)[2] = 55;
return 0;
}
struct CommonApiInfo CommonApiArray[MAX_COMMON_API_NR] =
{
{NewFuncApi, "NewFuncApi", 1}
//add new api...
};
int Common_call_Interface(char *name, void *InOutParams)
{
unsigned int u32EntryIndex = 0;
int ret = 0;
for(u32EntryIndex = 0; u32EntryIndex <MAX_COMMON_API_NR; u32EntryIndex ++)
{
//
if(CommonApiArray[u32EntryIndex].FuncApiNR == 0)
{
printf("Func: %s, isnt found in CommonApiArray,force return \n", name);
return -1;
}
if(!strcmp(name, CommonApiArray[u32EntryIndex].FuncApiStrName))
{
if(NULL != CommonApiArray[u32EntryIndex].FuncApiPtr && NULL != InOutParams)
{
ret = CommonApiArray[u32EntryIndex].FuncApiPtr(InOutParams);
printf("Func: %s, found in CommonApiArray,FuncApiNR=%d\n", name, u32EntryIndex + 1);
return ret;
}
else
{
return -1;
}
}
}
}
#include <stdio.h>
#include "my_shared_lib/my_shared_lib.h"
int main(void)
{
int Params[3] ={100,1,0};
Common_call_Interface("NewFuncApi", (void *)Params);
printf("[app]get param from so:%d\n",Params[2]);
return 0;
}
3 bin文件存储数据
1.原理
C库函数读写二进制文件,将文件使用少量格式分段存储用户格式化数据
例如从第5个字节[字节编号0开始]开始存储一个结构体,结构体中包含5个int型成员,第1个为校验码[9],第2个到第5个为有效数据[4,3,2,1]
实际会在第5字节处存放一个起始地址[本例中为5,也叫做mark],然后偏移10个字节后开始存储[10个字节用来存放起始地址],
最终在bin文件中的分布如下[hexdump -C bin_file即可查看]:
00000000 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 09 |................|
00000010 00 00 00 04 00 00 00 03 00 00 00 02 00 00 00 01 |................|
2.使用步骤
每次运行时需首先检查bin文件有效性,方法是读取指定位置的起始地址,一般读取位置地址 = 存放的地址即为校验正确
校验失败的情况有:
1、第一次运行,还未创建bin文件
2、用户修改了存储起始地址
3、bin文件损坏
如果校验失败,都会remove掉这个bin文件[第一次运行时的校验失败除外]
后面类实例化时会重新新建该bin文件
新建该文件后,必须执行一次打mark动作,这样下次运行时才能校验通过
3.运行程序时 跟任意一个参数就会触发写读操作,验证bin文件是否可正常使用
注意:写数据的实际起始地址是用户制定地址+10字节的偏移,这样可保证每次更新数据不会冲掉mark,导致下次运行校验失败,再新建文件
+10字节偏移逻辑在源文件的read与save中已写死,可根据需要调整
地址:https://github.com/lupingguang/c_test/tree/master/bin_rw