在真正的 argv 上使用它之后,我将机器瞄准getopt()我自己的类似 argv 的阵列。该接口是不可重入的(保持状态),并且各种实现以不同的方式重置。
- 在 XPG3/SVID 中是
optreset = 1;(freebsd, macosx) - 在 XPG4/POSIX/SUS 中是
optind = 1;(suse tumbleweed) - 在 Linux/GLIBC 它也是
optind = 0;(debian)
我不想仅仅为此而自动配置。
什么是可靠的集合#ifdef?
唯一的标准就是设置optind = 1。
SUSv4标准没有提及optreset并声明optind = 0“未指定”:
如果应用程序
optind在调用之前设置为零getopt(),则行为未指定。
否则,它的标准就含糊不清,根本没有说任何关于getopt()多次调用的事情,既不允许它也没有声明它未定义。
此外,没有定义说是否支持optind = 0或optreset支持(fwiw 前者在 OpenBSD 上受支持,但在 FreeBSD 上不支持,后者在 Linux/musl 上,但在 Linux/glibc 上不支持),所以不可能有任何可靠的通过#ifdefs 检测它的方法。
不是针对花哨getopt/_long实现中的错误(见下文),只要设置optind = 1为重新启动就getopt()可以正常工作
a)你不使用任何GNU 扩展:
扫描多个参数向量或多次重新扫描同一向量的程序,并希望在 optstring 的开头 使用 GNU 扩展,例如
+和,或更改两次 扫描之间-POSIXLY_CORRECT的值,必须getopt()通过重置optind为0
b)getopt()在它返回之前,您不会在中间重新启动-1。例如。来自FreeBSD 的getopt:
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
...
int
getopt(int nargc, char * const nargv[], const char *ostr)
{
static char *place = EMSG; /* option letter processing */
...
if (optreset || *place == 0) { /* update scanning pointer */
optreset = 0;
...
place = EMSG;
return (-1);
...
place = EMSG;
return (-1);
...
place = EMSG;
if (strchr(ostr, '-') == NULL)
return (-1);
...
return (BADCH);
...
return (BADARG);
...
return (BADCH);
...
return (optopt); /* return option letter */
}
即使在返回之后,glibc 的实现getopt也会保持陈旧状态-1。如果释放旧字符串,这可能会导致愚蠢的错误和崩溃。
OpenBSD 实现也会这样做,但仅在使用虚假-选项时,如getopt("q", ["prog", "-q-", NULL]). 请注意,OpenBSD 的getopt_long实现可能已经getopt()成为其他系统(如 Solaris 和 Android)的默认实现。