strptime()函数的函数声明出现问题

3
我有一个时间日期字符串,希望将其转换为tm对象。谷歌告诉我POSIX标准(但不是C语言)包含一个称为strptime()的函数可以完成此工作。
手册上说它在中,并且我需要在包含文件之前包含#define _XOPEN_SOURCE。很容易理解。
但是,编译器仍然会发出隐式声明警告。我打开了/usr/include/time.h并找到了函数声明:
 # ifdef __USE_XOPEN
/* Parse S according to FORMAT and store binary time information in TP.
   The return value is a pointer to the first unparsed character in S.  */
extern char *strptime (const char *__restrict __s,
               const char *__restrict __fmt, struct tm *__tp)
     __THROW;
#endif

看起来我需要使用 #define _USE_XOPEN

但这也不管用。编译器仍然没有看到声明。

有什么想法吗?我正在使用一个相对较新的Linux版本(Mint),带有gcc 5.4.0。


谢谢约翰,但是这些建议都似乎没能解决问题。我不太明白为什么编译器看不到声明,因为我检查过它确实在time.h文件中。 - simplicio
1
#define _XOPEN_SOURCE 700 / #include <time.h> / #include <stdio.h> / int main(void) { printf("%p\n", (void *)strptime); return 0; } — 如果您使用 gcc -std=c11 -Wall -c test-strptime.c 编译时出现错误,那么您可以尝试添加 -ansi 参数将标准重置为 C90。GCC 5.4.0 应默认使用 C11(实际上是 -std=gnu11),除非您使用的构建中有人做了一些可怕的事情(这是不太可能的)。 - Jonathan Leffler
1
你的第一段代码确实编译通过了。但有趣的是,如果我在 #define 之前加上 #include stdio.h,它就不能编译通过。所以我认为是 include 语句相对于定义语句的顺序引起了问题。我会在我的代码中继续尝试并报告结果。 - simplicio
4
#define _XOPEN_SOURCE 700 必须在第一个系统头文件之前(无论是被直接包含还是间接包含)。 - Jonathan Leffler
1
另一个选择是下载公共领域实现的 strptime 并添加到您的项目中。 - M.M
显示剩余2条评论
1个回答

9
将注释转换为答案。
为了解决这个问题,你可以在GCC命令行上使用-std=gnu11而不是-std=c11,或者使用#define _XOPEN_SOURCE 700或其等效项(例如,在命令行上使用-D_XOPEN_SOURCE=700)。700代表POSIX 2008;600或500则代表较早版本的POSIX或X/Open。
理论上,您也可以使用_POSIX_C_SOURCE 200809L(请参见POSIX 编译环境),但它不会暴露_XOPEN_SOURCE 700所暴露的所有内容,因此通常最好使用后者。
请注意,POSIX规范中的strptime()被注释为XSI扩展,这意味着您必须设置_XOPEN_SOURCE;仅设置_POSIX_C_SOURCE是不够的。
测试代码
此测试代码打印strptime函数的地址;如果未声明strptime(),则无法编译。
#define _XOPEN_SOURCE 700
#include <time.h>
#include <stdio.h>

int main(void)
{
    printf("%p\n", (void *)strptime);
    return 0;
}

使用gcc -std=c11 -Wall -c test-strptime.c命令可以为您编译。如果在选项中添加-ansi,则将标准重置为C90。GCC 5.4.0应默认设置为C11(实际上是-std=gnu11),除非您使用的GCC版本构建时发生了不良情况(这不太可能)。

请注意,编译器基于类似_XOPEN_SOURCE的设置取消并重新设置__USE_XOPEN,手动设置它不能可靠地工作。

位置很重要

您必须在第一次包含系统头文件之前(无论是直接包含还是间接包含),指定#define _XOPEN_SOURCE 700。如果在尝试设置_XOPEN_SOURCE之前包含系统头文件,则设置已被确定,并且后续操作实际上被忽略。 POSIX在“编译环境”链接中已经说明:

在编译定义了由POSIX.1-2008指定的特性测试宏的应用程序时,在定义特性测试宏之前不得包括POSIX.1-2008定义的任何头文件。此限制也适用于使用这些特性测试宏的任何实现提供的头文件。如果宏的定义不在#include之前,则结果是未定义的。

常见的未定义结果之一是您尝试设置/更改POSIX版本时完全被忽略。


在同一构建中,使用不同的源定义来处理不同的单元是否有效,还是它们应该全部相同? - M.M
@M.M:我认为你正在涉及“未指定的行为”。通常情况下,你可以逃脱惩罚,因为标准(POSIX或C)接口不会发生变化——一旦它们被标准化,它们就会保持不变,直到它们从标准中删除。即使在这种情况下,它们可能仍然存在很长时间,即使它们不再是标准(gets()任何人?)。对于诸如strptime()strftime()之类的函数,直接的C接口不会改变,但是接受的格式选项集会改变。这也影响了C和printf() - Jonathan Leffler

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