C中的数据封装

我目前正在研究一个嵌入式系统,我在电路板上有一个组件,它出现了两次。 我想为组件提供一个.c和一个.h文件。

我有以下代码:

typedef struct { uint32_t pin_reset; uint32_t pin_drdy; uint32_t pin_start; volatile avr32_spi_t *spi_module; uint8_t cs_id; } ads1248_options_t; 

这些都是硬件设置。 我创建了这个结构的两个实例(每个部分一个)。

现在我需要在后台保留一组值。 例如,我可以每秒从该设备读取值,并且我想保留最后100个值。 我希望这些数据不能从我的组件的“外部”访问(仅通过我的组件中的特殊function)。

我不确定如何在这里继续。 我真的需要将数组作为结构的一部分吗? 我想到的是做以下事情:

 int32_t *adc_values; // <-- Add this to struct int32_t *adc_value_buffer = malloc(sizeof(int32_t) * 100); // <-- Call in initialize function, this will never be freed on purpose 

然而,我将能够从我的代码中的任何地方(也来自我的组件外部)访问我的int32_t指针,这是我不喜欢的。

这是唯一的方法吗? 你知道更好的方法吗?

谢谢。

对于为微控制器编写硬件驱动程序的具体情况,请考虑这样做 。

否则,请使用opaque / incomplete类型 。 你会惊讶地发现,很少有C程序员知道如何实际实现自定义类型的100%私有封装。 这就是为什么有一些关于C缺乏OOfunction的持久性神话,称为私有封装。 这个神话源于缺乏C知识,而不是其他任何东西。

这是怎么回事:

ads1248.h

 typedef struct ads1248_options_t ads1248_options_t; // incomplete/opaque type ads1248_options_t* ads1248_init (parameters); // a "constructor" void ads1248_destroy (ads1248_options_t* ads); // a "destructor" 

ads1248.c

 #include "ads1248.h" struct ads1248_options_t { uint32_t pin_reset; uint32_t pin_drdy; uint32_t pin_start; volatile avr32_spi_t *spi_module; uint8_t cs_id; }; ads1248_options_t* ads1248_init (parameters) { ads1248_options_t* ads = malloc(sizeof(ads1248_options_t)); // do things with ads based on parameters return ads; } void ads1248_destroy (ads1248_options_t* ads) { free(ads); } 

main.c中

 #include "ads1248.h" int main() { ads1248_options_t* ads = ads1248_init(parameters); ... ads1248_destroy(ads); } 

现在main中的代码无法访问任何struct成员,所有成员都是100%私有的。 它只能创建一个指向struct对象的指针,而不是它的实例。 如果你熟悉它,就像C ++中的抽象基类一样工作。 唯一的区别是你必须手动调用init / destroy函数,而不是使用真正的构造函数/析构函数。

我希望这些数据不能从我的组件的“外部”访问(仅通过我的组件中的特殊function)。

你可以这样做(包括数据的大型malloc ):

 #include  #include  #include  typedef struct { uint32_t pin_reset; uint32_t pin_drdy; uint32_t pin_start; volatile avr32_spi_t *spi_module; uint8_t cs_id; } ads1248_options_t; void fn(ads1248_options_t *x) { int32_t *values = (int32_t *)(x + 1); /* values are not accesible via a member of the struct */ values[0] = 10; printf("%d\n", values[0]); } int main(void) { ads1248_options_t *x = malloc(sizeof(*x) + (sizeof(int32_t) * 100)); fn(x); free(x); return 0; } 

通常,C中的结构完全定义在标题中,尽管它们完全不透明(例如FILE ),或者只有文档中指定的某些字段。

C缺少private来防止意外访问,但我认为这是一个小问题:如果规范中没有提到某个字段,为什么有人试图访问它? 您是否偶然访问过FILE的成员? (最好不要做一些事情,比如拥有一个已发布的成员foo和一个可以通过一个小错字轻松访问的未发布的fooo 。)有些人使用惯例,例如给它们“不寻常”的名字,例如,有一个尾随的下划线私人会员。

另一种方式是PIMPL习语 :将结构转发声明为不完整类型,并仅在实现文件中提供完整的声明。 这可能使调试复杂化,并且由于内联和附加间接的可能性较小而可能具有性能损失,尽管这可以通过链路时间优化来解决。 两者的组合也是可能的,声明标题中的公共字段以及指向保存私有字段的不完整结构类型的指针。

您可以将结构的一部分设为私有。

object.h

 struct object_public { uint32_t public_item1; uint32_t public_item2; }; 

object.c

 struct object { struct object_public public; uint32_t private_item1; uint32_t *private_ptr; } 

指向object的指针可以object_public转换为指向object_public的指针,因为object_publicstruct object的第一个项目。 所以object.c之外的代码将通过指向object_public的指针引用该对象。 object.c中的代码通过指向object的指针引用object 。 只有object.c中的代码才能知道私有成员。

程序不应定义或分配实例object_public因为该实例不会附加私有内容。

将结构作为第一项包含在另一个结构中的技术实际上是一种在C中实现单inheritance的方法。我不记得曾经像这样使用它进行封装。 但我想我会把这个想法抛在那里。

您可以:

  1. 使您的整个ads1248_options_t成为不透明类型(已在其他答案中讨论)
  2. adc_values成员设为opaque类型,例如:

      // in the header(.h) typedef struct adc_values adc_values_t; // in the code (.c) struct adc_values { int32_t *values; }; 
  3. 拥有一个与您的ads1248_options_t “平行”的值数组的静态数组,并提供访问它们的函数。 喜欢:

     // in the header (.h) int32_t get_adc_value(int id, int value_idx); // in the code (.c) static int32_t values[MAX_ADS][MAX_VALUES]; // or static int32_t *values[MAX_ADS]; // malloc()-ate members somewhere int32_t get_adc_value(int id, int value_idx) { return values[id][value_idx] } 

    如果用户不知道要使用的索引,请在ads1248_options_t保留索引( id )。

  4. 您可以提供一些“并行”分配值数组的其他方式来代替静态数组,但是,需要一种方法来识别哪个数组属于哪个ADC,其id是最简单的解决方案。