在真正的 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 上不支持),所以不可能有任何可靠的通过#ifdef
s 检测它的方法。
不是针对花哨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)的默认实现。