1. 数组转16进制字符
static const char *hexdigits = "0123456789ABCDEF";
void data2hex(const void *data, uint32_t len, char *str)
{
const uint8_t *cdata = (uint8_t *data);
for (uint32_t i=0; i<len; i++) {
str[i*2 ] = hexdigits[(cdata[i] >> 4) & 0xF];
str[i*2+1] = hexdigits[cdata[i] & 0xF];
}
str[len*2] = 0;
}
2. 整数转16进制字符
static const char *hexdigits = "0123456789ABCDEF";
void uint32hex(uint32_t num, char *str)
{
for (uint32_t i=0; i<8; i++) {
str[i] = hexdigits[(num >> (28 - i*4)) & 0xF];
}
}
3. 内存数据存放结构化
/* 存放数据起始地址 */
#define FLASH_STORAGE (0x08000000)
/* 将地址转化为字节类型, 用于按字节访问 */
#define FLASH_PTR(x) (const uint8_t *)(x)
/* 宏定义数据结构指针, 指向结构首地址 */
#define storageRom ( (const Storage *)FLASH_PTR(FLASH_STORAGE) )
/* 宏定义各种简单/复合结构的数据类型 */
#define STORAGE_BOOL(NAME) STORAGE_FIELD(bool, NAME)
#define STORAGE_NODE(NAME) STORAGE_FIELD(StorageHDNode, NAME)
#define STORAGE_UINT32(NAME) STORAGE_FIELD(uint32_t, NAME)
/* 数据结构中的复合类型的数据结构定义 */
#define STORAGE_FIELD(TYPE, NAME) \
bool has_##NAME; \
TYPE NAME;
/* 数据结构中的指定长度的字符串结构 */
#define STORAGE_STRING(NAME, SIZE) \
bool has_##NAME; \
char NAME[SIZE];
/* 数据结构中的指定长度的数组结构定义 */
#define STORAGE_BYTES(NAME, SIZE) \
bool has_##NAME; \
struct { \
uint32_t size; \
uint8_t bytes[SIZE]; \
}NAME;
Note: has_##NAME 用于表示该字段数据是否有效
/* 内存中存放的数据结构定义 */
typedef struct {
uint32_t depth;
uint32_t fingerprint;
uint32_t child_num;
struct {
uint32_t size;
uint8_t bytes[32];
}chain_code;
STORAGE_BYTES(private_key, 32);
STORAGE_BYTES(public_key, 33);
}StorageHDNode;
/* 内存中存放的数据结构 */
typedef struct _Storage {
uint32_t version;
STORAGE_NODE (node)
STORAGE_STRING (mnemonic, 241)
STORAGE_BOOL (passphrase_protection)
STORAGE_UINT32 (pin_failed_attempts)
STORAGE_BYTES (homescreen, 1024)
}Storage;
4. 结构内部访问接口
typedef uint32_t size_t;
/* 返回结构体成员变量在结构中的偏移位置, 4字节对齐 */
#define offsetof(st, m) ((size_t)( &((st *)0)->m) )
/* 计算成员变量的大小 */
#define membersize(st, m) (sizeof((st *)0)->m)
/* 获取数据结构所占的内存大小, 4字节对齐 */
#define STORAGE_SIZE(last_member) ( ((offsetof(Storage, last_member) + membersize(Storage, last_member)) + 3) & ~3)
e.g:
size_t storage_size = 0;
storage_size = OLD_STORAGE_SIZE(homescreen);
if (storage_size != sizeof(Storage)) {
/* 数据结构已添加新成员, 处理函数1 */
}
else {
/* 数据结构没变, 处理函数2 */
}
Note: 常用于兼容旧版本消息结构, 新成员只能在最后添加
5. 常用函数实现
/* 获取字符串的字节数, 不计算结束符'\0'nst */
static size_t strlen(const char * s)
{
size_t rc = 0;
while (s[rc]) {
++rc;
}
return rc;
}
/* 拷贝指定长度的数据 */
static void * memcpy(void *s1, const void *s2, size_t n)
{
char *dest = (char *)s1;
const char *src = (const char *)s2;
while (n--) {
*dest++ = *src++;
}
return s1;
}
/* 将指定长度地址数据初始化为指定值 */
static void * memset(void *s, int c, size_t n)
{
unsigned char *p = (unsigned char *)s;
while (n--) {
*p++ = (unsigned char)c;
}
return s;
}
6. 关键字用法
-
const
优点:
1. 定义 const 常量, 具有不可变性.
2. 便于进行类型检查, 消除隐患.
3. 保护被修饰的东西, 防止意外的修改, 增强程序的健壮性.
4. 为函数重载提供参考.
5. 节省空间, 避免不必要的内存分配.
e.g:
#define PI 3.14159 //常量宏
const double Pi = 3.14159; //const 修饰常量, 此时未将 Pi 放入 ROM 中
double I = PI; //预编译期间宏替换, 分配内存
double i = Pi; //此时为Pi 分配内存, 以后不再分配
double J = PI; //再进行宏替换, 又一次分配空间
double j = Pi; //没有内存分配 -
修饰常量
int const x = 0;
const int x = 0; -
修饰常数组
int const a[3] = {1, 2, 3};
const int a[3] = {1, 2, 3}; -
修饰常指针
const int *p; //const 修饰指向的对象, p 可变, p 指向的对象不可变
int const *p; //同上
int *const p; //const 修饰指针 p, p 不可变, p 指向的对象可变
const int *const p; //指针 p 和 p 指向的对象均不可变 -
修饰常引用
const double &var; //该引用所引用的对象不能被更新 -
修饰函数常参数
void func(const int var); //告诉编译器 var 在函数体中无法改变, 防止使用者的一些无意或错误的修改 -
修饰函数的返回值
const int func(); //表示函数返回值不可被改变