C中的数据封装
Data encapsulation in 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 个值。我希望这些数据无法从我的组件的 "outside" 访问(只能通过我组件中的特殊功能)。
我不确定如何在这里进行。我真的需要让数组成为我的结构的一部分吗?我想到的是执行以下操作:
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 指针。
这是唯一的方法吗?你知道更好的方法吗?
谢谢。
I would like this data to be non-accessible from the "outside" of my
component (only through special functions in my component).
你可以这样做(包括数据在内的一大malloc
):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.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;
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 中的结构通常在 header 中完全定义,尽管它们完全不透明(例如 FILE
),或者仅在文档中指定了它们的某些字段.
C 缺少 private
来防止意外访问,但我认为这是一个小问题:如果规范中未提及某个字段,为什么有人要尝试访问它?您是否曾经不小心访问过 FILE
的成员? (最好不要做像发布成员 foo
和 non-published fooo
这样的事情,它们很容易被一个小的打字错误访问。)有些使用约定,比如给他们 "unusual" 名称,例如,在私有成员上有尾随下划线。
另一种方式是PIMPL idiom: Forward-declare结构作为不完整的类型,只在实现文件中提供完整的声明。这可能会使调试复杂化,并且由于内联和附加间接的可能性较小,可能会导致性能下降,尽管这可能可以通过 link-time 优化来解决。两者的组合也是可能的,在 header 中声明 public 字段以及指向包含私有字段的不完整结构类型的指针。
对于为微控制器编写硬件驱动程序的具体情况,请考虑 。
否则,使用opaque/incomplete类型。您会惊讶地发现,知道如何实际实现自定义类型的 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 函数,而不是使用真正的 constructors/destructors.
您可以像这样将结构的一部分设为私有。
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
是 struct object
中的第一项。因此 object.c 之外的代码将通过指向 object_public
的指针引用该对象。 object.c 中的代码通过指向 object
的指针引用对象。只有 object.c 中的代码会知道私有成员。
程序不应定义或分配实例 object_public
因为该实例不会附加私有内容。
将结构作为第一项包含在另一个结构中的技术确实是在 C 中实现单继承的一种方法。我不记得曾经像这样使用它进行封装。但我想我会把这个想法扔在那里。
您可以:
- 使您的整个
ads1248_options_t
成为不透明类型(如其他答案中已讨论的那样)
仅将 adc_values
成员设为不透明类型,例如:
// in the header(.h)
typedef struct adc_values adc_values_t;
// in the code (.c)
struct adc_values {
int32_t *values;
};
为您的 ads1248_options_t
设置一个值数组的静态数组 "parallel" 并提供访问它们的函数。喜欢:
// 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
)
您可以提供一些其他分配值数组的方法,而不是静态数组 "in parallel",但是,同样,需要一种方法来识别哪个数组属于哪个 ADC,其中它 id
是最简单的解决方案。
我目前正在开发一个嵌入式系统,我在板上有一个组件出现了两次。我想为组件准备一个 .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 个值。我希望这些数据无法从我的组件的 "outside" 访问(只能通过我组件中的特殊功能)。
我不确定如何在这里进行。我真的需要让数组成为我的结构的一部分吗?我想到的是执行以下操作:
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 指针。
这是唯一的方法吗?你知道更好的方法吗?
谢谢。
I would like this data to be non-accessible from the "outside" of my component (only through special functions in my component).
你可以这样做(包括数据在内的一大malloc
):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.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;
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 中的结构通常在 header 中完全定义,尽管它们完全不透明(例如 FILE
),或者仅在文档中指定了它们的某些字段.
C 缺少 private
来防止意外访问,但我认为这是一个小问题:如果规范中未提及某个字段,为什么有人要尝试访问它?您是否曾经不小心访问过 FILE
的成员? (最好不要做像发布成员 foo
和 non-published fooo
这样的事情,它们很容易被一个小的打字错误访问。)有些使用约定,比如给他们 "unusual" 名称,例如,在私有成员上有尾随下划线。
另一种方式是PIMPL idiom: Forward-declare结构作为不完整的类型,只在实现文件中提供完整的声明。这可能会使调试复杂化,并且由于内联和附加间接的可能性较小,可能会导致性能下降,尽管这可能可以通过 link-time 优化来解决。两者的组合也是可能的,在 header 中声明 public 字段以及指向包含私有字段的不完整结构类型的指针。
对于为微控制器编写硬件驱动程序的具体情况,请考虑
否则,使用opaque/incomplete类型。您会惊讶地发现,知道如何实际实现自定义类型的 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 函数,而不是使用真正的 constructors/destructors.
您可以像这样将结构的一部分设为私有。
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
是 struct object
中的第一项。因此 object.c 之外的代码将通过指向 object_public
的指针引用该对象。 object.c 中的代码通过指向 object
的指针引用对象。只有 object.c 中的代码会知道私有成员。
程序不应定义或分配实例 object_public
因为该实例不会附加私有内容。
将结构作为第一项包含在另一个结构中的技术确实是在 C 中实现单继承的一种方法。我不记得曾经像这样使用它进行封装。但我想我会把这个想法扔在那里。
您可以:
- 使您的整个
ads1248_options_t
成为不透明类型(如其他答案中已讨论的那样) 仅将
adc_values
成员设为不透明类型,例如:// in the header(.h) typedef struct adc_values adc_values_t; // in the code (.c) struct adc_values { int32_t *values; };
为您的
ads1248_options_t
设置一个值数组的静态数组 "parallel" 并提供访问它们的函数。喜欢:// 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
. 中保留一个索引 (您可以提供一些其他分配值数组的方法,而不是静态数组 "in parallel",但是,同样,需要一种方法来识别哪个数组属于哪个 ADC,其中它
id
是最简单的解决方案。
id
)