【问题标题】:Make a object accessible by only its library, and not by any other routine in the program使对象只能由其库访问,而不能由程序中的任何其他例程访问
【发布时间】:2021-09-03 19:41:20
【问题描述】:

假设我有两个(或更多)c 函数 func1()func2() 都需要缓冲区变量 int buff。如果这两个函数都保存在单独的文件中,func1.cfunc2.c,我该怎么做才能使 buff 只能被 func1() 和 @ 访问987654326@ 而不是调用例程(或任何其他例程)。
这是一个示例设置:

文件func1.c

/*func1.c*/
static int buff;

int *func1(int x)
{
    buff = x;
    return &buff;
}

文件func2.c

/*func2.c*/
static int buff;

int *func2(int x)
{
    buff = x;
    return &buff;
}

标题header.h

/*header for func1.c and func2.c*/
//multiple inclusion guard not present.
int *func1(int);
int *func2(int);

文件main.c

#include<stdio.h>
#include"header.h"

int main()
{
    int *ptr;

    ptr = func1(1);
    printf("&buff = %p , buff = %d\n", ptr, *ptr);
    ptr = func2(2);
    printf("&buff = %p , buff = %d\n", ptr, *ptr);

    return 0;
}

正如预期的那样,输出显示 buff 的不同内存位置。

&buff = 0x55b8fd3f0034 , buff = 1
&buff = 0x55b8fd3f0038 , buff = 2

但我只需要一个副本增益,不需要更多。
当然,我可以将两个函数放在同一个文件中,并将 buff 定义为 static int,但这样我将失去分别编译这些函数的能力。
如果我将int buff 放在单独的 buff.c 中并在 func1.cfunc2.c 中声明它 extern em>,但调用例程(在本例中为 main)很容易访问它。


基本上,我需要创建一个函数库,这些函数库可以在同一个外部对象上工作,并且只有他们可以访问。调用例程可能不需要所有函数,因此我不想将它们放在一个文件中并创建未使用的代码。但对象必须只有一个副本。

如果可以的话,请帮助我如何做同样的事情。

【问题讨论】:

  • 您必须将所有需要buff 的函数作为“私有”变量放在同一个文件中。
  • "...所以我不想将它们放在一个文件中并创建未使用的代码。"除非你有大量的函数,否则我不会担心。
  • 您建议您正在构建一个库。我很确定事后有一些不错的方法来操作库的符号表,以调整诸如符号可见性之类的东西。所以你可以(a)使用一个普通的全局变量,(b)构建你的库,(c)调整库以使全局变量非全局,并且(d)确保你的库的客户无法欺骗和偷看它。
  • @SteveSummit,我做了变通方法和调整,基本上我也很想知道通过丢弃未使用的“未使用代码”可以减少多少最终可执行文件的大小(意思是文件大小)函数定义。
  • 对于“常规”库(.a.lib)和静态链接,我希望拆分您的源文件,以便不需要的函数不会复制到最终的可执行文件中,帮助使最终的可执行文件更小。但是对于动态链接的库(.so.dll),据我所知,无论如何,所有内容总是最终在加载的库中,但没有一个最终在使用该库的可执行文件中,所以我不会期待它有所作为。

标签: c++ c object


【解决方案1】:

您需要在其中一个文件中定义非静态变量,例如:

int buff;

int *func1(int x)
{
    buff = x;
    return &buff;
}

在头文件中声明为extern:

/*header for func1.c and func2.c*/
//multiple inclusion guard not present.

extern int buff;

int *func1(int);
int *func2(int);

将其包含在所有其他文件中:

/*func2.c*/
#include "header.h"

int *func1(int x)
{
    buff = x;
    return &buff;
}

如果您不希望变量可见,则需要创建函数来获取和设置“隐藏”变量。

typedef enum
{
    GET,
    SET,
    REF,
}OP_t;


#define CREATE(type, name) type getset##name(OP_t oper, type val, type **ref) \
{\
    static type buff;\
    switch(oper)\
    {\
        case GET:\
            return buff;\
        case SET:\
            buff = val;\
            break;\
        case REF:\
            if(ref) *ref = &buff;\
            break;\
    }\
    return 0;\
}\

