【问题标题】:Iterating over argv[] produces segmentation fault遍历 argv[] 会产生分段错误
【发布时间】:2015-12-15 22:12:43
【问题描述】:

我正在编写一个从命令行读取“标志”以及程序名称的程序。我希望程序读取几个标志(-i-c-n)并在调用一个或多个标志时执行不同的函数。

这是我开始写的代码:

  printf("Argv 0: %s\n", argv[0]);
  printf("Argv 1: %s\n", argv[1]);
  printf("Argv 2: %s\n", argv[2]);

  for (int i = 1; i <= argc + 1; i++) {
    if (strcmp("-i", argv[i]) == 0) {
      printf("%s\n", "found -i"); 
    }
    else{
      printf("%s\n", "did not find -i");
    }

  }

只是尝试使用一个标志(-i),但我希望它同时读取一个或多个标志并调用相应的函数。

当我执行程序时:

./program-name test -i
Argv 0: test
Argv 1: -i
Argv 2: (null)
found -i
Segmentation fault 

【问题讨论】:

  • 不知道定义,i &lt;= argc + 1会不会导致越界访问?
  • @MikeCAT 一句话:不,但strcmp("-i", argv[argc]) 可以。

标签: c loops argv argc


【解决方案1】:

为了简化,argc 是命令行参数的计数,参数保存在argv[] 中。由于 C 数组具有基于 0 的索引,因此在您的代码中,您需要更改

 for (int i = 1; i <= argc + 1; i++)

for (int i = 1; i < argc;  i++)

限制对有效参数列表的访问。

为此添加一点参考,引用C11,第 §5.1.2.2.1 章,程序启动强调我的

如果argc的值大于零,则argv[0]指向的字符串 代表程序名称; argv[0][0] 应为空字符,如果 程序名称在主机环境中不可用。如果argc 的值为 大于一,argv[1]argv[argc-1] 指向的字符串 表示程序参数

【讨论】:

  • 确定吗?参照。我在上面的评论。
  • @PeterA.Schneider 好吧,从这个角度来看,argv[argc] 不是 有效“程序参数”。 (注意使用 valid
  • 并从同一位置进一步引用 ;-):argv[argc] 应为空指针。但不是有效的参数,是的。
  • 只想指出,OP 的 for 循环 关闭 两个
【解决方案2】:

不要重做已被证明可靠的旧代码。它浪费时间并重复努力。作为一名长期的 C 编码员,我总是(当我记得时)在尝试从头开始之前先查看代码库。我先重新调整用途,然后再创建。我很聪明,知道在我之前有很多更聪明的编码员。

使用免费且有效的 getopt() 和 getopt_long() 函数为您解析命令行参数。如果你用谷歌搜索“getopt example”but here is the GNU C tutorial,代码库中有很多示例。

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main (int argc, char **argv)
{
  int aflag = 0;
  int bflag = 0;
  char *cvalue = NULL;
  int index;
  int c;

  opterr = 0;

  while ((c = getopt (argc, argv, "abc:")) != -1)
    switch (c)
      {
      case 'a':
        aflag = 1;
        break;
      case 'b':
        bflag = 1;
        break;
      case 'c':
        cvalue = optarg;
        break;
      case '?':
        if (optopt == 'c')
          fprintf (stderr, "Option -%c requires an argument.\n", optopt);
        else if (isprint (optopt))
          fprintf (stderr, "Unknown option `-%c'.\n", optopt);
        else
          fprintf (stderr,
                   "Unknown option character `\\x%x'.\n",
                   optopt);
        return 1;
      default:
        abort ();
      }

  printf ("aflag = %d, bflag = %d, cvalue = %s\n",
          aflag, bflag, cvalue);

  for (index = optind; index < argc; index++)
    printf ("Non-option argument %s\n", argv[index]);
  return 0;
}

【讨论】:

  • 谢谢! getopt是要走的路! :D
【解决方案3】:

您超出了argv 的范围; for 循环的条件不正确。应该是i &lt; argc

另外,为什么要重新发明轮子?查看getopt()

【讨论】:

  • 我不认为这是正确的。 argv[argc] 是有效的(它的值始终为 null)——这就是 dereferencing 它失败的原因。
  • @PeterA.Schneider i &lt; argc 不包括argv[argc]
  • @i486 没错。 OP 的循环是正确的,但是是多余的。他必须测试 argv[i] 是否为 null,如果是则不取消引用(argv[argc] 就是这种情况。
  • @PeterA.Schneider 我不明白你的意思。该程序的参数从argv[0]argv[argc-1]。由于argv[1] 是程序的调用名称,所以明智的做法是设置一个循环,从1 迭代到argc - 1(含)。
  • (我想你想说“因为 argv[0] 是程序的调用名称”。)你是对的,循环应该只运行到 argc-1 .但是表达式argv[argc] 在表达式中使用是有效的,并且根本不会“超出argv 的范围”。 argv 足够长并且确实有argc+1 元素。只是最后一个元素argv[argc] 为空,因此*argv[argc] 执行*argv[argc] 尝试取消引用空指针,因此崩溃在具有内存保护的系统上。
【解决方案4】:

使用您的代码和循环,这一行是错误的:

(strcmp("-i", argv[i])

当你将 argv[i] 传递给 strcmp 时,你应该确保它不为 NULL。已知该函数会以 NULL 指针作为参数进行段错误。您应该更好地测试 argv[i] != NULL 或将 i

【讨论】:

    猜你喜欢
    • 2013-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-27
    • 2015-09-03
    相关资源
    最近更新 更多