C 使函数仅在库中可见,而不在API中可见

C 使函数仅在库中可见,而不在API中可见,c,visibility,C,Visibility,我正在编写一个分布在多个文件中的C99库,例如 // core.h void my_private_fn(); void API_my_public_fn(); 我只希望使用库的程序可以看到带前缀的函数,但我还需要在core.h中公开my_private\u fn,以便模块a.c使用。在C语言中有没有办法使my_private\u fn只在库中可见?将它们放在一个内部头文件中,该文件只在库中使用,不分发给最终用户,比如,core\u internal.h将它们放在一个内部头文件中,该头文件只

我正在编写一个分布在多个文件中的C99库,例如

// core.h
void my_private_fn();

void API_my_public_fn();

我只希望使用库的程序可以看到带前缀的函数,但我还需要在
core.h
中公开
my_private\u fn
,以便
模块a.c
使用。在C语言中有没有办法使
my_private\u fn
只在库中可见?

将它们放在一个内部头文件中,该文件只在库中使用,不分发给最终用户,比如,
core\u internal.h

将它们放在一个内部头文件中,该头文件只在库中使用,不分发给最终用户,比如说,
core\u internal.h
如果函数必须只在定义它的编译单元中可见,那么您可以将其声明为
static
。因为C语言提供的作用域很少:一个符号只能有3个作用域:

  • 块的局部(块可以是函数或函数内部的块)
  • 静态范围(函数外部的静态声明):符号仅在声明它的编译单元中可见
  • 全局范围(函数外部的非静态声明):符号在整个程序中可见

最多,您可以将声明隐藏在一个私有的包含文件中,而不是在官方文档化的API中声明。这样顺从的用户就不应该使用它。但是您不能阻止用户将声明放在自己的代码中并使用该函数。

如果该函数必须仅在定义它的编译单元中可见,则可以将其声明为
静态。因为C语言提供的作用域很少:一个符号只能有3个作用域:

  • 块的局部(块可以是函数或函数内部的块)
  • 静态范围(函数外部的静态声明):符号仅在声明它的编译单元中可见
  • 全局范围(函数外部的非静态声明):符号在整个程序中可见

最多,您可以将声明隐藏在一个私有的包含文件中,而不是在官方文档化的API中声明。这样顺从的用户就不应该使用它。但是您不能阻止用户将声明放在自己的代码中并使用该函数。

我找到了一种更简洁的方法,根据我选择的Serge的答案构建代码,其最大优点在于

关键是将“private”函数放在只包含在C文件中的头文件中,而不包含在头文件中。这样,“私有”符号在内部可用,但外部调用方无法使用。在完整的示例中:

core.h

void my_public_fn();
void my_private_fn();
#include "core.h"

void module_a_fn();
#include "module_a.h"
// etc.
core_priv.h

void my_public_fn();
void my_private_fn();
#include "core.h"

void module_a_fn();
#include "module_a.h"
// etc.
core.c

#include <stdio.h>

#include "core.h"
#include "core_priv.h"

void my_private_fn() {
    printf("Private function called.\n");
}

void my_public_fn() {
    printf("Public function called.\n");
}
#include "core_priv.h"
#include "module_a.h"

void module_a_fn() {
    my_private_fn();
    my_public_fn();
}
#include "library.h"

int main() {
    //my_private_fn(); // This triggers a compile warning.
    my_public_fn();    // I can still reach the "core" public function.
    module_a_fn();     // This calls the "private" function internally.

    return 0;
}
模块a.c

#include <stdio.h>

#include "core.h"
#include "core_priv.h"

void my_private_fn() {
    printf("Private function called.\n");
}

void my_public_fn() {
    printf("Public function called.\n");
}
#include "core_priv.h"
#include "module_a.h"

void module_a_fn() {
    my_private_fn();
    my_public_fn();
}
#include "library.h"

int main() {
    //my_private_fn(); // This triggers a compile warning.
    my_public_fn();    // I can still reach the "core" public function.
    module_a_fn();     // This calls the "private" function internally.

    return 0;
}
如果需要,我们可以将多个模块分组到一个公共库头中

library.h

void my_public_fn();
void my_private_fn();
#include "core.h"

