-
背景
在C语言中常有数字大小端问题要处理,而在C++中,可以使用泛型编程,较为方便地解决这个问题,C语言虽然也有泛型的关键字_Generic,但C语言的泛型相较C++而言,功能相差较大。因此本文讨论在C语言下去实现数字反转大小端问题。
1. C++的延伸
-
从C++泛型角度来看,从泛型角度入手是个不错的选择。
void ReverseEndian(void *buf, uint8_t len) { uint8_t *pArr = buf; for (int i = 0, j = len - 1; i < j; i++, j--) { pArr[i] ^= pArr[j]; pArr[j] ^= pArr[i]; pArr[i] ^= pArr[j]; } } #define REVERSE_ENDIAN1(a) _Generic((a), \ uint16_t : ReverseEndian((void *), &a, sizeof(uint16_t)), \ uint32_t : ReverseEndian((void *), &a, sizeof(uint32_t)), \ uint64_t : ReverseEndian((void *), &a, sizeof(uint64_t)), \ int16_t : ReverseEndian((void *), &a, sizeof(int16_t)), \ int32_t : ReverseEndian((void *), &a, sizeof(int32_t)), \ int64_t : ReverseEndian((void *), &a, sizeof(int64_t)), \ float : ReverseEndian((void *), &a, sizeof(float)), \ double : ReverseEndian((void *), &a, sizeof(double)), \ )到这里,其实可以初步实现反转大小端问题,但是有两个问题,一个是此处直接修改a的值,使用&a指针来进行重新排列bytes,对于宏函数来说不能直接传入字面数字和其他传值形式;另一个问题是sizeof($type),可以使用sizeof(a)来代替,但是这样各种类型的函数实现就一致了,此处使用泛型变得没有意义。
2. C语言泛型的变化
-
针对上面的问题作出优化,这里不再使用泛型关键字,宏函数采用返回值的方式来避免一些传值问题,也就需要使用推导类型关键字typeof。
void ReverseEndian(void *buf, uint8_t len) { uint8_t *pArr = buf; for (int i = 0, j = len - 1; i < j; i++, j--) { pArr[i] ^= pArr[j]; pArr[j] ^= pArr[i]; pArr[i] ^= pArr[j]; } } #define REVERSE_ENDIAN2(a) ({ \ typeof(a) _a = (a); \ ReverseEndian((void *)&_a, sizeof(_a)); \ _a; \ }) -
这里也可以使用纯宏函数的方式实现
#define REVERSE_ENDIAN3(a) ({ \ typeof(a) _a = (a); \ uint8_t *pArr = (uint8_t *)&_a; \ for (int i = 0, j = sizeof(_a) - 1; i < j; i++, j--) { \ pArr[i] ^= pArr[j]; \ pArr[j] ^= pArr[i]; \ pArr[i] ^= pArr[j]; \ } \ _a; \ })
3. 总结
- C语言的泛型确实使用起来确实有较多的限制,typeof和_Generic这两个关键字的组合使用,跟接近泛型。
- 我之前在C语言中处理反转大小端问题较为头疼,同事中有采用for循环来颠倒拷贝数组方式来处理这个问题,较为繁琐,使用一个宏来实现所有类型反转大小端较为一致友好。
949






