33

我的应用程序可能传入了大量的参数,我想避免将参数复制到过滤列表中的命中记忆。我想将它们过滤到位,但我很确定弄乱 argv 数组本身或它指向的任何数据可能是不可取的。有什么建议么?

4

8 回答 8

72

C99 标准说明了关于修改argv(and argc) 的内容:

参数 argc 和 argv 以及 argv 数组指向的字符串应该可以被程序修改,并在程序启动和程序终止之间保留它们最后存储的值。

于 2009-06-08T07:28:28.797 回答
26

一旦 argv 被传递到 main 方法中,您就可以像对待任何其他 C 数组一样对待它 - 随意更改它,只需注意您正在使用它做什么。除了您在代码中明确使用它执行的操作之外,数组的内容不会影响程序的返回代码或执行。我想不出任何理由“不建议”特别对待它。

当然,您仍然需要注意意外访问超出 argv 范围的内存。它像普通 C 数组一样可访问的另一面是,它也像任何其他普通 C 数组一样容易出现访问错误。(感谢所有在评论和其他回复中指出这一点的人!)

于 2009-06-08T05:18:37.907 回答
7

最新的 C 标准草案 (N1256) 规定该main函数有两种允许的形式:

int main (void);
int main (int argc, char* argv[]);

但关键是“或以其他一些实现定义的方式”的子句。在我看来,这似乎是标准中的一个漏洞,大到足以让一辆半挂车通过。

有些人专门使用"const char *"argv禁止更改参数。如果您的 main 函数以这种方式定义,则不允许更改argv[]指向的字符,如以下程序所示:

pax> cat qq.c
#include <stdio.h>
int main (int c, const char *v[]) {
    *v[1] = 'X';
    printf ("[%s]\n", v[1]);
    return 0;
}

pax> gcc -o qq qq.c
qq.c: In function `main':
qq.c:3: error: assignment of read-only location

但是,如果您删除"const",它可以正常工作:

pax> cat qq2.c
#include <stdio.h>
int main (int c, char *v[]) {
    *v[1] = 'X';
    printf ("[%s]\n", v[1]);
    return 0;
}

pax> gcc -o qq2 qq2.c ; ./qq2
[Xello]

我认为这也是 C++ 的情况。目前的草案规定:

All implementations shall allow both of the following definitions of main:
    int main();
    int main(int argc, char* argv[]);

但它并没有明确禁止其他变体,因此您可能也可以接受"const"C++ 中的版本(事实上,g++ 可以)。

您唯一需要注意的是尝试增加任何元素的大小。这些标准没有规定它们的存储方式,因此扩展一个参数可能(可能会)影响其他参数或其他一些不相关的数据。

于 2009-06-08T06:50:55.410 回答
4

根据经验,诸如 GNU getopt() 之类的函数可以置换参数列表而不会引起问题。正如@Tim 所说,只要您明智地玩,就可以操纵指针数组,甚至可以操纵单个字符串。只是不要超出任何隐式数组边界。

于 2009-06-08T05:22:23.707 回答
2

操作系统在执行之前将 argv 和 argc 推送到应用程序堆栈中,您可以像对待任何其他堆栈变量一样对待它们。

于 2009-06-08T07:48:18.273 回答
2

一些图书馆这样做!

由 glut opengl 库(GlutInit)提供的初始化方法扫描 glut 相关参数,并通过向前移动后续元素argv(移动指针,而不是实际字符串)和递减 argc来清除它们

2.1

glutInit glutInit 用于初始化 GLUT 库。

用法

void glutInit(int *argcp, char **argv);

argcp

从 main 指向程序未修改的 argc 变量的指针。返回时, argcp 指向的值将被更新,因为 glutInit 提取任何用于 GLUT 库的命令行选项。

argv

程序未修改的来自 main 的 argv 变量。与 argcp 一样,argv 的数据将被更新,因为 glutInit 提取 GLUT 库可以理解的任何命令行选项。

于 2018-05-18T09:03:15.580 回答
1

唯一一次我会说直接操作 argv 是一个主意是当应用程序根据 argv[0] 的内容改变其行为时。

但是,在考虑可移植性的情况下,根据 argv[0] 更改程序的行为本身就是一个非常糟糕的主意。

除此之外,您可以像对待任何其他阵列一样对待它。正如乔纳森所说,GNU getopt() 非破坏性地置换参数列表,我已经看到其他 getopt() 实现可以序列化甚至散列参数(当程序接近 ARG_MAX 时很有用)。

小心你的指针算术。

于 2009-06-08T05:56:38.203 回答
1

的原始分配argv留作编译器/运行时选择。因此,随意修改它可能不安全。许多系统在堆栈上构建它,因此当 main 返回时它会自动释放。其他人在堆上构建它,并在 main 返回时释放它(或不释放它)。

只要您不尝试使其更长(缓冲区溢出错误),更改参数的值是安全的。打乱参数的顺序是安全的。

删除您已预处理的参数,可以使用以下方法:

(许多错误条件未检查,“--special”其他未检查第一个 arg 等。这毕竟只是一个概念演示。)

int main(int argc, char** argv)
{
    bool doSpecial = false; // an assumption
    if (0 == strcmp(argv[1], "--special"))
    {
        doSpecial = true; // no longer an assumption
        // remove the "--special" argument
        //  but do copy the NULL at the end.
        for(int i=1; i<argc; ++i)
            argv[i]  = argv[i+1];
        --argc;
    }
    // all normal processing with "--special" removed.
    // the doSpecial flag is available if wanted.
    return 0;
}

但是请参阅此进行完整操作:(用于操作 argv 样式向量的libiberty库的一部分)

http://www.opensource.apple.com/source/gcc/gcc-5666.3/libiberty/argv.c

它已获得 GNU LGPL 许可。

于 2016-02-10T14:53:43.257 回答