programing

불투명 C 구조체: 다양한 선언 방법

randomtip 2022. 9. 4. 12:44
반응형

불투명 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;
}

이를 넘어서는 유일한 개선점은 다음과 같습니다.

  1. 하고, 해 주세요.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에러 타입을 지정합니다.

  2. 를 추가합니다.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
    

다음 항목도 참조하십시오.

  1. [상기 답변을 참고한 또 다른 답변]C에 숨어 있는 불투명한 구조 및 데이터에 대한 아키텍처 고려사항과 접근방법

객체 기반 C 아키텍처에 대한 추가 정보:

  1. 자체 구조물 롤아웃 시 도우미 기능 제공

유효한 사용을 위한 추가 정보 및 정당성goto프로페셔널 코드에 대한 오류 처리:

  1. 의 사용에 찬성하는 주장goto에러 처리에 대해서는, https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/Research_General/goto_for_error_handling_in_C/readme.md 를 참조해 주세요.
  2. **** 사용의 장점을 보여주는 뛰어난 기사gotoC 오류 처리 중: "C 오류 처리에 goto 사용" - https://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c
  3. C에서 오류 관리를 위해 goto를 사용할 수 있습니까?
  4. 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

반응형