void module_a_fn();
#include "module_a.h"
// etc.
这样,使用库的程序只需包含一个文件,其中仅包含:

main.c

#include <stdio.h>

#include "core.h"
#include "core_priv.h"

void my_private_fn() {
    printf("Private function called.\n");
}

void my_public_fn() {
    printf("Public function called.\n");
}
#include "core_priv.h"
#include "module_a.h"

void module_a_fn() {
    my_private_fn();
    my_public_fn();
}
#include "library.h"

int main() {
    //my_private_fn(); // This triggers a compile warning.
    my_public_fn();    // I can still reach the "core" public function.
    module_a_fn();     // This calls the "private" function internally.

    return 0;
}
使用
gcc-Wall*.c-o main.o
编译并执行
/main.o
生成:

Public function called.
Private function called.
Public function called.

我找到了一种更简洁的方式,在我选择的Serge答案的基础上构建代码,Serge的答案的最大优点在于

关键是将“private”函数放在只包含在C文件中的头文件中,而不包含在头文件中。这样,“私有”符号在内部可用,但外部调用方无法使用。在完整的示例中:

core.h

void my_public_fn();
void my_private_fn();
#include "core.h"

void module_a_fn();
#include "module_a.h"
// etc.
core_priv.h

void my_public_fn();
void my_private_fn();
#include "core.h"

void module_a_fn();
#include "module_a.h"
// etc.
core.c

#include <stdio.h>

#include "core.h"
#include "core_priv.h"

void my_private_fn() {
    printf("Private function called.\n");
}

void my_public_fn() {
    printf("Public function called.\n");
}
#include "core_priv.h"
#include "module_a.h"

void module_a_fn() {
    my_private_fn();
    my_public_fn();
}
#include "library.h"

int main() {
    //my_private_fn(); // This triggers a compile warning.
    my_public_fn();    // I can still reach the "core" public function.
    module_a_fn();     // This calls the "private" function internally.

    return 0;
}
模块a.c

#include <stdio.h>

#include "core.h"
#include "core_priv.h"

void my_private_fn() {
    printf("Private function called.\n");
}

void my_public_fn() {
    printf("Public function called.\n");
}
#include "core_priv.h"
#include "module_a.h"

void module_a_fn() {
    my_private_fn();
    my_public_fn();
}
#include "library.h"

int main() {
    //my_private_fn(); // This triggers a compile warning.
    my_public_fn();    // I can still reach the "core" public function.
    module_a_fn();     // This calls the "private" function internally.

    return 0;
}
如果需要,我们可以将多个模块分组到一个公共库头中

library.h

void my_public_fn();
void my_private_fn();
#include "core.h"

void module_a_fn();
#include "module_a.h"
// etc.
这样,使用库的程序只需包含一个文件,其中仅包含:

main.c

#include <stdio.h>

#include "core.h"
#include "core_priv.h"

void my_private_fn() {
    printf("Private function called.\n");
}

void my_public_fn() {
    printf("Public function called.\n");
}
#include "core_priv.h"
#include "module_a.h"

void module_a_fn() {
    my_private_fn();
    my_public_fn();
}
#include "library.h"

int main() {
    //my_private_fn(); // This triggers a compile warning.
    my_public_fn();    // I can still reach the "core" public function.
    module_a_fn();     // This calls the "private" function internally.

    return 0;
}
使用
gcc-Wall*.c-o main.o
编译并执行
/main.o
生成:

Public function called.
Private function called.
Public function called.

根据您使用的平台,您可能会签出(注意,该问题的公认答案并不能解决您的问题)。您可能需要查看Windows及其
\u declspec(dllimport)
\u declspec(dllexport)
限定符,还可能需要查看GNU。取决于您使用的平台,您可以查看(请注意,该问题的公认答案并不能解决您的问题)。您可能需要查看Windows及其
\uuudeclspec(dllimport)
\uudeclspec(dllexport)
限定符,并且您可能需要查看GNU。我喜欢这个,因为它不需要特定于编译器的选项。我不关心流氓演员,只关心API的可读性。这比我建议的命名约定要好。谢谢。我喜欢这个,因为它不需要特定于编译器的选项。我不关心流氓演员,只关心API的可读性。这比我建议的命名约定要好。谢谢