对于大多数编程语言而言,命名空间似乎都是一件易如反掌的事情。但据我所知,ANSI C 并不支持它。为什么呢?有计划在未来的标准中包含它吗?
对于大多数编程语言而言,命名空间似乎都是一件易如反掌的事情。但据我所知,ANSI C 并不支持它。为什么呢?有计划在未来的标准中包含它吗?
为了完整性,在C语言中有几种实现“名称空间”所提供的好处的方法。
我最喜欢的方法之一是使用一个结构体来存储一堆指向你的库/等等接口的方法指针。
然后,你可以使用一个外部实例来初始化这个结构体,在你的库中指向所有的函数。这样可以使你在库中保持名称简单,而不会影响客户端的名称空间(除了全局范围的外部变量,1个变量与可能数百个方法相比更容易处理)。
当然,这种方法需要进行额外的维护,但我认为它是很小的。
以下是一个示例:
/* interface.h */
struct library {
const int some_value;
void (*method1)(void);
void (*method2)(int);
/* ... */
};
extern const struct library Library;
/* end interface.h */
/* interface.c */
#include "interface.h"
void method1(void)
{
...
}
void method2(int arg)
{
...
}
const struct library Library = {
.method1 = method1,
.method2 = method2,
.some_value = 36
};
/* end interface.c */
/* client code */
#include "interface.h"
int main(void)
{
Library.method1();
Library.method2(5);
printf("%d\n", Library.some_value);
return 0;
}
/* end client code */
使用.
语法可以在经典的Library_function()
和Library_some_value
方法之间建立强关联。但是,这种语法存在一些限制,例如您无法将宏用作函数。
library.method1()
时是否足够聪明,能够在编译时解除函数指针的引用? - einpoklum.c
文件中,因此只有在.c
文件中显式公开的函数才会被公开。 - lastmjs-O2
和-flto
编译时,gcc将计算出function1
/method2
的实际地址。除非您将这些库与自己的源代码一起编译,否则此方法会增加其函数调用的开销。 - Alex ReinkingC 有命名空间。其中一个用于结构体标签,另一个用于其他类型。考虑以下定义:
struct foo
{
int a;
};
typedef struct bar
{
int a;
} foo;
第一个有tag foo,后者是通过typedef变成类型foo。仍然没有名称冲突发生。这是因为结构标签和类型(内置类型和typedef的类型)存在不同的命名空间。
C语言不允许按意愿创建新的命名空间。在语言被标准化之前,这被认为并不重要。增加命名空间也会威胁到向后兼容性,因为它需要正确工作的名称编码。我认为这可以归因于技术上的细节,而不是哲学上的问题。
编辑: JeremyP幸运地纠正了我并提到了我错过的命名空间。标签和struct / union成员也有命名空间。
C具有命名空间。语法为namespace_name
。您甚至可以像general_specific_name
那样嵌套它们。如果想要在不每次编写命名空间名称的情况下访问名称,可以在头文件中包含相关的预处理器宏,例如:
#define myfunction mylib_myfunction
这比命名混淆和某些语言为了提供命名空间而犯下的其他罪行要干净得多。
历史上,C编译器不会修饰函数名(在Windows上会进行修饰,但针对cdecl调用约定的修饰只涉及添加下划线前缀)。
这使得从其他语言(包括汇编语言)使用C库变得容易,并且这也是为什么你经常会在C++ API中看到extern "C"
的包装器之一的原因。
vector::add
或等效方法即可。我也无法想象。 - Kaihaku只是出于历史原因,那个时候没有人考虑过像命名空间这样的东西。此外,他们真的试图保持语言简单。也许将来会有。
虽然不是答案,但也不是评论。C语言没有提供显式定义命名空间
的方法,它具有变量作用域。例如:
int i=10;
struct ex {
int i;
}
void foo() {
int i=0;
}
void bar() {
int i=5;
foo();
printf("my i=%d\n", i);
}
void foobar() {
foo();
bar();
printf("my i=%d\n", i);
}
mylib.h
void mylib_init();
void mylib_sayhello();
using
,也不能从 mylib
导入。namespace mylib { void init(); void say_hello(); }
替换最后两行,这也是重要的(有点)。 - einpoklumANSI C在命名空间出现之前就被发明了。
namespace MY_OBJECT {
struct HANDLE;
HANDLE *init();
void destroy(HANDLE * & h);
void do_something(HANDLE *h, ... );
}
struct MY_OBJECT_HANDLE;
struct MY_OBJECT_HANDLE *my_object_init();
void my_object_destroy( MY_OBJECT_HANDLE * & h );
void my_object_do_something(MY_OBJECT_HANDLE *h, ... );
template<T> T multiply<T>( T x, T y ) { return x*y }
multiply-template.h
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);
multiply-template.c
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
return x*y;
}
int_multiply.h
#ifndef _INT_MULTIPLY_H
#define _INT_MULTIPLY_H
#ifdef _multiply_
#undef _multiply_
#endif
#define _multiply_(NAME) int ## _ ## NAME
#ifdef _multiply_type_
#undef _multiply_type_
#endif
#define _multiply_type_ int
#include "multiply-template.h"
#endif
int_multiply.c
#include "int_multiply.h"
#include "multiply-template.c"
int int_multiply( int x, int y ) { return x * y }
void myfunc(void);
static const struct {
void(*myfunc)(void);
} mylib = {
.myfunc = myfunc
};
eg: header1.h
#ifdef LIB_FUNC_DECL
void func1(void);
#elif defined(LIB_STRUCT_DECL)
struct {
void(*func)(void);
} submodule1;
#else
.submodule1.func = func1,
#endif
mylib.h
#define LIB_FUNC_DECL
#include "header1.h"
#undef LIB_FUNC_DECL
#define LIB_STRUCT_DECL
static const struct {
#include "header1.h"
#undef LIB_STRUCT_DECL
} mylib = {
#include "header1.h"
};