#define HEAD(type, name) type getset##name(OP_t oper, type val, type **ref)
#define GETVAL(name) getset##name(GET, 0, NULL)
#define SETVAL(name,val) getset##name(SET, val, NULL)
#define GETREF(name,ref) getset##name(REF, 0, ref)

【讨论】:

  • OP 希望 buff 无法访问除 func1func2 之外的所有其他功能,因此这并不能解决 OP 问题。使用这种方法,每个包含标头的源文件都可以访问缓冲区。
【解决方案2】:

答案是:不可能。

C 没有办法说“这个变量可能被源文件 x、y、z 使用,而不能被任何其他源文件使用”。

因此,如果您希望 buff 对多个函数“私有”,则必须将这些函数放在同一个源文件中。

【讨论】:

    【解决方案3】:

    C 标准没有提供执行此操作的方法。它通常使用 C 标准之外的编译器和链接器的特性来完成。这是在 macOS 上使用 Apple 开发人员工具的示例。对于适合您环境的选项,您应该指定您正在使用的构建工具和版本,例如您使用的是 Apple 工具、GNU 工具、Microsoft 工具还是其他工具。

    a.c

    #include <stdio.h>
    
    int x = 123;
    
    void a(void)
    {
        printf("In a.c, x is %d.\n", x);
    }
    

    这个在b.c:

    #include <stdio.h>
    
    extern int x;
    
    void b(void)
    {
        printf("In b.c, x is %d.\n", x);
    }
    

    我们将源文件编译成目标模块:

    clang -c a.c b.c
    

    然后将它们链接到新的对象模块r.o,同时请求不导出符号x(链接器视图中的_x):

    ld -r -o r.o -unexported_symbol _x a.o b.o
    

    那么,如果我们有另一个源文件c.c 尝试使用x

    #include <stdio.h>
    
    extern int x;
    
    extern void a(void);
    extern void b(void);
    
    int main(void)
    {
        a();
        b();
        printf("In c.c, x is %d.\n", x);
    }
    

    尝试使用clang -o c c.c r.o 构建可执行文件:

    架构 x86_64 的未定义符号: “_x”,引用自: _main 在 c-139a35.o ld:未找到架构 x86_64 的符号

    但是,如果我们删除 c.c 中引用 x 的两行,则构建成功,程序会打印:

    在交流中,x 是 123。 在 b.c 中,x 是 123。

    【讨论】:

      【解决方案4】:

      解决此问题的一种典型方法是为全局变量指定一个以_ 开头的名称。

      也就是说,你可以在func1.c中写

      int _mylib_buff;
      

      然后在func2.c中,当然,你会有

      extern int _mylib_buff;
      

      当然,在这种情况下,_mylib_buff 在技术上是一个普通的全局变量。它根本不是真正的“私人”。但是以_ 开头的全局变量是“按照惯例”私有的,我认为这在实践中可以正常工作。但是,很明显,没有什么可以阻止其他源文件欺骗和偷看名义上的私有变量,标准 C 也没有办法阻止这样做。

      另一个复杂之处是,一些以_ 开头的标识符被保留给实现,你不应该在你自己的代码中使用它们。 (也就是说,实现的组件——比如你的 C 编译器和 C 库——有半全局变量,它们试图对你隐藏,而且它们通常也使用前导 _ 来实现这一点。 ) 我很确定规则说你可以定义一个以前导下划线后跟小写字母开头的全局变量,但是规则有些复杂,我永远记不起所有的细微差别。请参阅C FAQ list 中的问题1.91.29

      【讨论】:

      • 在变量名前加上_ 解决不了任何问题。该变量仍可用于任何编译单元。
      • @4386427 你在我解释的地方读过我的回答吗?
      猜你喜欢
      • 2015-03-27
      • 2023-01-27
      • 1970-01-01
      • 2018-11-12
      • 1970-01-01
      • 1970-01-01
      • 2013-08-31
      • 2020-02-29
      • 2011-12-06
      相关资源
      最近更新 更多