了解标识符的链接
Understanding linkage of identifiers
我正在阅读 the Standard: N1570 并且遇到了一些误解。我写了下面这个简单的例子:
test.h
:
#ifndef TEST_H
#define TEST_H
extern int second;
#endif //TEST_H
test.c
:
#include "test.h"
enum test_enum{
first,
second
};
但是编译失败,报错:
error: ‘second’ redeclared as different kind of symbol
second
^~~~~~
这很奇怪,因为第 6.4.4.3#2
节指定:
2 An identifier declared as an enumeration constant has type int.
在我们的案例中,枚举常量具有文件作用域,因此我希望它能够正常编译。
我把上面的例子重写如下:
main.c
:
extern int second;
int main(int argc, char const *argv[])
{
printf("Second: %d\n", second);
}
现在链接器抱怨:
undefined reference to `second'
为什么?它应该在 test.c
中找到定义,因为 Section 6.2.2#5
指定:
If the declaration of an identifier for an object has file scope and
no storage-class specifier, its linkage is external.
要完成您正在尝试的操作,您只需稍微重构一下代码,这样 test.[ch]
就不会看到 second
的重新声明。问题是符号 second
一次被定义为 extern int second;
,然后又被定义为 enum
中的符号。您不能在同一个文件中同时显示两者。
要做到这一点,您可以使用类似于以下条件的第二个预处理器来编写 test1.h
:
#ifndef TEST_H
#define TEST_H
#ifdef USE_ENUM
enum test_enum{
first,
second
};
#else
extern int second;
#endif
#endif
根据是否定义USE_ENUM
,代码将使用enum
提供的符号,如果没有,则需要定义[=81] =] second
在 test1.c
#include "test1.h"
#ifdef USE_ENUM
char stub (void) /* stub to prevent empty compilation unit */
{ return 0; }
#else
int second = 2;
#endif
(注意使用 stub
函数来防止 空编译单元 如果定义了 USE_ENUM
——因为在test1.c
否则)
现在所需要做的就是在包含 main()
的文件中包含 test1.h
并通过编译器将 -DUSE_ENUM
定义为编译器选项,具体取决于您要使用的代码,例如
#include <stdio.h>
#include "test1.h"
int main (void) {
printf ("second: %d\n", second);
}
使用 test.c
中定义的 int second
进行编译
示例:
$ gcc -Wall -Wextra -pedantic -std=c11 -o bin/main1 main1.c test1.c
例子Use/Output
如果未定义 USE_ENUM
,则在 test1.c
中定义并通过 extern
访问的 second
的定义将导致 second
具有值2
,例如
$ ./bin/main1
second: 2
使用 test.h
中定义的 enum
进行编译
示例:
$ gcc -Wall -Wextra -pedantic -std=c11 -o bin/main1 main1.c test1.c -DUSE_ENUM
例子Use/Output
当定义USE_ENUM
时,符号second
的值由test1.h
中的enum
提供,例如
$ ./bin/main1
second: 1
虽然这是对您尝试进行的轻微重构,但我看不到不使用预处理器条件的另一种方法。
对象和常量是不同的东西
你对 6.4.4.3 2 的引用,枚举常量的类型为 int
,表明你认为因为 extern int second
和 enum { second }
声明 second
是一个int
,second
的这两个声明可能指的是同一件事。这是不正确的。
extern int second
将 second
声明为将保存 int
的对象(内存区域)的名称。 enum { second }
将 second
声明为枚举常量,该常量将具有特定值。枚举常量没有与之关联的对象(没有内存)。 int
对象和 int
常量是不同的东西,您不能在同一范围内对它们使用相同的标识符。
并非所有声明都是定义
关于您关于 link 错误的问题,“未定义对‘second’的引用”,尽管 test.c
可能包含 external int second
(因为它是由包含的 test.h
,这不是 second
的定义。它只是一个声明,它告诉编译器该名称引用一个对象。它不定义对象。或者,如果 test.c
包含 enum { second }
, 这只是声明 second
是一个常量。它没有定义一个对象。
由于编程语言发展的历史,定义的规则有点复杂。对于在文件范围内声明的对象的标识符,基本上有四种情况:
- 带
extern
的声明只是声明,不是定义。示例:extern int second;
.
- 带有初始值设定项的声明是一个定义。示例:
int second = 2;
.
- 没有
extern
且没有初始值设定项的声明是暂定定义。如果翻译单元(正在编译的源文件,包含所有包含的文件)中没有出现定义,则暂定定义成为定义。示例:int second;
.
link年龄在这里没有帮助。 test.c
中的extern int second
和main.c
中的extern int second
可能由于linkage引用了同一个对象,但是没有定义它们引用的对象.或者,换句话说,如果 test.c
包含 enum { second }
,那么它没有定义一个名为 second
的对象,因此 [=38= 中的 extern int second
没有对象] 可以参考。
我正在阅读 the Standard: N1570 并且遇到了一些误解。我写了下面这个简单的例子:
test.h
:
#ifndef TEST_H
#define TEST_H
extern int second;
#endif //TEST_H
test.c
:
#include "test.h"
enum test_enum{
first,
second
};
但是编译失败,报错:
error: ‘second’ redeclared as different kind of symbol
second
^~~~~~
这很奇怪,因为第 6.4.4.3#2
节指定:
2 An identifier declared as an enumeration constant has type int.
在我们的案例中,枚举常量具有文件作用域,因此我希望它能够正常编译。
我把上面的例子重写如下:
main.c
:
extern int second;
int main(int argc, char const *argv[])
{
printf("Second: %d\n", second);
}
现在链接器抱怨:
undefined reference to `second'
为什么?它应该在 test.c
中找到定义,因为 Section 6.2.2#5
指定:
If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.
要完成您正在尝试的操作,您只需稍微重构一下代码,这样 test.[ch]
就不会看到 second
的重新声明。问题是符号 second
一次被定义为 extern int second;
,然后又被定义为 enum
中的符号。您不能在同一个文件中同时显示两者。
要做到这一点,您可以使用类似于以下条件的第二个预处理器来编写 test1.h
:
#ifndef TEST_H
#define TEST_H
#ifdef USE_ENUM
enum test_enum{
first,
second
};
#else
extern int second;
#endif
#endif
根据是否定义USE_ENUM
,代码将使用enum
提供的符号,如果没有,则需要定义[=81] =] second
在 test1.c
#include "test1.h"
#ifdef USE_ENUM
char stub (void) /* stub to prevent empty compilation unit */
{ return 0; }
#else
int second = 2;
#endif
(注意使用 stub
函数来防止 空编译单元 如果定义了 USE_ENUM
——因为在test1.c
否则)
现在所需要做的就是在包含 main()
的文件中包含 test1.h
并通过编译器将 -DUSE_ENUM
定义为编译器选项,具体取决于您要使用的代码,例如
#include <stdio.h>
#include "test1.h"
int main (void) {
printf ("second: %d\n", second);
}
使用 test.c
int second
进行编译
示例:
$ gcc -Wall -Wextra -pedantic -std=c11 -o bin/main1 main1.c test1.c
例子Use/Output
如果未定义 USE_ENUM
,则在 test1.c
中定义并通过 extern
访问的 second
的定义将导致 second
具有值2
,例如
$ ./bin/main1
second: 2
使用 test.h
enum
进行编译
示例:
$ gcc -Wall -Wextra -pedantic -std=c11 -o bin/main1 main1.c test1.c -DUSE_ENUM
例子Use/Output
当定义USE_ENUM
时,符号second
的值由test1.h
中的enum
提供,例如
$ ./bin/main1
second: 1
虽然这是对您尝试进行的轻微重构,但我看不到不使用预处理器条件的另一种方法。
对象和常量是不同的东西
你对 6.4.4.3 2 的引用,枚举常量的类型为 int
,表明你认为因为 extern int second
和 enum { second }
声明 second
是一个int
,second
的这两个声明可能指的是同一件事。这是不正确的。
extern int second
将 second
声明为将保存 int
的对象(内存区域)的名称。 enum { second }
将 second
声明为枚举常量,该常量将具有特定值。枚举常量没有与之关联的对象(没有内存)。 int
对象和 int
常量是不同的东西,您不能在同一范围内对它们使用相同的标识符。
并非所有声明都是定义
关于您关于 link 错误的问题,“未定义对‘second’的引用”,尽管 test.c
可能包含 external int second
(因为它是由包含的 test.h
,这不是 second
的定义。它只是一个声明,它告诉编译器该名称引用一个对象。它不定义对象。或者,如果 test.c
包含 enum { second }
, 这只是声明 second
是一个常量。它没有定义一个对象。
由于编程语言发展的历史,定义的规则有点复杂。对于在文件范围内声明的对象的标识符,基本上有四种情况:
- 带
extern
的声明只是声明,不是定义。示例:extern int second;
. - 带有初始值设定项的声明是一个定义。示例:
int second = 2;
. - 没有
extern
且没有初始值设定项的声明是暂定定义。如果翻译单元(正在编译的源文件,包含所有包含的文件)中没有出现定义,则暂定定义成为定义。示例:int second;
.
link年龄在这里没有帮助。 test.c
中的extern int second
和main.c
中的extern int second
可能由于linkage引用了同一个对象,但是没有定义它们引用的对象.或者,换句话说,如果 test.c
包含 enum { second }
,那么它没有定义一个名为 second
的对象,因此 [=38= 中的 extern int second
没有对象] 可以参考。