【问题标题】:Levels of visibility of symbols from a compilation unit - C编译单元中符号的可见性级别 - C
【发布时间】:2016-12-23 21:10:03
【问题描述】:

在下面的代码中,

/**********linkedListImpl.c ***********/

#include"list/list.h"

#if defined(LINKED_LIST)



/***************** Representation - start ******************/
  /* struct members are not visible to other .c files */
  static struct DListNode{

    void *item;
    struct DListNode *next;
    struct DListNode *prev;
  };

  /* Should be used in this .c file, only, so static */
  static typedef struct DListNode DListNode;
  static DListNode* createNode(void *);


  static struct List{

    DListNode *head;
    int size; /*size attribute is not part of list definition, but quick way 
                  to help user code */
  }List;


  .....
  #endif

/************ list.h ************/
#ifndef LIST_H /* Header guard */
#define LIST_H
#include"type.h"

/***************** Usage-start ************/

#if defined(ARRAY) || (LINKED_LIST)

  typedef struct List List;

#else
  #error "Wrong list implementation macro name !!!"
#endif

 ...
#endif /* LIST_H */

List 类型在list.h 中声明,使用staticspecifier,

  typedef struct List List;

并在linkedListImpl.c 中定义,使用staticspecifier,

 static struct List{

    DListNode *head;
    int size;
  }List;

目的:使List 符号仅通过指针(List*) 对用户(main.c) 可用。用户(main.c) 应该无法访问main.c 中的List 成员。

linkedListImpl.c 中,符号DListNode 是使用static 说明符定义的,

  static struct DListNode{

    void *item;
    struct DListNode *next;
    struct DListNode *prev;
  };

  static typedef struct DListNode DListNode;

目的:对于符号DListNode,用户(main.c)既不能访问main.c中的DListNode成员,也不能通过DListNode*指针访问DListNode对象。

这是隐藏符号ListDListNode 的正确方法吗?

注意:Here 是完整代码

【问题讨论】:

    标签: c static linker


    【解决方案1】:

    是的,就是这样。它被称为不透明指针,用于隐藏接口中的实现细节。

    您可能对What is an opaque pointer in C?感兴趣

    【讨论】:

    • static struct List {...}List 做了两件事:我定义了一个结构List,它还声明了一个名为Liststatic 变量List 类型struct List。单词static 仅涉及变量List,而不涉及结构ListDListNode 仅作为结构定义编写,因此 static 一词在这里没有用,正如 Basile 回答的那样。
    【解决方案2】:

    static struct DListNode { ... }; 是无用的(它声明了struct DListNodeno 变量),但确实声明了struct DListNode,因此与简单的struct DListNode { 完全相同。 ..};.就像static int; 声明了一个包含 0 个整数变量的列表(请注意,static int x,y; 是声明两个变量列表 xy 的常用方法)。

    一种非常常见的方法是在公共头文件中定义所有struct(及其字段)。实际上,您正在展示实现细节(但编译器需要它们来发出代码)。

    一种不太常见的方法是在头文件中声明struct,并且使用指向它的指针(opaque pointer,如Burns' answer)。然后你可能有一个定义struct 的成员的实现。另见this

    一个常见的例子是来自<stdio.h>FILE。它很可能被定义为一些struct,但字段名称​​通常是隐藏且未使用的。

    C 编程很重要的是要有良好的约定并遵循它们。

    【讨论】:

      【解决方案3】:

      你所拥有的将起作用。

      您在头文件中的typedef 也充当结构的前向声明。这足以传递指向struct 类型的指针,但可以防止指针被取消引用。

      这允许您将struct 的实现细节保留给.c 文件,如果它是定义

      【讨论】:

        【解决方案4】:

        当您需要在纯 C 中创建不透明引用时,最好的方法是创建 2 组单独的标头,其中一组具有完整声明,将在实现“方法”的源中使用,另一组带有不透明引用的标头用户使用。

        这是许多操作系统中常用的策略。看看 MS 标头,它们使用 HANDLE 来引用对象。 HANDLES 被创建并传递给系统函数,即 C++ 方法的纯 C 模拟,它驻留在使用完整定义头文件的模块中。

        考虑这两个标题:

        /* Internal header "InnerDList.h" */
          struct DListNode{
            void *item;
            struct DListNode *next;
            struct DListNode *prev;
          };
        

        第二次

        /* User header "DList.h" */
        typedef struct DListNode DListNode;
        
        DListNode *CreateDList(void * data, ...);
        HRESULT    AddDListNode(DListNode *Dlist, void *data);
        HRESULT    GetDListNode(DListNode *Dlist, void *SearchData);
        ...
        

        使用第一个允许编译适用于 DList 结构的函数,第二个允许通过不透明对象使用 DList。

        允许使用完整或部分定义的变体可能如下:在内部标头中定义预处理器符号,并使用它有条件地定义不透明对象。

        /* Internal header "InnerDList.h" */
        #define INNERDLIST 1    //We will use this symbol to modify user header
        
          typedef struct DListNode{
            void *item;
            struct DListNode *next;
            struct DListNode *prev;
          } DListNode;
        #include "DList.h"    //Now include user header
        

        在用户标题中:

        /* User header "DList.h" */
        #ifndef INNERDLIST
        typedef struct DListNode DListNode;    //define only if not called by internal header
        #endif
        DListNode *CreateDList(void * data, ...);
        HRESULT    AddDListNode(DListNode *Dlist, void *data);
        HRESULT    GetDListNode(DListNode *Dlist, void *SearchData);
        ...
        

        【讨论】:

        • 如果我什至不想使用DListNode对象,通过不透明对象,在其他编译单元中,应该是什么方法?
        • @overexchange 你是什么意思?您想在其他模块中有完整的定义吗?
        • 在我的代码中,意图是通过main.c 中的不透明指针使List 可用,但DListNode 甚至不应该通过@ 中的不透明指针(完全隐藏)可用987654329@。 DListNode 纯粹用于linkedListImpl.c 。为DListNode 设置这种可见性级别的方法是什么?
        • @overexchange 您必须创建适当的标头,选择性地包含您希望通过完整定义、不透明指针或什么都不提供的内容。我添加了一个关于如何管理它的示例。够清楚吗?
        猜你喜欢
        • 2019-08-02
        • 1970-01-01
        • 2017-02-20
        • 1970-01-01
        • 2012-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多