理解C语言命名空间

34

以下内容摘自这里

C语言中,有两个不同的类型命名空间:结构体/联合体/枚举标签名称的命名空间和typedef名称的命名空间。

name.c

$ cat name.c
#include<stdio.h>

typedef long long long2;

int long2 () {
    return 4;
}

int main() {

    printf("hello, world!");
    return 0;
}
$ gcc name.c -o name
name.c:4: error: 'long2' redeclared as different kind of symbol
name.c:3: error: previous declaration of 'long2' was here
$

name2.c

$ cat name2.c
#include<stdio.h>

int four() {
    return 4;
}

struct dummy {
    int member;
};

int main() {

    struct dummy four;
}

$ gcc name2.c -o name2
$ 

我正在尝试理解C语言中的命名空间冲突。

  • 在第一个例子中,为什么会产生冲突?函数是否也属于typedef命名空间?

  • 在第二个例子中,为什么根本没有冲突?函数和变量都被命名为four。编译器为什么允许这样做?&four应该如何解析?

4个回答

42

C语言有四种不同的标识符名称空间:

  • 标签名称(类似于goto语句)
  • 结构体、联合体和枚举类型的名称
  • 结构体和联合体成员的名称(每个结构体/联合体有一个单独的名称空间)
  • 所有其他标识符的名称(函数名、对象名、类型(def)名、枚举常量等)

请参见C99 6.2.3。

因此,你的两个问题可以回答如下:

  1. 是的,函数名和typedef名称共享相同的名称空间。
  2. 没有冲突,因为编译器将使用作用域规则(用于函数或对象名)。在main函数中的标识符被称为“遮蔽”全局函数名,如果你设置警告级别足够高,编译器会警告你。

1
结构体/联合体/枚举类型是否共享一个命名空间?也就是说,struct Tunion T能否同时存在? - iBug
1
iBug,不行,因为结构体、联合体和枚举共享相同的命名空间(标签命名空间)。 - dandev486

8

你的第二个示例并没有展示“无冲突”。实际上存在冲突!请尝试以下代码:

#include <stdio.h>
int four(void) { return 4; }
struct dummy { int member; };
int main(void) {
    struct dummy four;
    four.member = four();
}

现在是这样的

#include <stdio.h>
int four(void) { return 4; }
struct dummy { int member; };
int main(void) {
    int (*fx)(void) = four; /* "save" function */
    struct dummy four;     /* hide it         */
    four.member = fx();    /* use "hidden" fx */
}

在您的第二个示例中,变量four隐藏了函数four()

8

但您的例子中关键点不在于命名空间,而在于名称的作用域。

name.c 中,两个 long2 都是“普通标识符”(共享相同的名称空间),并且它们都定义在相同的作用域中,因此存在冲突。(C99 §6.7/3)

如果在 name2.c 中,局部变量 four 的作用域比函数 four 更深,因此变量 隐藏 了函数 four。(C99 §6.2.1/4)


1

为了快速回答您的两个问题。

Q1. 在第一种情况下,为什么会发生冲突?函数是否也属于typedef命名空间?

A1. 是的,函数和typedef类型的标识符共享相同的命名空间。因此会出现冲突。

Q2. 在第二种情况下,为什么根本没有冲突?函数和变量都被命名为four。编译器为什么允许这样做?&four应该如何解决?

A2. 即使在第二个示例中,两个名为four的标识符属于同一个标识符命名空间(即Ordinary namespace),也不存在冲突。因为主函数中的变量标识符four位于函数作用域中,而函数标识符four位于文件作用域中。因此,后者被前者隐藏,遵循基本的作用域规则。如果将变量four移动到文件作用域(全局)中,则会出现错误。

...

struct dummy four; // Error! there's a previous definition
                   // in this file scope(global).

int main() {
    // struct dummy four; // Commented out.
}

实际上,有四个不同的标识符命名空间:Label、Tag、Member和Ordinary。你可以在https://en.cppreference.com/w/c/language/name_space中找到关于它们的详细信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接