3

我发现了两种将命令行参数传递到字符数组的方法:

int main (int argc, char **argv)
{
  const char *s1 = argv[0];
  char s2[256];
  strcpy(s2, argv[0]);

  printf("s1: %s\ns2: %s\n\n", s1, s2);
}

在 AIX 系统上使用 IBM xlc 编译器编译

[我的提示]> ./a.out

s1:./a.out

s2: ./a.out

哪个实现(s1 或 s2)是正确的?s1 很好,因为 argv[0] 可以是任意长度。s2 要求 argv[0] 的长度 < 256 个字符。

我不明白 s1 如何/为什么应该工作。我认为s1 的右侧应该在编译时需要,但我认为它是在运行时生成的。

4

6 回答 6

3

s1 起作用的原因是 argv[0] 的类型指针。您只是分配地址(不是实际值),这是安全的。您没有执行任何类型的分配或强制转换。

我通常更喜欢第一个选项,因为您应该只从参数变量中读取。

于 2009-07-31T19:01:58.580 回答
3

如果您不想更改字符串,则 s1 将起作用。

如果要更改字符串,则可以对其进行复制。如果您的系统支持,您应该使用更安全的 strnlen() 和 strncpy()。

于 2009-07-31T19:04:17.950 回答
1

我认为 s1 的右侧应该在编译时需要,但我认为它是在运行时生成的。

不,每次遇到语句时都需要它。例如:

void f() {
   int x = 1;
   ...
}

整数 x 将在每次调用函数时初始化为 1,而不是在编译时。

于 2009-07-31T19:12:02.443 回答
1

s2 具有易受缓​​冲区溢出影响的令人愉快的特性。

我见过人们改变 argv[0] 的值。在某些情况下,(在某些操作系统上)更改 argv[0] 将使程序在 ps 中显示为您将其更改为的任何内容。

于 2009-07-31T19:12:21.710 回答
0

如果您只想引用参数而不对其进行任何更改,那么s1是正确的。

如果您需要以任何方式修改参数,那么您需要像在s2示例中一样制作它的副本,但在s2示例中,您需要显式检查长度是否比您复制到的缓冲区长. 例如,如果您将像 filename.jpg 这样的参数作为输入并将 filename.gif 作为输出保存,那么您需要复制该参数,因为您会将扩展名从 .jpg 更改为 .gif

于 2009-07-31T19:08:39.203 回答
0

我会选择 s1,尤其是对于 n > 0 的 argv[n]。像 s2 这样的东西可以让你接受经典的缓冲区溢出攻击。基本上,用户可以格式化长度超过 256 个字符的参数并覆盖堆栈上的信息,以便他们可以运行任何他们想要的代码。

于 2009-07-31T19:14:10.050 回答