불투명 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 를 참조해 주세요.
- **** 사용의 장점을 보여주는 뛰어난 기사gotoC 오류 처리 중: "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 |