6

我正在嵌入式目标中编写 C 语言。由于日益增加的复杂性和可测试性问题,模块化是必须的。

乍一看,程序是一个控制循环。使用内部硬件读取物理输入,应用一些计算,并应用计算输出。然而,控制是相当复杂的,并且具有许多内部状态和变化的变量。

该控件分为不同的模块,捆绑了不同状态的功能。常见的任务/计算在单独的模块中提供,并在不同的模块中调用,以保持自己干燥。对于整个项目的枚举和类型一致性,使用了一个顶级 .H 文件(就我而言,因为继承等 OO 策略在我的框架中不是一个选项)。

在决定如何将变量传入和传出模块时,我的问题就出现了。

我最初的做法是:

mymodule.H:

struct Inputs{
    int input1;
    ...
    int inputN;
}

struct Outputs{
    int output1;
    ...
    int outputN;
}

void mymodule(Inputs in,Outputs* out);

并且在主函数(或调用此模块的模块)中创建了“输入”和“输出”类型的结构。

然后,将变量复制到 Inputs 结构体,调用函数(引用 Outputs 结构体),一旦完成,该结构体的内容将用于进一步计算。

但是,这会导致占用大量内存,因为每个模块都需要在调用模块中创建 InputType 和 OutputType 的实例。在我看来,这不是一个优雅的解决方案。顺便说一句,我的项目中不允许动态分配。

您能否为我提供一些指导方针和/或替代想法以找到一个好的解决方案?

谢谢你。

添加

解决方案之一可能是将 InputStruct 也作为指针传递,但由于它们是模块的有效输入,我如何确保它们不会在代码中被修改?

添加

顺便说一句,出现的另一个问题是并非所有模块都接收相同的变量,并且没有可用的继承机制(因为这是 C),每个模块的结构都必须加载正确的值。我很迷茫...

4

3 回答 3

5

您不必因将参数传入和传出函数而接受大量内存占用。关键是通过引用传递参数,并使用const关键字确保输入不被修改。一个众所周知的例子是:

int strcpy(char *destination, const char *source);

其中只有指向源和目标字符缓冲区的指针被传入,而不是缓冲区的副本,但const关键字阻止 strcpy() 修改源缓冲区的内容。

如果您有太多参数而无法单独传递每个参数,那么请务必将指向结构的指针传递给您的函数,再次使用const关键字来保证不修改输入:

int myFunc(struct myFuncOut *outputs, const struct myFuncIn *inputs);

inputs因为结构体是通过引用传递的,所以 myFunc() 将在调用函数使用的同一块内存上进行操作(但由于关键字,它无法写入指向的内存const),因此传递它的内存开销到函数的只是指针的那个,在典型的嵌入式系统上是四个字节,并且没有复制开销。


至于您的第二个隐含问题,一个函数的输出需要作为输入传递给另一个函数,但参数列表并不相同,除了从一个结构复制到另一个结构之外,您可能无能为力。如果你幸运的话,你也许可以做这样的事情:

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 以及用于附加参数的第二个结构传递给第二个函数。额外的内存成本是一个指针。也许您可以将数据组织成逻辑组,每个组由一个结构表示,这样就没有太多的结构,但没有结构包含在使用该结构的其余部分时并不总是需要的字段?


顺便说一句,您的一条评论似乎暗示您希望结构的定义在可执行文件中具有内存开销。这不是真的。仅当分配结构的实例时才使用内存。

于 2013-04-11T07:44:20.043 回答
1

模块.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;
}

模块.h:

#define N 10

struct Inputs {
    int input[N];
};

struct Outputs {
    int output[N];
};


struct Inputs *getInputs();
struct Inputs *getOutputs();
void mymodule();
于 2013-04-10T12:00:42.060 回答
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 */

顺便说一句,这个答案建立在对这个问题的回答的基础上,其中还包含一些关于让所有这些函数内联的讨论。

于 2013-04-10T13:57:35.420 回答