0
点赞
收藏
分享

微信扫一扫

在C中使用单个成员结构的两种方法


我喜欢C的结构。 C中有很多奇怪的东西,但是在大多数情况下,结构是可预测的,有用的,易于理解的。
对于不熟悉C的人,结构体是数据集合。结构的一个例子是笛卡尔平面上的一个点:

struct point {

int x;

int y;

};


通常,结构体用于关联两个或更多个数据。正如标题所示,我将演示为什么你可能想要使用一个单一元素的结构体。
保留数组类型信息


与结构体不同,C的数组很麻烦,有许多令人惊讶的角落,很难让初学者与指针区分开来(这已经很难自己理解了)。我发现最令人困扰的是C的数组,只要它们在一个函数或模块边界被引用时,就放弃它们的大小信息。
我们来看几个例子,使用C的sizeof运算符。首先,我们将介绍数组不令人吃惊的行为。在这个例子中,我们分配数组,并通过它被声明的名称引用它。

void direct_reference() {

uint8_t example_array[10];

printf("%lu\n", sizeof(example_array));

}



int main() {

direct_reference();

return 0;

}


这个例子,当运行时,打印值'10',我们期望的。如果我们用这个称为indirect_reference的新函数替换函数direct_reference,我们看到不同的行为。

void indirect_reference(uint8_t referenced_array[10]) {

printf("%lu\n", sizeof(referenced_array));

}

int main() {

uint8_t example_array[10];

indirect_reference(example_array);



return 0;

}



在我的64位机器上,此示例打印值“8”,这恰好是我系统上指针的大小。请注意,sizeof将references_array视为一个指针,即使我们已经明确地告诉了这个引用的类型是什么。
幸运的是,我们可以使用单个成员的结构来跨参考边界保留这个大小信息!这是一个例子。

struct array_wrapper {

uint8_t array[10];

};

void indirect_reference(struct array_wrapper * a) {

printf("%lu\n", sizeof(a->array));

}

int main() {

struct array_wrapper ar;

indirect_reference(&ar);



return 0;

}


在这个例子中,我们定义一个具有单个字段的结构 - 数组。有意思的是,如果我们传递对struct的引用,并在struct的内部执行一个sizeof,那么数组的大小就会被保留 - 甚至跨引用边界。该结构保留其成员的所有大小信息。运行这个代码打印出值'10',就像我们预料的那样。当使用外部引用(使用“extern”)时,此行为也是一致的。编辑:reddit.com上的用户“nooneofnote”指出,通过使用typedef而不是包装结构可以实现相同的大小保留效果。
防止强制不需要的类型C提供了一种可以重命名类型的机制。 typedef关键字给了我们一种使count_t类似于int32_t下面的类型的方法。这对于语义很好,但它并没有给出任何额外的编译时间安全性。没有什么可以阻止你,例如,定义一个名为seconds的类型和另一个名为milliseconds的类型,然后意外地将它们添加在一起!在这两种情况下,C都会高兴地看到typedef,看到这两种类型都是整数,并将它们添加在一起!这当然可能产生无意义的结果。如果您添加5毫秒至10秒,则最终的值为15秒或毫秒。这显然不是所期望的行为。你可能已经猜到了,我们可以使用另一个单一的成员结构来给我们一个typedef单独的编译时安全性呢!在下面的示例中,我们可以看到错误地添加了毫秒和秒的情况。

typedef uint32_t seconds_t;



typedef uint32_t milliseconds_t;

int main() {

seconds_t x = 10;

milliseconds_t y = 20;



// oops! seconds_t result = x + y;

printf("seconds: %u\n", result);

}


我们可以通过做两件事来提供更多的安全性。首先,不用使用typedef,我们可以将值包装在一个结构体中!其次,我们可以定义一个对我们执行第二次添加的功能。这是我们的扩展示例。


struct seconds { uint32_t val; };

struct milliseconds { uint32_t val; };



struct seconds add_seconds(struct seconds a, struct seconds b) {

return (struct seconds) { a.val + b.val };

}

int main() {

struct seconds x = { 10 };

struct milliseconds y = { 20 };



// oops!

struct seconds result = add_seconds(x, y);

printf("seconds: %u\n", result.val);

}


在这个例子中,我们将包含在结构中的整数传递给一个负责将它们加在一起的函数。你可能可以说,这个例子不会编译!当我尝试构建这个例子时,我的编译器给了我以下结果:
error: passing 'struct milliseconds' to parameter of incompatible type 'struct seconds'
  struct seconds result = add_seconds(x, y);
我们所做的是创建一个情况,编译器可以更积极地发现我们的错误。 通过携带其他结构类型,我们可以帮助减少混合编译器通常认为可互操作的不同类型时所犯的错误。

举报

相关推荐

0 条评论