불투명 C 구조체: 다양한 선언 방법
저는 다음 두 가지 스타일의 불투명한 유형을 C API에서 선언하는 것을 보았습니다.C에서 불투명한 구조물/포인터를 선언하는 다양한 방법은 무엇입니까?어떤 스타일을 다른 스타일에 비해 사용하는 것이 확실한 장점이 있습니까?
옵션 1
// foo.h
typedef struct foo * fooRef;
void doStuff(fooRef f);
// foo.c
struct foo {
int x;
int y;
};
옵션 2
// foo.h
typedef struct _foo foo;
void doStuff(foo *f);
// foo.c
struct _foo {
int x;
int y;
};
muviciel이 게시했다가 삭제한 세 번째 옵션에 투표합니다.
나는 세 번째 방법을 보았다.
// foo.h struct foo; void doStuff(struct foo *f); // foo.c struct foo { int x; int y; };
못 치겠다면.struct
" " ",typedef struct foo foo;
(주의: 쓸모없고 문제가 있는 언더스코어 삭제)는 허용됩니다.하지만 무엇을 하든, 절대 사용하지 마세요.typedef
포인터 유형의 이름을 정의합니다.함수에 할 때마다 수 있는 다른 를 들어, 다른 수식을 처리합니다.const
는 큰입니다.-qualified).
옵션 1.5 ('오브젝트 베이스' C 아키텍처):
옵션 1을 사용하는 데 익숙하지만 참조처 이름을 붙이는 곳은 예외입니다._h
이것을 나타내는 것은 주어진 C "클래스"의 C 스타일 "객체"에 대한 "핸들"이다.을 사용할 수 .const
뿐 수 「input」은 사용하지 말아 주세요.const
변경 가능한 곳이면 어디든지 상관없습니다.다음 스타일을 수행합니다.
// -------------
// my_module.h
// -------------
// An opaque pointer (handle) to a C-style "object" of "class" type
// "my_module" (struct my_module_s *, or my_module_h):
typedef struct my_module_s *my_module_h;
void doStuff1(my_module_h my_module);
void doStuff2(const my_module_h my_module);
// -------------
// my_module.c
// -------------
// Definition of the opaque struct "object" of C-style "class" "my_module".
struct my_module_s
{
int int1;
int int2;
float f1;
// etc. etc--add more "private" member variables as you see fit
};
다음은 C에서 불투명 포인터를 사용하여 개체를 만드는 전체 예입니다.다음 아키텍처는 "개체 기반 C"라고 불릴 수 있습니다.
//==============================================================================================
// my_module.h
//==============================================================================================
// An opaque pointer (handle) to a C-style "object" of "class" type "my_module" (struct
// my_module_s *, or my_module_h):
typedef struct my_module_s *my_module_h;
// Create a new "object" of "class" "my_module": A function that takes a *pointer to* an
// "object" handle, `malloc`s memory for a new copy of the opaque `struct my_module_s`, then
// points the user's input handle (via its passed-in pointer) to this newly-created "object" of
// "class" "my_module".
void my_module_open(my_module_h * my_module_h_p);
// A function that takes this "object" (via its handle) as an input only and cannot modify it
void my_module_do_stuff1(const my_module_h my_module);
// A function that can modify the private content of this "object" (via its handle) (but still
// cannot modify the handle itself)
void my_module_do_stuff2(my_module_h my_module);
// Destroy the passed-in "object" of "class" type "my_module": A function that can close this
// object by stopping all operations, as required, and `free`ing its memory.
void my_module_close(my_module_h my_module);
//==============================================================================================
// my_module.c
//==============================================================================================
// Definition of the opaque struct "object" of C-style "class" "my_module".
// - NB: Since this is an opaque struct (declared in the header but not defined until the source
// file), it has the following 2 important properties:
// 1) It permits data hiding, wherein you end up with the equivalent of a C++ "class" with only
// *private* member variables.
// 2) Objects of this "class" can only be dynamically allocated. No static allocation is
// possible since any module including the header file does not know the contents of *nor the
// size of* (this is the critical part) this "class" (ie: C struct).
struct my_module_s
{
int my_private_int1;
int my_private_int2;
float my_private_float;
// etc. etc--add more "private" member variables as you see fit
};
void my_module_open(my_module_h * my_module_h_p)
{
// Ensure the passed-in pointer is not NULL (since it is a core dump/segmentation fault to
// try to dereference a NULL pointer)
if (!my_module_h_p)
{
// Print some error or store some error code here, and return it at the end of the
// function instead of returning void.
goto done;
}
// Now allocate the actual memory for a new my_module C object from the heap, thereby
// dynamically creating this C-style "object".
my_module_h my_module; // Create a local object handle (pointer to a struct)
// Dynamically allocate memory for the full contents of the struct "object"
my_module = malloc(sizeof(*my_module));
if (!my_module)
{
// Malloc failed due to out-of-memory. Print some error or store some error code here,
// and return it at the end of the function instead of returning void.
goto done;
}
// Initialize all memory to zero (OR just use `calloc()` instead of `malloc()` above!)
memset(my_module, 0, sizeof(*my_module));
// Now pass out this object to the user, and exit.
*my_module_h_p = my_module;
done:
}
void my_module_do_stuff1(const my_module_h my_module)
{
// Ensure my_module is not a NULL pointer.
if (!my_module)
{
goto done;
}
// Do stuff where you use my_module private "member" variables.
// Ex: use `my_module->my_private_int1` here, or `my_module->my_private_float`, etc.
done:
}
void my_module_do_stuff2(my_module_h my_module)
{
// Ensure my_module is not a NULL pointer.
if (!my_module)
{
goto done;
}
// Do stuff where you use AND UPDATE my_module private "member" variables.
// Ex:
my_module->my_private_int1 = 7;
my_module->my_private_float = 3.14159;
// Etc.
done:
}
void my_module_close(my_module_h my_module)
{
// Ensure my_module is not a NULL pointer.
if (!my_module)
{
goto done;
}
free(my_module);
done:
}
간단한 사용 예:
#include "my_module.h"
#include <stdbool.h>
#include <stdio.h>
int main()
{
printf("Hello World\n");
bool exit_now = false;
// setup/initialization
my_module_h my_module = NULL;
// For safety-critical and real-time embedded systems, it is **critical** that you ONLY call
// the `_open()` functions during **initialization**, but NOT during normal run-time,
// so that once the system is initialized and up-and-running, you can safely know that
// no more dynamic-memory allocation, which is non-deterministic and can lead to crashes,
// will occur.
my_module_open(&my_module);
// Ensure initialization was successful and `my_module` is no longer NULL.
if (!my_module)
{
// await connection of debugger, or automatic system power reset by watchdog
log_errors_and_enter_infinite_loop();
}
// run the program in this infinite main loop
while (exit_now == false)
{
my_module_do_stuff1(my_module);
my_module_do_stuff2(my_module);
}
// program clean-up; will only be reached in this case in the event of a major system
// problem, which triggers the infinite main loop above to `break` or exit via the
// `exit_now` variable
my_module_close(my_module);
// for microcontrollers or other low-level embedded systems, we can never return,
// so enter infinite loop instead
while (true) {}; // await reset by watchdog
return 0;
}
이를 넘어서는 유일한 개선점은 다음과 같습니다.
하고, 해 주세요.
void
/// @brief my_module error codes typedef enum my_module_error_e { /// No error MY_MODULE_ERROR_OK = 0, /// Invalid Arguments (ex: NULL pointer passed in where a valid pointer is required) MY_MODULE_ERROR_INVARG, /// Out of memory MY_MODULE_ERROR_NOMEM, /// etc. etc. MY_MODULE_ERROR_PROBLEM1, } my_module_error_t;
이제 ,, ,, ow를 반환하는 now, ow, ow를 .
void
모든 하고, 하십시오.my_module_error_t
에러 타입을 지정합니다.를 추가합니다.
my_module_config_t
파일에 하여 .h 파일에 합니다.open
함수를 사용하여 새 개체를 만들 때 내부 변수를 업데이트합니다.이것에 , 「 」, 「 」, 「 」, 「 」, 「 」를 호출했을 에, 변수를 , 을 도모할 수 ._open()
.예:
//-------------------- // my_module.h //-------------------- // my_module configuration struct typedef struct my_module_config_s { int my_config_param_int; float my_config_param_float; } my_module_config_t; my_module_error_t my_module_open(my_module_h * my_module_h_p, const my_module_config_t *config); //-------------------- // my_module.c //-------------------- my_module_error_t my_module_open(my_module_h * my_module_h_p, const my_module_config_t *config) { my_module_error_t err = MY_MODULE_ERROR_OK; // Ensure the passed-in pointer is not NULL (since it is a core dump/segmentation fault // to try to dereference a NULL pointer) if (!my_module_h_p) { // Print some error or store some error code here, and return it at the end of the // function instead of returning void. Ex: err = MY_MODULE_ERROR_INVARG; goto done; } // Now allocate the actual memory for a new my_module C object from the heap, thereby // dynamically creating this C-style "object". my_module_h my_module; // Create a local object handle (pointer to a struct) // Dynamically allocate memory for the full contents of the struct "object" my_module = malloc(sizeof(*my_module)); if (!my_module) { // Malloc failed due to out-of-memory. Print some error or store some error code // here, and return it at the end of the function instead of returning void. Ex: err = MY_MODULE_ERROR_NOMEM; goto done; } // Initialize all memory to zero (OR just use `calloc()` instead of `malloc()` above!) memset(my_module, 0, sizeof(*my_module)); // Now initialize the object with values per the config struct passed in. Set these // private variables inside `my_module` to whatever they need to be. You get the idea... my_module->my_private_int1 = config->my_config_param_int; my_module->my_private_int2 = config->my_config_param_int*3/2; my_module->my_private_float = config->my_config_param_float; // etc etc // Now pass out this object handle to the user, and exit. *my_module_h_p = my_module; done: return err; }
용도:
my_module_error_t err = MY_MODULE_ERROR_OK; my_module_h my_module = NULL; my_module_config_t my_module_config = { .my_config_param_int = 7, .my_config_param_float = 13.1278, }; err = my_module_open(&my_module, &my_module_config); if (err != MY_MODULE_ERROR_OK) { switch (err) { case MY_MODULE_ERROR_INVARG: printf("MY_MODULE_ERROR_INVARG\n"); break; case MY_MODULE_ERROR_NOMEM: printf("MY_MODULE_ERROR_NOMEM\n"); break; case MY_MODULE_ERROR_PROBLEM1: printf("MY_MODULE_ERROR_PROBLEM1\n"); break; case MY_MODULE_ERROR_OK: // not reachable, but included so that when you compile with // `-Wall -Wextra -Werror`, the compiler will fail to build if you forget to handle // any of the error codes in this switch statement. break; } // Do whatever else you need to in the event of an error, here. Ex: // await connection of debugger, or automatic system power reset by watchdog while (true) {}; } // ...continue other module initialization, and enter main loop
다음 항목도 참조하십시오.
- [상기 답변을 참고한 또 다른 답변]C에 숨어 있는 불투명한 구조 및 데이터에 대한 아키텍처 고려사항과 접근방법
객체 기반 C 아키텍처에 대한 추가 정보:
유효한 사용을 위한 추가 정보 및 정당성goto
프로페셔널 코드에 대한 오류 처리:
- 의 사용에 찬성하는 주장
goto
에러 처리에 대해서는, https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/Research_General/goto_for_error_handling_in_C/readme.md 를 참조해 주세요. - **** 사용의 장점을 보여주는 뛰어난 기사
goto
C 오류 처리 중: "C 오류 처리에 goto 사용" - https://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c - C에서 오류 관리를 위해 goto를 사용할 수 있습니까?
- C 코드의 오류 처리
검색어 검색 가능: C의 불투명 포인터, C의 불투명 구조, C의 typedef 열거형, C의 오류 처리, c 아키텍처, 객체 기반 c 아키텍처, c의 초기화 아키텍처에서의 동적 메모리 할당
bar(const fooRef)
는, 불변의 주소를 인수로 선언합니다. bar(const foo *)
불변의 foo 주소를 인수로 선언합니다.
이러한 이유로 옵션2를 선호하는 경향이 있습니다.즉, 제시된 인터페이스 타입은 각 간접 수준에서 cv-ness를 지정할 수 있는 타입입니다.물론 옵션1 라이브러리 라이터를 사용하지 않고foo
했을 때, 됩니다 는, 라이브러리 라이터는, 라이브러리 라이터는, 「라이브러리 라이터」가 「라이브러리 라이터」의 「라이브러리 라이터」가 「라이브러리 라이터」의 「라이브러리 라이터」라고 밖에 되지 않습니다).fooRef
이며, 「」는 「불변 인터페이스」의 일부입니다.foo
2 는 옵션 2 라이브러리 라이터가 2로 인식합니다.foo
을 이용하다
형틀에프/구조화되다
typedef struct { ... } foo;
옵션 3: 사용자에게 선택권 부여
/* foo.h */
typedef struct PersonInstance PersonInstance;
typedef struct PersonInstance * PersonHandle;
typedef const struct PersonInstance * ConstPersonHandle;
void saveStuff (PersonHandle person);
int readStuff (ConstPersonHandle person);
...
/* foo.c */
struct PersonInstance {
int a;
int b;
...
};
...
언급URL : https://stackoverflow.com/questions/3965279/opaque-c-structs-various-ways-to-declare-them
'programing' 카테고리의 다른 글
클래스의 모든 변수 이름 가져오기 (0) | 2022.09.04 |
---|---|
Java 불변의 컬렉션 (0) | 2022.09.04 |
아티팩트 org.apache.maven을 전송할 수 없습니다.플러그인: maven-surefire-pair: pattern: 2.7.1에서 central(http://repo1.maven.org/maven2)로) (0) | 2022.09.04 |
부언, 조건부 연산자 및 자동 박스 (0) | 2022.09.04 |
Java에서의 정규식 \\s vs. \\s (0) | 2022.09.04 |