来源:公众号【鱼鹰谈单片机】
作者:鱼鹰Osprey
ID :emOsprey
这周课上介绍断言实现的时候,莫名其妙被断言的真真假假搞晕了。
起因是看到一段关于判断一段字符串的长度,使用了 ASSERT 方式,原文大概是这样写的:
extern void Assert_Failed(uint8_t *func, uint32_t line);
void func()
{
ASSERT(sizeof(AES256_KEY) != 32);
ASSERT(sizeof(AES256_IV) != 16);
}
一开始总以为这种写法没问题,毕竟这么简单,模拟调试的时候确实也不会报错(打开了断言情况下)。
为了课上测试一下静态编译报错的效果,却发现始终有些问题,不该报错的时候报错了。
为了方便,鱼鹰直接用 MDK 测试了 sizeof 字符串的结果,发现确实也是 16(0x10)
然后我就困在了这两个前提上,STATIC_ASSERT 报错始终有问题。
extern void Assert_Failed(uint8_t *func, uint32_t line);
void func()
{
ASSERT(sizeof(AES256_KEY) != 32);
ASSERT(sizeof(AES256_IV) != 16);
STATIC_ASSERT(sizeof(AES256_IV) != 16);
}
调试的时候,STATIC_ASSERT 报错,但是 ASSERT 不报错。
这让鱼鹰百思不得其解,一开始以为两个宏逻辑相反,不都是逻辑值为假时报错。
但是通过分析宏发现,都是假的时候报错。
那肯定哪里分析有问题了。
因为编译器如果能得到结果为真,ASSERT 可能无法形成代码,即为空(void)0U。
因此鱼鹰使用了一个变量作为中间值,强行让编译器生成断言相关的代码,才算是发现了问题。
void func()
{
uint32_t size = sizeof(AES256_IV);
ASSERT(sizeof(AES256_KEY) != 32);
ASSERT(size != 16);
STATIC_ASSERT(sizeof(AES256_IV) != 16);
}
这个size 的值竟然是 17,而不是 16,难怪初始代码不报错,17 != 16,当然为真,当然不报错。
但写代码的人是希望这个字符串的长度为始终是16(不包含null的情况下),而明显代码中使用sizeof 时计算了 null 的长度。
所以代码应该这样写才对:
void func()
{
ASSERT(sizeof(AES256_KEY) == 32);
ASSERT(sizeof(AES256_IV) == 16);
STATIC_ASSERT(sizeof(AES256_IV) == 16);
}
但是又因为代码的sizeof 会计算null,因此需要去除这个数:
void func()
{
ASSERT(sizeof(AES256_KEY) - 1 == 32);
ASSERT(sizeof(AES256_IV) - 1 == 16);
STATIC_ASSERT(sizeof(AES256_IV) - 1 == 16);
}
这样 STATIC_ASSERT 可以在编译阶段就可以判断这个字符串的长度是否符合要求,多一个、少一个字符都不行。
这个坑你们遇到过吗?