c - 为什么我必须重新声明一个外部函数?
问题描述
我目前正在阅读 Richard Stevens 的《Unix 环境中的高级编程》,我编写了一个基本程序来测试如何创建文件系统链接。在那之后没有问题,我决定实现一些选项,从-s
通过命令行指定创建符号链接而不是硬链接的能力开始。为此,我键入def'd link_fn
,link
除非用户指定-s
在这种情况下将其设置为 ,否则将其设置为symlink
。
问题是我必须包含extern int symlink(const char* actualpath, const char* sympath);
,否则在运行 makefile 时会出现以下错误:
link/link.c: In function ‘main’:
link/link.c:30:18: error: ‘symlink’ undeclared (first use in this function)
30 | fn = symlink;
| ^~~~~~~
link/link.c:30:18: note: each undeclared identifier is reported only once for each function it appears in
make: *** [Makefile;31: link.o] Error 1
奇怪的是我仔细检查了这本书和手册页(man 2 symlink
),他们都说symlink
函数是在unistd.h
标题中声明的。我的标题部分如下所示:
#if defined(__unix__)
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/random.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>
#include <sys/sysinfo.h>
#include <sys/termios.h>
#include <sys/types.h>
#include <sys/user.h>
#elif defined(_WIN32)
...
#endif // OS-Dependent modules
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
...
我首先确保__unix__
已定义,并且symlink
andlink
函数都可以正常工作,但前提是我声明symlink
为外部,如下所示:
extern int symlink(const char* actualpath, const char* sympath);
然后我运行gcc
该-E
选项以查看是否unistd.h
真的被包含在内,果然,它被成功包含并且symlink
功能就在那里:
extern int symlink (const char *__from, const char *__to)
__attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) ;
那么,当我自己没有声明symlink
函数原型时,为什么会出现编译器错误呢?为什么link
当它们以完全相同的方式在同一个头文件中声明时,该函数没有给我这个问题?
这是link
函数,也是我在调试时生成的预处理器输出。
extern int link (const char *__from, const char *__to)
__attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) ;
#if defined(__unix__)
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#elif defined(_WIN32)
// Removed for brevity
#endif // OS-Dependent modules
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#if !defined(FALSE) || !defined(TRUE)
enum { FALSE, TRUE };
#endif // TRUE || FALSE
// Why does the compiler require this declaration, and only for symlink?
extern int symlink(const char* actualpath, const char* sympath);
typedef int (*link_fn)(const char* path, const char* link_path);
int main(int argc, char *argv[])
{
if (argc == 2) {
if (strcmp(argv[1], "--help") == 0) {
printf("\nUsage: %s <Existing Filename> <New Filename>\n\n", argv[0]);
printf("Options: \n");
printf(" -s Create symbolic link instead of a hard link\n\n");
return EXIT_SUCCESS;
}
}
if (argc < 3) {
fprintf(stderr, "Usage: %s <Existing Filename> <New Filename>\n", argv[0]);
return EXIT_FAILURE;
}
link_fn fn = link;
for (size_t i = 1; i < (size_t) argc - 2; ++i) {
if (strcmp(argv[i], "-s") == 0) {
fn = symlink;
}
}
const char* existing_path = argv[argc - 2];
const char* new_path = argv[argc - 1];
errno = 0;
const int return_code = fn(existing_path, new_path);
if (return_code == -1) {
fprintf(stderr, "[Error] %s\n", strerror(errno));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
该程序正在编译使用:
CC=gcc
CFLAGS="-std=c17 -Wall -Wextra -Werror -pedantic"
系统信息
gcc version 9.1.0
Manjaro Linux 18.0.4
x86-64
MRE
以下是 Eric Postpischil 建议的一个小例子:
#if defined(__unix__)
#include <unistd.h>
#endif
int (*a)(const char*, const char*) = link;
int (*b)(const char*, const char*) = symlink;
int main(void)
{
//
}
运行 make 会产生以下输出:
gcc -I include -E -o link.pp link/link.c
gcc -I include -S -masm=intel -o link.asm link/link.c
gcc -std=c17 -Wall -Wextra -Werror -pedantic -I include -c -o link.o link/link.c
link/link.c:9:38: error: ‘symlink’ undeclared here (not in a function)
9 | int (*b)(const char*, const char*) = symlink;
| ^~~~~~~
make: *** [Makefile;31: link.o] Error 1
预处理后的输出如下。
1 # 1 "link/link.c"
2 # 1 "<built-in>"
3 # 1 "<command-line>"
4 # 31 "<command-line>"
5 # 1 "/usr/include/stdc-predef.h" 1 3 4
6 # 32 "<command-line>" 2
7 # 1 "link/link.c"
...
12 # 1 "/usr/include/unistd.h" 1 3 4
13 # 25 "/usr/include/unistd.h" 3 4
14 # 1 "/usr/include/features.h" 1 3 4
15 # 450 "/usr/include/features.h" 3 4
16 # 1 "/usr/include/sys/cdefs.h" 1 3 4
17 # 452 "/usr/include/sys/cdefs.h" 3 4
18 # 1 "/usr/include/bits/wordsize.h" 1 3 4
19 # 453 "/usr/include/sys/cdefs.h" 2 3 4
20 # 1 "/usr/include/bits/long-double.h" 1 3 4
21 # 454 "/usr/include/sys/cdefs.h" 2 3 4
22 # 451 "/usr/include/features.h" 2 3 4
23 # 474 "/usr/include/features.h" 3 4
24 # 1 "/usr/include/gnu/stubs.h" 1 3 4
25 # 10 "/usr/include/gnu/stubs.h" 3 4
26 # 1 "/usr/include/gnu/stubs-64.h" 1 3 4
27 # 11 "/usr/include/gnu/stubs.h" 2 3 4
28 # 475 "/usr/include/features.h" 2 3 4
29 # 26 "/usr/include/unistd.h" 2 3 4
...
32 # 202 "/usr/include/unistd.h" 3 4
33 # 1 "/usr/include/bits/posix_opt.h" 1 3 4
34 # 203 "/usr/include/unistd.h" 2 3 4
...
38 # 1 "/usr/include/bits/environments.h" 1 3 4
39 # 22 "/usr/include/bits/environments.h" 3 4
40 # 1 "/usr/include/bits/wordsize.h" 1 3 4
41 # 23 "/usr/include/bits/environments.h" 2 3 4
42 # 207 "/usr/include/unistd.h" 2 3 4
43 # 217 "/usr/include/unistd.h" 3 4
44 # 1 "/usr/include/bits/types.h" 1 3 4
45 # 27 "/usr/include/bits/types.h" 3 4
46 # 1 "/usr/include/bits/wordsize.h" 1 3 4
47 # 28 "/usr/include/bits/types.h" 2 3 4
48 # 1 "/usr/include/bits/timesize.h" 1 3 4
49 # 29 "/usr/include/bits/types.h" 2 3 4
...
53 # 31 "/usr/include/bits/types.h" 3 4
54 typedef unsigned char __u_char;
其他函数声明和枚举声明了大约一千行。然后,在第 1169 行:
1169 extern int link (const char *__from, const char *__to)
1170 __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) ;
1171
1172
1173
1174
1175 extern int linkat (int __fromfd, const char *__from, int __tofd,
1176 const char *__to, int __flags)
1177 __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 4))) ;
1178
1179
1180
1181
1182 extern int symlink (const char *__from, const char *__to)
1183 __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) ;
并跳过几百行类似的声明:
1416
1417 # 6 "link/link.c" 2
1418
1419
1420
1421 # 8 "link/link.c"
1422 int (*a)(const char*, const char*) = link;
1423 int (*b)(const char*, const char*) = symlink;
1424
1425 int main(void)
1426 {
1427
1428 }
解决方案
为了symlink
被声明,您需要#define
正确的功能测试宏,如symlink
手册页中所示:
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
symlink():
_XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
|| /* Glibc versions <= 2.19: */ _BSD_SOURCE
您应该养成使用功能测试宏的习惯,尽管如果您使用默认-std
选项或任何-std
以gnu
. gnu
“标准”定义了_GNU_SOURCE
功能测试宏,它有效地绕过了所有功能测试。某些人可能认为这很方便,但它会导致烦人的可移植性问题。
尽管将定义放在每个源文件的顶部在技术上更好,但我发现放在-D_XOPEN_SOURCE=700
my 中更方便CFLAGS
,这也保证了定义在 any 之前#include
。
另外,请注意,如果您想准确地预处理源代码,您需要确保您的 CFLAGS 对于预处理命令与编译时相同。否则,您可能会错过某些符号未使用更严格的-std
选项定义(或预定义)的事实。
推荐阅读
- sql - SQL根据值将值移动到新列
- symfony - 为什么我将这个 PHP 版本“7.2.30”作为当前的 PHP 版本?
- python - Python Regex 检查字符串 1 和字符串 2 是否具有相同的模式
- sql - SQLite 如何同时使用 UPDATE 和 LIMIT?
- javascript - 如何在 JavaScript 中的不同时区获取给定时间的本地时间
- java - 向 JTable 添加一行时,我的所有列都消失了
- python - 正则表达式返回重复结果
- r - R中是否有任何多行注释?
- android - 在 Android 11 上调用 getPackageInfo 时出现 NameNotFoundException
- java - 无法在 Thymeleaf 中显示 Spring Boot 模型数据