【问题标题】:Unexpected behavior from integer checking in CC 中整数检查的意外行为
【发布时间】:2016-07-19 10:24:40
【问题描述】:

这是我为了检查输入是否为正整数而写的

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int isInt(char num[3]) {
    int l = strlen(num);
    int x;

    for ( x = 0; x < l; x++) {
        if ( ! isdigit(num[x]) )
            return 0;        
    }

    return 1;
}

int main(void) {
    int i;
    char str[3];

    printf("Enter a positive integer: ");
    scanf("%s", str);
    if ( isInt(str) == 1 ) {
        i = atoi(str);
        printf("%d \n", i);
    } else {
        printf("This is not a positive integer! \n");
    }

    return 0;
}

我发现了一些意想不到的行为,尽管程序似乎可以运行:

  1. 我是否#include &lt;ctype.h&gt; 没有区别。 isdigit() 函数在没有库的情况下如何工作? (我在 ubuntu 14.04 上使用 gcc 4.9.3)

  2. 如果我将str 强制为长度为3 的字符串,那么当我运行./a.out 时,它如何正确检查并打印大于999 的数字?如果我输入 5678 不应该str 只保存 567?或者它可能会保存前 2^3 位数字?我很困惑

  3. 如果我想检查一个字符串是否为正整数,但数字可能很大,str 应该有多大?有没有办法让它按需要变大?

【问题讨论】:

  • 最好每次问一个问题。而且您的大多数问题之前都有重复,请先搜索该网站。
  • 越界写入,未定义行为。
  • 代码构建对您不利,因为超出范围会给出工作结果。
  • 您可以使用动态内存分配,并根据需要使用realloc()
  • int l = strlen(num); 错误应该是size_t l = strlen(num); 也应该是scanf("%s", str); 应该是scanf("%2s", str);

标签: c string integer type-conversion


【解决方案1】:
  1. 头文件不是库;它只说明如何使用库。如果您省略头文件,那么编译器将不得不猜测函数采用哪些参数,但如果它猜测正确(或足够接近),那么一切似乎都可以正常工作。 (实际上它不会“猜测”;当然是有规则的。)

  2. 您没有“强加”str 的长度限制,您保留了 3 个字节的空间。如果您超出该保留,则程序可能(或可能不会)继续正常工作,但您可能会发现其他数据已意外损坏。没有可靠的方法来预测哪些数据会被覆盖;它是未定义的。

  3. 不,在用户输入之前无法知道数字有多大,但您可以使用%3s 设置将读取的最大字符数,例如。但是,您需要确保为字符串末尾的'\0' nul 字符终止符保留足够的空间。您还可以使用scanf 的替代方法以块的形式读取数据,但您可能最安全的是使用“大”缓冲区和最大字段宽度。请注意,scanf 可以直接读取数字,而无需您将其作为字符串读取然后自己进行转换。

【讨论】:

    【解决方案2】:

    关于第 3 点:

    不,没有办法提前知道。

    但是,很容易创建一个函数来读取较小部分的输入并将它们连接到一个大缓冲区中。大缓冲区将被动态分配,因此它可以在需要时增长。用户必须免费通话。

    这是一种实现方式:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define INCREMENT_SIZE 20
    #define SMALL_SIZE 10
    
    char* read_long_string(void)
    {
      char* all;
      char* tmp;
      int all_size;
      char some[SMALL_SIZE];
    
      all_size = INCREMENT_SIZE;
      all = malloc(all_size);
      if (!all)
      {
        // Out of mem
        return NULL;
      }
      all[0] = '\0';
    
      while(1)
      {
        // Make sure "all" has room for all bytes in "some"
        while(all_size - strlen(all) < sizeof(some))
        {
          all_size += INCREMENT_SIZE;
          tmp = realloc(all, all_size);  // Allocate more memory
          if (!tmp)
          {
            // Out of mem
            free(all);
            return NULL;
          }
          all = tmp;
        }
    
        // Read bytes into "some"
        if (!fgets(some, sizeof(some), stdin))
        {
          // Error
          free(all);
          return NULL;
        }
    
        if (strlen(some) > 0)
        {
          // Add the bytes to "all"
          strcat(all, some);
    
          // Check if newline has been pressed
          if (some[strlen(some)-1] == '\n')
          {
            // done
            return all;
          }
    
        }
      }
    }
    
    
    int main(int argc, char const *argv[])
    {
      char* s;
    
      printf("Enter a positive integer:\n");
      s = read_long_string();
      if (s) printf("%s", s);
    
      free(s);
    
      return 0;
    }
    

    【讨论】:

    • 在未初始化的 malloc()ed 缓冲区上调用 strlen() 不是一个好主意。
    • 嗯,它仍然是一个非常复杂的 Rube-Goldberg-Schlemiel 装置,但我想它现在在技术上是正确的。
    • @EOF - 如果您知道问题的更好解决方案,请发布。这将比仅仅将另一个解决方案称为“可笑地令人费解”更有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多