对于大多数语言来说,拥有命名空间似乎是轻而易举的事。但据我所知,ANSI C 不支持它。为什么不?是否有计划将其包含在未来的标准中?
10 回答
为了完整起见,有几种方法可以在 C 中实现您可能从命名空间中获得的“好处”。
我最喜欢的方法之一是使用一个结构来容纳一堆方法指针,它们是你的库/等的接口。
然后你使用这个结构的外部实例,你在你的库中初始化它指向你的所有函数。这使您可以在库中保持名称简单,而无需踩到客户端名称空间(除了全局范围内的外部变量,1 个变量与可能数百个方法..)
涉及一些额外的维护,但我觉得它很少。
这是一个例子:
/* interface.h */
struct library {
const int some_value;
void (*method1)(void);
void (*method2)(int);
/* ... */
};
extern const struct library Library;
/* 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 */
指某东西的用途 。语法在经典 Library_function() Library_some_value 方法上创建了强关联。但是有一些限制,因为您不能将宏用作函数。
C 确实有命名空间。一种用于结构标签,一种用于其他类型。考虑以下定义:
struct foo
{
int a;
};
typedef struct bar
{
int a;
} foo;
第一个有标签foo,后一个用 typedef 做成 foo 类型。仍然没有发生名称冲突。这是因为结构标记和类型(内置类型和 typedef 类型)存在于不同的命名空间中。
C 不允许按意愿创建新的命名空间。C 在被认为在语言中很重要之前已经标准化,并且添加名称空间也会威胁向后兼容性,因为它需要名称修改才能正常工作。我认为这可以归因于技术性,而不是哲学。
编辑:JeremyP 幸运地纠正了我并提到了我错过的命名空间。标签和结构/联合成员也有命名空间。
C 有命名空间。语法是namespace_name
. 您甚至可以将它们嵌套在general_specific_name
. 如果您希望能够在不每次都写出命名空间名称的情况下访问名称,请在头文件中包含相关的预处理器宏,例如
#define myfunction mylib_myfunction
这比名称修改和某些语言承诺提供命名空间的其他暴行要干净得多。
从历史上看,C 编译器不会修改名称(它们在 Windows 上进行,但cdecl
调用约定的修改仅包括添加下划线前缀)。
extern "C"
这使得使用其他语言(包括汇编程序)的 C 库变得容易,这也是您经常看到C++ API 包装器的原因之一。
只是历史原因。当时没有人想到有像命名空间这样的东西。他们也真的试图保持语言简单。他们将来可能会拥有它
ANSI C 是在命名空间之前发明的。
不是答案,但不是评论。C 没有提供namespace
明确定义的方法。它具有可变范围。例如:
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
也不能 import from mylib
。
因为想要将这种能力添加到 C 语言中的人们还没有团结起来,没有组织起来对编译器作者团队和 ISO 机构施加一些压力。
C 不支持像 C++ 这样的命名空间。C++ 命名空间的实现会破坏名称。下面概述的方法允许您在 C++ 中使用名称空间的好处,同时具有未损坏的名称。我意识到问题的本质是为什么 C 不支持命名空间(一个简单的答案是它不支持,因为它没有实现:))。我只是认为它可能会帮助某人了解我是如何实现模板和命名空间的功能的。
我写了一篇关于如何使用 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 }
使用模板文件如下
乘法模板.h
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);
乘法模板.c
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
return x*y;
}
我们现在可以定义 int_multiply 如下。在此示例中,我将创建一个 int_multiply.h/.c 文件。
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 }
我在提供的链接上创建了一个更详细的教程,展示了它如何与链接列表一起使用。希望这可以帮助某人!
你可以。像其他人的答案一样,在结构中定义函数指针。
但是,在你的头文件中声明它,将它标记为 static const 并使用相应的函数对其进行初始化。使用 -O1 或更高版本,它将被优化为正常的函数调用
例如:
void myfunc(void);
static const struct {
void(*myfunc)(void);
} mylib = {
.myfunc = myfunc
};
利用#include 语句,您无需在一个标头中定义所有函数。
不要添加标题保护,因为您不止一次包含它。
例如: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"
};