【问题标题】:How can I hide the declaration of a struct in C?如何在 C 中隐藏结构的声明?
【发布时间】:2009-07-20 17:02:07
【问题描述】:

Why should we typedef a struct so often in C?的问题中,unwind回答说:

在后一种情况下,您不能返回 价值点,因为它 声明对用户隐藏 头文件。这是一种技术 例如,在 GTK+ 中广泛使用。

声明隐藏是如何实现的?为什么不能按值返回Point?

添加:

我明白为什么我不能按值返回结构,但是仍然很难理解为什么我不能在我的函数中尊重这一点。即如果我的结构有名为 y 的成员,为什么我不能这样做?

 pointer_to_struct->y = some_value; 

我为什么要使用方法来做到这一点? (如 Gtk+)

谢谢大家,再次为我的英语不好感到抱歉。

【问题讨论】:

    标签: c


    【解决方案1】:

    看看这个使用公共头文件、私有头文件和实现文件的库示例。

    在文件public.h中:

    struct Point;
    
    struct Point* getSomePoint();
    

    在文件private.h中:

    struct Point
    {
        int x;
        int y;
    }
    

    在文件private.c中:

    struct Point* getSomePoint()
    {
        /* ... */
    }
    

    如果你把这三个文件编译成一个库,你只需将public.h和库对象文件交给库的使用者。

    getSomePoint 必须返回一个指向Point 的指针,因为public.h 没有定义Point 的大小,只是它是一个结构并且它存在。库的使用者可以使用指向Point 的指针,但不能访问成员或复制它,因为他们不知道结构的大小。

    关于您的进一步问题: 您不能取消引用,因为使用该库的程序仅具有来自 private.h 的信息,不包含成员声明。因此它不能访问点结构的成员。

    您可以将此视为 C 的封装特性,就像您将 C++ 类的数据成员声明为私有一样。

    【讨论】:

    • @chux-ReinstateMonica 你是对的,刚刚解决了这个问题。
    • 如果我想在file.c 中声明一个结构只能在file.c 中使用,这可能吗?
    • @mercury0114 如果您在file.c 中声明一个结构,它将仅在file.c 中的该声明点下方可见。
    【解决方案2】:

    他的意思是你不能在标题中按值返回结构,因为为此,结构必须完全声明。但在他的示例中,这发生在 C 文件中(使 X 成为完整类型的声明在 C 文件中“隐藏”,而不是暴露在标题中)。如果这是结构的第一个声明,则以下仅声明不完整的类型

    struct X;
    

    然后,你可以声明函数

    struct X f(void);
    

    但是你不能定义函数,因为你不能创建那个类型的变量,更不用说返回它了(它的大小是未知的)。

    struct X f(void) { // <- error here
      // ...
    }
    

    发生错误是因为“x”仍然不完整。现在,如果你只包含带有不完整声明的头文件,那么你就不能调用那个函数,因为函数调用的表达式会产生一个不完整的类型,这是被禁止的。

    如果您要在两者之间提供完整类型 struct X 的声明,它将是有效的

    struct X;
    struct X f(void);
    
    // ...
    struct X { int data; };
    struct X f(void) { // valid now: struct X is a complete type
      // ...
    }
    

    这也适用于使用typedef 的方式:它们都命名相同,(可能不完整)类型。一次使用普通标识符X,另一次使用标签struct X

    【讨论】:

      【解决方案3】:

      在头文件中:

      typedef struct _point * Point;
      

      编译器看到后就知道了:

      • 有一个名为_pointstruct
      • 有一个指针类型Point 可以引用struct _point

      编译器不知道:

      • struct _point 的样子。
      • struct _point 包含哪些成员。
      • struct _point 有多大。

      不仅编译器不知道,我们程序员也不知道。这意味着我们不能编写依赖于struct _point 的那些属性的代码,这意味着我们的代码可能更具可移植性。

      根据上面的代码,你可以编写如下函数:

      Point f() {
         ....
      }
      

      因为Point 是一个指针,而struct 指针的大小都相同,编译器不需要知道关于它们的任何其他信息。但是你不能写一个按值返回的函数:

      struct _point f() {
        ....
      }
      

      因为编译器对struct _point一无所知,特别是它的大小,它需要它来构造返回值。

      因此,我们只能通过Point 类型来引用struct _point,这实际上是一个指针。这就是标准 C 具有像 FILE 这样的类型的原因,这些类型只能通过指针访问 - 您不能在代码中创建 FILE 结构实例。

      【讨论】:

        【解决方案4】:

        那个帖子的意思是:如果你看到标题

        typedef struct _Point Point;
        
        Point * point_new(int x, int y);
        

        那你不知道Point的实现细节。

        【讨论】:

          【解决方案5】:

          老问题,更好的答案:

          在头文件中:

          typedef struct _Point Point;
          

          在 C 文件中:

          struct _Point
          {
             int X;
             int Y;
          };
          

          【讨论】:

            【解决方案6】:

            作为使用不透明指针的替代方法(正如其他人提到的),如果您想避免使用堆内存,则可以返回一个不透明的字节包:

            // In public.h:
            struct Point
            {
                uint8_t data[SIZEOF_POINT];  // make sure this size is correct!
            };
            void MakePoint(struct Point *p);
            
            // In private.h:
            struct Point
            {
                int x, y, z;
            };
            
            void MakePoint(struct Point *p);
            
            // In private.c:
            void MakePoint(struct Point *p)
            {
                p->x = 1;
                p->y = 2;
                p->z = 3;
            }
            

            然后,您可以在客户端代码中在堆栈上创建结构的实例,但客户端不知道其中有什么——它只知道它是一个给定大小的字节块。当然,如果它可以猜测成员的偏移量和数据类型,它仍然可以访问数据,但是你再次遇到不透明指针的相同问题(尽管在这种情况下客户端不知道对象大小)。

            例如,pthreads 库中使用的各种结构对pthread_tpthread_cond_t 等类型使用不透明字节的结构——您仍然可以在堆栈上创建这些结构的实例(而且您通常做),但你不知道里面有什么。只需查看您的 /usr/include/pthreads.h 及其包含的各种文件即可。

            【讨论】:

            • 你会遇到对齐问题,public struct Point 原生对齐要求基于 uint8_t 数据类型,例如。 1 个字节,而私有 struct Point 本机对齐要求基于 int,可能是 4 个字节。我试图在回答stackoverflow.com/a/17619016/611560问候时解释这个问题。
            猜你喜欢
            • 2021-12-13
            • 2018-08-13
            • 2021-03-10
            • 1970-01-01
            • 1970-01-01
            • 2023-02-25
            • 1970-01-01
            • 2011-02-13
            • 1970-01-01
            相关资源
            最近更新 更多