【问题标题】:Function to Split a String into Letters and Digits in C在 C 中将字符串拆分为字母和数字的函数
【发布时间】:2021-07-29 09:23:04
【问题描述】:

我对 C 很陌生,我正在尝试编写一个函数,该函数采用 B、kB、mB 或 gB 为单位的用户输入 RAM 大小,并确定地址长度。我的测试程序如下:

int bitLength(char input[6]) {

  char nums[4];
  char letters[2];

  for(int i = 0; i < (strlen(input)-1); i++){
    if(isdigit(input[i])){
      memmove(&nums[i], &input[i], 1);
    } else {
      //memmove(&letters[i], &input[i], 1);
    }
  }
  int numsInt = atoi(nums);
  int numExponent = log10(numsInt)/log10(2);
  printf("%s\n", nums);
  printf("%s\n", letters);
  printf("%d", numExponent);
  return numExponent;
}

这可以正常工作,但这只是因为我已经注释掉了那一行。当我尝试使用该行更改“字母”字符数组时,它将“nums”字符数组更改为“5m2”

我的字符串输入是 '512mB' 我需要这些字母来判断用户输入是 B、kB、mB 还是 gB。 我很困惑为什么注释掉的行会改变“nums”数组。

谢谢。

【问题讨论】:

    标签: arrays c string memory-address string.h


    【解决方案1】:

    您的代码中有几个问题。 @MarkSolus 已经指出,您访问letters 越界,因为您使用i 作为索引,而i 在您执行memmove 时可以超过1。

    在这个答案中,我将解决其他一些问题。

    字符串大小和终止

    C 中的字符串需要一个零终止符。因此,数组必须比您希望存储在数组中的字符串大 1。所以

    char nums[4];     // Can only hold a 3 char string
    char letters[2];  // Can only hold a 1 char string
    

    您很可能希望将两个数组都增加 1。

    此外,您的代码从不添加零终止符。所以你的字符串是无效的。

    你需要这样的代码:

    nums[some_index] = '\0';  // Add zero-termination
    

    或者,您可以从将整个数组初始化为零开始。喜欢:

    char nums[5] = {0};
    char letters[3] = {0};
    

    缺少边界检查

    您的循环是使用strlen 作为停止条件的for 循环。现在如果我输入 "123456789BBBBBBBB" 会发生什么?好吧,循环将继续,i 将递增到值 ..., 5, 6, 7, ... 然后您将索引具有大于数组大小的值的数组,即越界访问(这真的很糟糕)。

    您需要确保永远不会越界访问数组。

    无格式检查

    现在如果我输入一个没有任何数字的输入,例如“你好世界” ?在这种情况下,不会向nums 写入任何内容,因此在atoi(nums) 中使用时将不会对其进行初始化。再次 - 真的很糟糕。

    此外,应该检查以确保非数字输入是 B、kB、mB 或 gB 之一。

    性能

    这并不重要,但是...使用memmove 复制单个字符很慢。直接赋值即可。

    memmove(&nums[i], &input[i], 1); ---> nums[i] = input[i];
    

    如何解决

    修复代码的方法有很多种。下面是一个简单的解决方案。这不是最好的方法,但这样做是为了保持代码简单:

    #define DIGIT_LEN 4
    #define FORMAT_LEN 2
    
    int bitLength(char *input)
    {
        char nums[DIGIT_LEN + 1] = {0};    // Max allowed number is 9999
        char letters[FORMAT_LEN  + 1] = {0}; // Allow at max two non-digit chars
    
        if (input == NULL) exit(1);  // error - illegal input
    
        if (!isdigit(input[0])) exit(1); // error - input must start with a digit
    
        // parse digits (at max 4 digits)
        int i = 0;
        while(i < DIGITS && isdigit(input[i]))
        {
            nums[i] = input[i];
            ++i;
        }
    
        // parse memory format, i.e. rest of strin must be of of B, kB, mB, gB
        if ((strcmp(&input[i], "B") != 0) &&
            (strcmp(&input[i], "kB") != 0) &&
            (strcmp(&input[i], "mB") != 0) &&
            (strcmp(&input[i], "gB") != 0))
        {
              // error - illegal input
              exit(1);
        }
        strcpy(letters, &input[i]);
    
        // Now nums and letter are ready for further processing
        ...
        ...
      }
    
    }
    

    【讨论】:

      【解决方案2】:

      在您的输入512mB 中,"mB" 不是数字,应该在注释代码中处理。处理这些字符时,i 是 3 和 4。但是因为 letters 的长度只有 2,所以当您执行 memmove(&amp;letters[i], &amp;input[i], 1); 时,letters[i] 访问超出数组范围,因此它会执行未定义的行为 - 在这种情况下,写入nums 数组的内存。

      要修复它,您必须为 letters 保留唯一索引。或者更好的是,对于 numsletters,因为 iinput 的索引。

      【讨论】:

        猜你喜欢
        • 2021-06-15
        • 1970-01-01
        • 1970-01-01
        • 2013-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-26
        相关资源
        最近更新 更多