【问题标题】:Why is the second argument not working with strtol?为什么第二个参数不适用于 strtol?
【发布时间】:2023-09-28 06:34:01
【问题描述】:

我做了这样的事情:

/* convert the argv[1] into Integer and store the result in key
 * using library function: strtol() to do it */
char **flag = NULL;
key = strtol(argv[1], flag, 10);

// if argv[1] is not all digits
if (**flag != '\0')
{
    printf("Usage: ./caesar key\n");
    return 1;
}

但它会引发分段错误。我不知道为什么。

在 C 文档中,strtol 的原型为 long int strtol(const char *nptr, char **endptr, int base)。为什么会出现分段错误?

当将部分代码更改为char *flagstrtol(argv[1], &flag, 10)if (*flag != '\0') 时,一切正常。

我了解(某种程度上)进行编辑如何更正代码。但是,我不知道为什么原始代码不起作用。有人知道吗?

【问题讨论】:

    标签: c pointers segmentation-fault strtol


    【解决方案1】:

    您需要传递char * 指针的地址,以便strtol 可以更新它。

    char *flag = NULL;
    key = strtol(argv[1], &flag, 10);
    
    // if argv[1] is not all digits
    if (*flag != '\0')
    

    调用后,指针会被修改为指向输入字符串解析区域的末尾。

    这里的手册页措辞确实令人困惑。

    【讨论】:

    • 我明白你在说什么。但我更困惑为什么 **flag 不起作用?
    • 在您的代码中,flag 是一个char**,其值为NULL。当您将其传递给strtol 时,它会看到参数值为NULL 并且不对其执行任何操作。当strtol 返回时flag 的值仍然是NULL,因此在您尝试初始取消引用时会导致段错误。您应该使用调试器观察这一点。
    【解决方案2】:

    不知道为什么原来的代码不起作用?有人知道吗?

    因为这里

    char **flag = NULL;
    

    flag设置为NULL,那么这里

    key = strtol(argv[1], flag, 10);
    

    flags 的值(NULL)被传递给strtol(),它不会以任何方式改变flags 的值,它是在调用strtol() 之前和之后的NULL

    终于来了

    if (**flag != '\0')
    

    flag 的值 NULL 被取消引用,这会调用未定义的行为,这可能导致任何事情,在您的情况下是崩溃。

    【讨论】:

      【解决方案3】:

      考虑这个简单的函数,它将一个数乘以 2。

      void Times2(int number, int *result)
      {
        *result = number * 2;
      }
      

      你应该这样称呼它:

      int result;
      Times2(3, &result);
      printf("%d\n", result);
      

      预期的输出将是4

      但是,如果您这样称呼它(这基本上就是您在错误代码中调用 strtol 时所做的事情):

      int *result = NULL;
      Times2(3, result);
      printf("%d\n", result);
      

      您的程序很可能会出现分段错误,因为在 Times2 函数中,您取消了对 NULL 指针的引用。

      【讨论】:

        【解决方案4】:

        为什么第二个参数不适用于strol

        char **flag = NULL;
        key = strtol(argv[1], flag, 10);
        

        它正在工作。传递一个 空指针 很好,但 flag 不会在调用代码中改变。

        当第二个参数是 空指针 时,不会更新转换结束的位置。


        后来的代码:if (**flag != '\0') 中的 *flag 是错误的(未定义行为),因为它试图取消引用空指针。


        改为传递已知指针的地址:

        //char **flag = NULL;
        char *flag;
        
        //key = strtol(argv[1], flag, 10);
        key = strtol(argv[1], &flag, 10);
        
        // if (**flag != '\0')
        if (*flag != '\0') 
        

        以下是从char*int 的完整测试转换。

        #include <stdlib.h>
        #incluse <ctype.h>
        
        // Return error flag
        int my_strtol(long *destination, const char *s) {
          if (destination == NULL) {
            return 1;
          } 
          if (s == NULL) {
            *destination = 0;
            return 1;
          }
        
          char *endptr;
          errno = 0;
          *destination = strtol(s, &endptr, 0); 
        
          // If no conversion or out-of-range
          if (s == endptr || errno == ERANGE) {
            return 1;
          }
          // I like to tolerate trailing white-space.
          while (isspace(*(const unsigned char *) endptr)) {
            endptr++;
          }
          return *endptr != '\0';
        }   
        

        【讨论】: