C语言中的数据封装

C语言中的数据封装,c,struct,embedded,malloc,C,Struct,Embedded,Malloc,我目前在一个嵌入式系统上工作,我在一块板上有一个组件出现了两次。我想要一个组件的.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; 这些都是硬件设置。我创建了这个结构的两个实例(

我目前在一个嵌入式系统上工作,我在一块板上有一个组件出现了两次。我想要一个组件的.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个值。我希望此数据不能从组件的“外部”访问(只能通过组件中的特殊功能访问)

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

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\u t*adc\u值;//
我希望此数据不能从我的“外部”访问
组件(仅通过我的组件中的特殊功能)

您可以这样做(一个大的
malloc
包括数据):

#包括
#包括
#包括
类型定义结构{
uint32引脚复位;
uint32引脚图;
uint32引脚启动;
易失性avr32_spi_t*spi_模块;
uint8_t cs_id;
}ads1248_选项;
无效fn(ads1248_选项_t*x)
{
int32_t*值=(int32_t*)(x+1);
/*无法通过结构的成员访问值*/
数值[0]=10;
printf(“%d\n”,值[0]);
}
内部主(空)
{
ads1248_options_t*x=malloc(sizeof(*x)+(sizeof(int32_t)*100));
fn(x);
免费(x);
返回0;
}

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

C缺少代码>私有< /代码>以防止意外访问,但我认为这是一个小问题:如果在规范中没有提到一个字段,为什么有人试图访问它?您是否曾经意外访问过

文件的成员?(可能最好不要让已发布的成员
foo
和未发布的
fooo
通过一个小字体就可以轻松访问。)有些人使用约定,比如给他们起“不寻常”的名字,例如,在私有成员上有一个尾随下划线


另一种方法是:将结构声明为不完整类型,并仅在实现文件中提供完整声明。这可能会使调试复杂化,并且可能会由于内联和附加间接寻址的可能性较小而导致性能下降,尽管这可以通过链路时间优化来解决。两者的组合也是可能的,在标头中声明公共字段,以及指向私有字段的不完整结构类型的指针。

< p>对于为微控制器编写硬件驱动程序的特定情况,这似乎是,请考虑。 否则,请使用不透明/不完整类型。您会惊讶地发现,知道如何真正实现自定义类型的100%私有封装的C程序员少得可怜。这就是为什么人们一直认为C缺乏被称为私有封装的OO特性的原因。这个神话起源于缺乏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中的代码无法访问任何结构成员,所有成员都是100%私有的。它只能创建指向结构对象的指针,而不能创建其实例。与C++中的抽象基类完全一样,如果你熟悉它的话。唯一的区别是您必须手动调用init/destroy函数,而不是使用真正的构造函数/析构函数。

您可以像这样将结构的一部分设置为私有

对象.h

struct object_public {
    uint32_t public_item1;
    uint32_t public_item2;
};
对象c

struct object {
    struct object_public public;
    uint32_t private_item1;
    uint32_t *private_ptr;
}
指向
对象
的指针可以强制转换为指向
对象公用
的指针,因为
对象公用
结构对象
中的第一项。因此object.c之外的代码将通过指向
object\u public
的指针引用该对象。而object.c中的代码通过指向
object
的指针引用对象。只有object.c中的代码才知道私有成员

程序不应该定义或分配实例
object\u public
,因为该实例不会附加私有内容

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

你可以:

  • 将您的整个
    ads1248\u选项\u t
    设置为不透明类型(如其他答案中所述)
  • 仅将
    adc\u值
    成员设置为不透明类型,如:

     // in the header(.h)
    typedef struct adc_values adc_values_t;
    
     // in the code (.c)
    struct adc_values { 
        int32_t *values;
    };
    
  • 拥有一个与您的
    ads1248\u选项\u 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\u选项中保留索引(
    id

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


  • 标准警告:请输入
    malloc()
    C
    @JoachimPileborg中的family的返回值,为什么无效?为什么不使用不完整的结构类型?如果您使结构不透明,那么只需使用特殊函数在结构中设置字段。使用不透明指针封装您的
    struct
    ,如@TomL。既然你的程序无论如何都必须能够处理最坏的情况,那为什么你不总是为4个IC预留空间呢?这难道不是我想要实现的相反吗?我的主要功能应该是免费的
    // 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]
    }