我在嵌入式目标中编写C语言。由于日益复杂和可测试性问题,模块化是必须的。
一目了然,该程序是一个控制循环。使用内部硬件读取物理输入,应用一些计算并应用计算输出。但是,控件非常复杂,并且具有许多内部状态和变化的变量。
该控件分为不同的模块,捆绑不同状态的功能。常见的任务/计算在不同的模块中提供,并在不同的模块中调用,以保持自己的干燥。对于整个项目中的枚举和类型一致性,使用了一个顶级.H文件(就我而言,在我的框架中,诸如继承之类的OO策略不是一个选项。)
在决定如何向模块传递变量和从模块传递变量时,我的问题出现了。
我最初的做法是:
mymodule.H:
struct Inputs{
int input1;
...
int inputN;
}
struct Outputs{
int output1;
...
int outputN;
}
void mymodule(Inputs in,Outputs* out);
在main函数(或调用此模块的模块)中创建了“Inputs”和“Outputs”类型的结构。
然后,将变量复制到Inputs结构中,调用该函数(引用Outputs结构),一旦完成,该结构的内容将用于进一步的计算。
但是,这会导致大量内存占用,因为每个模块都需要在调用模块中创建InputType和OutputType实例。在我看来,这不是一个优雅的解决方案。顺便说一下,我的项目中不允许动态分配。
您能否提供一些指导方针和/或其他想法以获得良好的解决方案?
谢谢。
加
其中一个解决方案可能是将InputStruct也作为指针传递,但由于它们是模块的有效输入,我怎么能保证它们不会在代码中被修改?
加
顺便说一下,出现的另一个问题是,并非所有模块都接收相同的变量,并且没有可用的继承机制(因为这是C),每个模块的结构都必须加载适当的值。我很混淆......
答案 0 :(得分:5)
您不必通过传入和传出函数来接受大量内存占用。关键是通过引用传递参数,并使用const
关键字来确保不修改输入。一个众所周知的例子是:
int strcpy(char *destination, const char *source);
其中只传入指向源和目标字符缓冲区的指针,而不是缓冲区的副本,但const
关键字阻止strcpy()修改源缓冲区的内容。
如果您有这么多参数,那么单独传递每个参数都是不切实际的,那么一定要将结构指针传递给您的函数,再次使用const
关键字来保证输入不被修改:
int myFunc(struct myFuncOut *outputs, const struct myFuncIn *inputs);
因为struct是通过引用传递的,myFunc()将在调用函数使用的相同内存上运行(但是由于{{inputs
,它将无法写入const
指向的内存。 1}}关键字),所以将它传递给函数的内存开销只是指针的内存开销,在典型的嵌入式系统上是四个字节,并且没有复制开销。
至于你的第二个隐含问题,一个函数的输出需要作为输入传递给另一个函数,但参数列表不相同,除了从一个结构复制到另一个结构之外,你可能做的事情可能不多。另一个。如果你很幸运,你可能会做这样的事情:
struct myFunc1Out
{
int common1;
int common2;
int common3;
};
struct myFunc2In
{
int common1;
int common2;
int common3;
int special1;
int special2;
}
struct myFunc2In data;
myFunc1((struct myFunc1Out *)(*data), *inputs);
data.special1 = 1;
data.special2 = 2;
myFunc2(*outputs, *data);
你看到这里发生了什么吗? struct myFunc2In的第一部分与struct myFunc1Out相同,因此您可以将前者转换为后者,并且将忽略额外的字段。你可以把它想象成一个(非常)穷人的多态性。
另一种可能不那么模糊的方法是将结构myFunc1Out传递给第二个函数以及附加参数的第二个结构。额外的内存成本是一个指针。也许您可以将数据组织成逻辑组,每个逻辑组由一个结构体表示,这样结构就没有太多,但是没有结构包含在使用该结构的其余部分时并不总是需要的字段?
顺便说一下,你的一条评论似乎暗示你期望结构的定义在可执行文件中有内存开销。这不是真的。仅在分配了结构的实例时才使用内存。
答案 1 :(得分:1)
的module.c:
#include "module.h"
struct Inputs *getInput() {
static struct Inputs inputs;
return &inputs;
}
struct Outputs *getOutput() {
static struct Outputs outputs;
return &outputs;
}
struct Outputs *mymodule() {
struct Outputs *o = getOutput();
struct Inputs *i = getInput();
o->output[0] = i->input[0];
return o;
}
module.h中:
#define N 10
struct Inputs {
int input[N];
};
struct Outputs {
int output[N];
};
struct Inputs *getInputs();
struct Inputs *getOutputs();
void mymodule();
答案 2 :(得分:0)
一种可能性是隐藏setter / getter函数后面的变量,然后使用预处理器来控制模块中这些函数的可见性。
iovars.c:
/* iovars.c */
static int s_input1;
static int s_input2;
static int s_output1;
static int s_output2;
int GetIn1(void) { return s_input1; }
int GetIn2(void) { return s_input2; }
void SetIn1(int value) { s_input1 = value; }
void SetIn2(int value) { s_input2 = value; }
int GetOut1(void) { return s_output1; }
int GetOut2(void) { return s_output2; }
void SetOut1(int value) { s_output1 = value; }
void SetOut2(int value) { s_ouput2 = value; }
iovars.h:
/* iovars.h */
#ifdef USING_IN1_READONLY || USING_IN1_READWRITE
int GetIn1(void);
#ifdef USING_IN1_READWRITE
void SetIn1(int value);
#endif
#endif
#ifdef USING_IN2_READONLY || USING_IN2_READWRITE
int GetIn2(void);
#ifdef USING_IN2_READWRITE
void SetIn2(int value);
#endif
#endif
#ifdef USING_OUT1_READONLY || USING_OUT1_READWRITE
int GetOut1(void);
#ifdef USING_OUT1_READWRITE
void SetOut1(int value);
#endif
#endif
#ifdef USING_OUT2_READONLY || USING_OUT2_READWRITE
int GetOut2(void);
#ifdef USING_OUT2_READWRITE
void SetOut2(int value);
#endif
#endif
确定这有点单调乏味,但您现在可以在模块中按变量控制变量的可见性和可写性:
/* moduleA.c */
#define USING_IN1_READONLY
#define USING_OUT1_READWRITE
#include "iovars.h"
/* code in this module can only see functions to read input 1
and to read or write output 1 */
顺便说一句,这个答案建立在对this question的回复的基础之上,其中还包含了一些关于将所有这些函数内联到内联的讨论。