假设我们有以下复杂的 struct (在C中).它包括固定长度的数组、不同大小的成员、枚举和其他 struct .此 struct 未打包,无法打包.

struct {
    uint8_t smallNum;
    /* uint8_t align0 */
    /* uint8_t align1 */
    /* uint8_t align2 */
    uint32_t arrayOfBigNums[5];
    bool isFalse;
    /* uint8_t align0 */
    /* uint8_t align1 */
    /* uint8_t align2 */
    struct myOtherStruct;
    /* uint8_t align0 */
    enum mySmallEnum;
...
    uint8_t aByte;
    /* uint8_t align0 */
    /* uint8_t align1 */
} myStruct;

为了进行单元测试,我们想用随机数据来初始化这个 struct .测试包括获取该 struct 、将其序列化、将其写入闪存、读取、反序列化,然后判断数据是否没有更改.

或者生成所有可能的值,或者生成一个较小的随机子集并对其进行测试.那么,人们将如何着手做这件事呢?

直接的解决办法如下:

myStruct testData = {0};
myStruct readData = {0};
testData.smallNum = rand();
testData.arrayOfBigNums[0] = rand();
testData.arrayOfBigNums[1] = rand();
....
testData.aByte = rand();
save(&testData);
load(&readData);
int ret = memcmp(&readData, &testData, sizeof(myStruct)); 
// ret == 0. Good!

但该解决方案不可扩展.向 struct 添加另一个字段或增加数组将需要更改单元测试.此外,它将是一个很长的初始化函数,容易出现人为错误.

另一种解决方案是生成一个随机的字节数组,然后将其memcpy写入我们的 struct .虽然这是一个很好的 idea ,但实际上它行不通,因为我们的 struct 没有打包.有些字节现在为0,将来也始终为0.

myStruct testData = {0};
myStruct readData = {0};
uint8_t* randomData = genRandomData(sizeof(myStruct));
memcpy((uint8_t*)&testData, randomData, sizeof(myStruct));
save(&testData);
load(&readData);
int ret = memcmp(&readData, &testData, sizeof(myStruct)); 
// ret != 0 because deserialization will fill the struct properly. 
// Ignoring junk bytes in padding

我目前的工作解决方案是在前一个方案的基础上迭代改进.使用0xff初始化 struct ,将其强制转换为字节数组,并使用所有内容对其进行掩码.但据我所知,这是无害的.

myStruct structMask = {MACRO_TO_FILL_WITH_MANY(-1)};
myStruct testData = {0};
myStruct readData = {0};
uint8_t* randomData = genRandomData(sizeof(myStruct));
memcpy((uint8_t*)&testData, randomData, sizeof(myStruct));
for(size_t i = 0; i < sizeof(myStruct); i++){
    ((uint8_t*)&testData)[i] &= ((uint8_t*)&structMask)[i];
}
save(&testData);
load(&readData);
int ret = memcmp(&readData, &testData, sizeof(myStruct)); 
// ret != 0 because deserialization will fill the struct properly. 
// Ignoring junk bytes in padding

该解决方案也不是最优的,因为它没有考虑枚举限制.像bool(来自stdbool.h)这样的类型被存储为0/1,这很好,但是枚举仍然可以有未定义的值.因此,我们需要手动对所有枚举值取模.

寻找更通用/更健壮的解决方案.

推荐答案

好吧,我其实找到了我自己问题的答案.主要问题是我的 struct 没有初始化式.但是,有there already exist个序列化函数.所以我们可以使用不起作用的第二种解决方案,并将其转换为起作用的解决方案.

因此,一个更干净的解决方案应该是:

myStruct testData = {0};
myStruct readData = {0};
uint8_t* randomData = genRandomData(sizeof(myStruct));
memcpy(&testData, randomData, sizeof(myStruct));
save(&testData);
load(&readData); // readData is now a valid random struct.

myStruct realReadData = {0}
save(&readData);
load(&realReadData);

memcmp(readReadData, readData, sizeof(myStruct)); // This does equal 0 if serialize/deserialize functions are correct.

如果序列化/反序列化函数中存在问题,那么输入 struct (尽管可能是损坏的)应该提供不同的结果.

C++相关问答推荐

librsvg rsvg_handle_get_dimensions获取像素大小与浏览器中的渲染大小没有不同

Tiva TM4C123GXL的I2C通信

我可以在C中声明不同长度数组的数组而不带变量名吗?

在C23中使用_GENERIC实现带有右值的IS_POINTER(P)?

非正规化边缘毛刺

如何使用[BTStack]BLE发送大型(>;2kb)信息包

如何在C语言中正确打印图形

Flose()在Docker容器中抛出段错误

用C语言计算文本文件中的整数个数

为什么我从CSV文件中进行排序和搜索的代码没有显示数据的所有结果?

如何在GET_STRING输入后对少数几个特定字符串进行C判断?

cairo 剪辑区域是否存在多个矩形?

添加函数会 destruct 嵌入式C代码(无IDE)

OSDev--双缓冲重启系统

为什么我的半数组测试和奇数组测试不起作用?(我使用Assert进行调试)

在下面的C程序中,.Ap0是如何解释的?

当我在34mb的.mp4文件中使用FREAD时,我得到了一个分段错误,我如何解决它?

变量的指针右对齐,函数的指针左对齐

如何在 C 中编辑 struct 体中的多个变量

如何在Linux上从控制台左上角开始打印文本?