【问题标题】:How do you convert parameters from char to int in the main function for C?如何在 C 的主函数中将参数从 char 转换为 int?
【发布时间】:2017-08-15 06:00:16
【问题描述】:

我有这个代码:

int main(int argc, char *argv[]) {
    int num = *argv[1];

当我在终端中运行带有参数的函数时:例如,如果我要调用./main 17,我想要num = 17。但是,使用此代码,num = 49(ASCII 值为 1,因为argv 是一个字符数组)。我怎样才能将num = 17 读取为int?玩弄代码,我可以让它将参数转换为 int,但它仍然只会读取/转换第一个值(1 而不是 17)。

我是 C 新手,指针/指向数组的指针的概念仍然让我感到困惑。 *argv[1] 不应该返回数组中第二个字符的值吗?为什么它改为读取数组中第二个字符的第一个值?

感谢您的帮助!

【问题讨论】:

  • argv[1] 是指向 char 的指针,其中包含第一个完整参数。
  • if (argc > 1) { int num = atoi(argv[1]); }
  • 参数是字符串。您需要将它们转换为数字,可能使用atoi() 或其亲属之一,或者可能使用strtol() 或其亲属之一。后者需要相当小心才能从中获得任何好处。
  • @FilipKočicaWhy not to use atoi
  • 有关管理strtol() 所需的“相当谨慎”的信息,请参阅Correct usage of strtol()

标签: c arrays pointers


【解决方案1】:

如何将参数从 char 转换为 int?

可以通过简单的演员表(促销)来完成,但这不是你的情况。

在您的情况下,*argv[]指向 char 的指针数组(您可以使用 this 来分解复杂的 C 声明),这意味着 argv[1] 是数组中的第二个元素,即数组中的第二个char*,意味着*argv[1]是数组中第二个char*中的第一个char

为了更清楚地显示它,假设argv 包含2 个字符串{"good", "day"}argv[1]"day"*argv[1]'d'(注意类型的区别 - charchar*!)

现在,输入字符串中的第一个char'1'。它的 ascii 确实是 49 as,所以为了得到它的 "int" 值,你应该像这样使用 atoi

int i = atoi("17");

但是 atoi 得到const char *,因此向它提供17 是一个好主意,而向它发送char 则不会。这意味着atoi 应该得到argv[1] 而不是*argv[1]

int main(int argc, char *argv[]) {
     int num = atoi(argv[1]);
     // not    : int num = *argv[1];        -->  simple promotion that would take the ascii value of '1' :(
     // and not: int num = atoi(*argv[1]);  -->  argument is char

注意:atoi 被认为已过时,因此您可能希望使用 long int strtol(const char *str, char **endptr, int base),但对于一个简单的示例,我更喜欢使用 atoi

【讨论】:

    【解决方案2】:

    不应该*argv[1]返回数组中第二个字符的值吗?

    看签名:

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

    这里,argv 是一个 数组 ([]),由 指针 (*) 到 char。所以argv[1] 是这个数组中的第二个指针。它指向命令行中给出的 first 参数。 argv[0] 保留给程序本身的名称。虽然这也可以是任何字符串,但程序的名称​​按照惯例放在那里(shell 会这样做)。

    如果你只是取消引用一个指针,你会得到它指向的值,所以*argv[1] 会给你第一个参数的第一个字符。你可以写成argv[1][0],它们是等价的。要获取第一个参数的 第二个 字符,您可以编写 argv[1][1]


    这里要注意的重要一点是,您永远不能将数组传递给 C 中的函数。上面的签名显示了数组类型,但 C 会在函数声明中自动将数组类型调整为指针类型.这导致以下声明:

    int main(int argc, char **argv)
    

    C 中的索引运算符 ([]) 根据指针算法工作:a[x] 等价于 *(a+x)。数组的标识符被评估为指向第一个数组元素的指针在大多数情况下(例外包括sizeof 运算符)。因此,无论a 是数组还是指针,索引的工作原理都是一样的。这就是为什么您可以将argv 视为非常类似于数组的原因。


    解决您的“核心”问题:argv 中总是会有 字符串,并且您需要数字输入,这意味着您必须将字符串转换为数字。已经有这样做的功能。一个很简单的就是atoi(),你可以这样使用:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char **argv)
    {
        if (argc != 2)
        {
            // use program name in argv[0] for error message:
            fprintf(stderr, "Usage: %s [number]\n", argv[0]);
            return EXIT_FAILURE;
        }
        int i = atoi(argv[1]);
        printf("Argument is %d.\n", i);
        return EXIT_SUCCESS;
    }
    

    如果参数不能被解析为数字,这会给你0,如果它溢出你的int,它会给你一些不确定的值。如果您必须确保参数 一个有效的整数,您可以改用strtol()(注意它转换为long,而不是int,它可以处理不同的基数,所以我们必须通过10 来表示十进制):

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    int main(int argc, char **argv)
    {
        if (argc != 2)
        {
            // use program name in argv[0] for error message:
            fprintf(stderr, "Usage: %s [number]\n", argv[0]);
            return EXIT_FAILURE;
        }
    
        errno = 0;    // reset error number
        char *endptr; // this will point to the first character not read by strtol
        long i = strtol(argv[1], &endptr, 10);
        if (errno == ERANGE)
        {
            fprintf(stderr, "This number is too small or too large.\n");
            return EXIT_FAILURE;
        }
        else if (endptr == argv[1])
        {
            // no character was converted. This also catches the case of an empty argument
            fprintf(stderr, "The argument was not a number.\n");
            return EXIT_FAILURE;
        }
        else if (*endptr)
        {
            // endptr doesn't point to NUL, so there were characters not converted
            fprintf(stderr, "Unexpected characters in number.\n");
            return EXIT_FAILURE;
        }
    
        printf("You entered %ld.\n", i);
        return EXIT_SUCCESS;
    }
    

    【讨论】:

      【解决方案3】:

      我是 C 新手,指针/指向数组的指针的概念仍然让我感到困惑。

      在 C 中,字符串由以 null 结尾的 ('\0') 字符数组表示。让我们考虑以下示例:

      char str[] = "Hello world!"
      

      字符在内存中是连续的,str 的使用将decay 指向指向字符串第一个元素的字符指针 (char*)。 &amp;str[0] 获取的第一个元素 (&amp;) 的地址也将指向该地址:

      | . | . | . | H | e | l | l | o |   | W | o | r | l | d | ! | \0 | . | . | . |
                    ^                                               ^
                   str                                        null terminator
      

      *argv[1]不应该返回数组中第二个字符的值吗?

      首先在argv中是一个字符指针char* argv[]的数组,这样它就可以被解释为一个字符串数组。

      第一个字符串argv[0] 是程序本身的程序名称,然后是传递的参数:

      argv[0] contains a pointer to the string:    "program name"
      argv[1] contains a pointer to the argument:  "17"
      

      如果您使用* 取消引用argv[1],您会得到该地址的第一个字符,这里是1,它是Ascii 代码中的十进制49。示例:

          p             r        ("program name")
          ^             ^
       argv[0]    (argv[0] + 1)
      --------------------------------------------
          1             7        ("17")
          ^             ^
       argv[1]    (argv[1] + 1)
      

      如何将num = 17 读取为int

      使用argc 检查传递的参数数量,其中还包含程序名称(read here more about argc and argv)。如果有 2 个,则可以将 strtol() to convert argv[1] 用于整数。使用strtol() 而不是atoi(),因为atoi() 被认为已被弃用,因为没有可用的错误检查。如果 atoi() 失败,它只会返回 0 作为整数,而不是 strtol() 将第二个参数和全局 errno 变量设置为特定值。

      以下代码将使用 strtol() 设置第二个参数的指针来检查转换错误。还有上溢和下溢错误要检查like it's described here on SO。此外,如果要将返回的 long 值存储到 int 变量中,则必须检查返回的值是否适合 int 变量。但为简单起见,我忽略了这一点:

      #include <stdio.h>
      #include <stdlib.h>
      
      int main (int argc, char* argv[])
      {
         /* At least 1 argument passed? */
         if (argc >= 2)
         {
            char* endptr;
            long num = strtol(argv[1], &endptr, 10);
      
            /* Were characters consumed? */
            if (argv[1] != endptr)
            {
               printf("Entered number: %ld\n", num);
            }
            else
            {
               printf("Entered argument was not a number!\n");
            }
         }
         else
         {
            printf("Usage: %s [number]!\n", argv[0]);
         }
         return 0;
      }
      

      【讨论】:

      • 嗨@Amy,如果这个或任何答案解决了您的问题,请点击复选标记考虑accepting it。这向更广泛的社区表明您已经找到了解决方案,并为回答者和您自己提供了一些声誉。没有义务这样做。
      【解决方案4】:

      这是你想要做的:

      #include <stdio.h>
      #include <stdlib.h>
      
      int main (int argc, char *argv []) {
          int num = atoi (argv[1]);
          printf ("Here's what you gave me: %d", num);
          return 0;
      }
      

      这是documentation for atoi ()

      argv 是一个字符串数组,所以 argv[x] 指向一个字符串。 atoi () 接受 ASCII 字符串作为输入并返回 int

      奖励:这仍然有点不安全。尝试在不传递参数的情况下运行该程序,看看会发生什么。

      另外,您必须查看documentation for strtol (),这是一种安全的方法。

      【讨论】:

      • 你不会得到任何参数 -> atoi (argv[1]); -> UB
      • 你是什么意思@FilipKočica?这工作得很好。但是,它没有检查argv[1] 是否指向实际位置,这意味着如果您在没有命令行参数的情况下调用程序,它会出现段错误。
      • 不,这并不意味着它存在段错误。任何事情都可能发生,包括但不限于段错误。如果argc 为0 - 是的,这也是允许的,那么argv[1] 甚至不需要是空指针。
      • 没错,这就是我添加最后一个编辑的原因。
      猜你喜欢
      • 2013-11-15
      • 1970-01-01
      • 2013-05-02
      • 1970-01-01
      • 1970-01-01
      • 2017-11-21
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多