2013-04-10 25 views
5

Tôi đang lập trình C trong một mục tiêu được nhúng. Do sự phức tạp ngày càng tăng và các vấn đề testability, mô đun là phải.Biến truyền hiệu quả cho các mô-đun trong nền lập trình c được nhúng

Trong nháy mắt, chương trình là vòng điều khiển. Đọc các đầu vào vật lý sử dụng phần cứng nội bộ, áp dụng một số phép tính và áp dụng các kết quả tính toán. Tuy nhiên, việc kiểm soát khá phức tạp, và có nhiều trạng thái bên trong và thay đổi các biến.

Điều khiển này được chia thành các mô-đun khác nhau, bao gồm các chức năng của các trạng thái khác nhau. Các nhiệm vụ/tính toán phổ biến được cung cấp trong các mô-đun riêng biệt và được gọi trong các mô-đun khác nhau, để giữ cho bản thân tôi bị khô. Đối với enum và kiểu nhất quán trên toàn bộ dự án, một tệp .H hàng đầu được sử dụng (như các chiến lược OO như kế thừa không phải là một tùy chọn trong khung của tôi, theo như tôi quan tâm).

Sự cố của tôi nảy sinh khi quyết định cách chuyển các biến đến và từ các mô-đun.

cách tiếp cận ban đầu của tôi là:

mymodule.H: 

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

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

void mymodule(Inputs in,Outputs* out); 

Và trong hàm main (hoặc các mô-đun mà gọi module này) có "đầu vào" và "đầu ra" loại cấu trúc tạo ra.

Sau đó, các biến được sao chép vào cấu trúc đầu vào, hàm được gọi (tham chiếu kết quả đầu ra) và sau khi hoàn thành, nội dung của cấu trúc này được sử dụng để tính toán thêm.

Tuy nhiên, điều này sẽ dẫn đến một dấu chân bộ nhớ lớn, vì mỗi mô-đun yêu cầu các thể hiện của InputType và OutputType được tạo trong mô-đun gọi. Nó không phải là một giải pháp ellegant theo ý kiến ​​của tôi. Phân bổ động, bằng cách này, không được phép trong dự án của tôi.

Bạn có thể cung cấp cho tôi một số nguyên tắc và/hoặc ý tưởng thay thế để có được giải pháp tốt không?

Cảm ơn bạn.

Added

Một trong những giải pháp có thể được thông qua InputStruct cũng như con trỏ, nhưng vì họ là một cách hiệu quả đầu vào cho các mô-đun, làm thế nào tôi có thể đảm bảo rằng họ không được sửa đổi dọc theo mã?

Added

Bằng cách này, một vấn đề khác nảy sinh là một thực tế rằng không phải tất cả các mô-đun nhận các biến giống nhau, và không có cơ chế thừa kế có sẵn (vì đây là C), cấu trúc của mỗi mô-đun phải được nạp với các giá trị thích hợp. Tôi khá obfuscated ...

+1

Cấu trúc 'Đầu vào' cũng phải được chuyển thành con trỏ. – mah

+0

Tôi không hiểu tại sao bạn không được phép sử dụng 'bộ nhớ động'? Bạn sẽ hoàn thành một dự án khá phức tạp như thế nào? –

+0

@ bash.d Quá ít bộ nhớ để đủ khả năng phân mảnh? –

Trả lời

5

Bạn không phải chấp nhận dung lượng bộ nhớ lớn khi truyền tham số vào và ra khỏi chức năng. Điều quan trọng là để vượt qua các tham số bằng cách tham khảo, và sử dụng các từ khóa const để đảm bảo rằng đầu vào không được sửa đổi. Một ví dụ nổi tiếng là:

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

trong đó chỉ có các con trỏ đến nguồn và nhân vật đến bộ đệm được thông qua tại, không phải là một bản sao của bộ đệm, nhưng từ khóa const ngăn strcpy() từ sửa đổi nội dung bộ đệm nguồn.

Nếu bạn có rất nhiều thông số mà đi qua mỗi cá nhân là không thực tế sau đó bằng mọi cách vượt qua con trỏ đến cấu trúc thành chức năng của mình thay vào đó, một lần nữa, bằng cách sử dụng từ khóa const để đảm bảo rằng đầu vào không được sửa đổi:

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

Bởi vì cấu trúc được truyền bằng tham chiếu, myFunc() sẽ hoạt động trên cùng một bộ nhớ mà chức năng gọi sử dụng (nhưng nó sẽ không thể ghi vào bộ nhớ được trỏ tới bởi inputs nhờ từ khóa const), do đó phí bộ nhớ của chuyển nó đến hàm chỉ là con trỏ, mà trên một hệ thống nhúng điển hình là bốn byte, và không có sao chép quá mức ead.


Đối với vấn đề ngụ ý thứ hai của bạn, rằng kết quả đầu ra từ một trong những chức năng cần phải được thông qua như là đầu vào cho chức năng khác, nhưng danh sách tham số là không giống nhau, có thể không có nhiều mà bạn có thể làm khác hơn là bản sao từ một cấu trúc khác. Nếu bạn may mắn, bạn có thể làm điều gì đó như thế này:

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); 

Bạn có thấy điều gì đang xảy ra ở đây không? Phần đầu tiên của cấu trúc myFunc2In giống như struct myFunc1Out, vì vậy bạn có thể chỉ cần đúc cấu trúc cũ sang phần sau và các trường bổ sung sẽ bị bỏ qua. Bạn có thể nghĩ nó như một đa hình của người đàn ông nghèo (rất).

Loại khác, có lẽ ít mơ hồ hơn, cách chuyển cấu trúc myFunc1Out sang hàm thứ hai cùng với cấu trúc thứ hai cho các tham số bổ sung. Chi phí bộ nhớ bổ sung là một con trỏ. Có lẽ bạn có thể tổ chức dữ liệu của bạn thành các nhóm logic, mỗi nhóm được biểu diễn bởi một cấu trúc, sao cho không có quá nhiều cấu trúc, nhưng không có struct nào chứa các trường không phải lúc nào cũng được yêu cầu ở bất cứ nơi nào còn lại của cấu trúc đó?


Nhân tiện, một trong các nhận xét của bạn dường như ngụ ý rằng bạn định nghĩa cấu trúc có phí trên bộ nhớ trong tệp thực thi. Đây không phải là sự thật. Bộ nhớ chỉ được sử dụng khi một cá thể của cấu trúc được cấp phát.

+0

Không phải là _const struct * _ có nghĩa là con trỏ ** sẽ vẫn không đổi, thay vì cấu trúc trỏ đến? – Manex

+0

Ok, chỉ cần kiểm tra, nó hoạt động. – Manex

+0

@Manex: Không, một con trỏ liên tục sẽ được khai báo là 'struct * const'. Cá nhân, đối với khai báo cấu trúc con trỏ đến hằng số, tôi thích 'struct const *'. Nó tương đương với 'const struct *', nhưng có nhiều cú pháp nhất quán với các cách sử dụng 'const' khác. –

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(); 
0

Một khả năng là để ẩn các biến đằng sau chức năng setter/getter và sau đó sử dụng tiền xử lý để kiểm soát khả năng hiển thị của các chức năng đó trong các mô-đun.

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 

OK đó là một chút tẻ nhạt nhưng bây giờ bạn có thể kiểm soát tầm nhìn và writability trên một biến theo từng biến trong module của bạn:

/* 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 */ 

BTW câu trả lời này được xây dựng dựa trên câu trả lời cho this question cũng có một số thảo luận về việc đưa tất cả các chức năng đó vào nội tuyến.