8

我正在学习 C 并编写一个简单的程序,该程序将假定每个为二进制数的 2 个字符串值并根据用户选择执行算术运算:

  • 添加两个值,
  • 从输入 1 中减去输入 2,或
  • 将两个值相乘。

我的实现假设字符串中的每个字符都是一个二进制位,例如char bin5 = "0101";,但是一次解析字符串一个字符的方法似乎太天真了。理想情况下,我想直接使用二进制值。

在 C 中执行此操作的最有效方法是什么?有没有更好的方法将输入视为二进制值,而不是scanf()从字符串中获取每一位?

我做了一些研究,但从初学者的角度来看,我没有发现任何明显更好的方法。任何建议,将不胜感激!

4

5 回答 5

12

建议:
没有什么比一次遍历一个字符并确保用户只输入一个和零更好的了。请记住,即使假设所有内容都是or ,您也可以编写一个非常快速的汇编程序,但您并不想这样做。用户可以输入任何内容,并且您希望能够告诉他们他们是否搞砸了。10

确实,与添加实际数字可能需要的几个周期相比,这看起来慢得令人难以置信,但是如果你在纳秒或毫秒内得到答案真的很重要吗?无论如何,人类只能检测到 30 毫秒的延迟。

最后,与解析字符串或添加数字相比,从用户那里获取输入并将输出写入屏幕所花费的时间要长得多,因此您的算法在这里几乎不是瓶颈。为实际上计算密集型的事物保存您的花哨优化:-)。

您在这里应该关注的是减少任务的人力密集度。而且,事实证明,有人已经为你做到了。

解决方案:
查看手册页strtol()

long strtol(const char *nptr, char **endptr, int base);

这将允许您将任何基数的字符串 (nptr) 转换为长字符串。它也检查错误。转换二进制字符串的示例用法:

#include <stdlib.h>

char buf[MAX_BUF];
get_some_input(buf);

char *err;
long number = strtol(buf, &err, 2);
if (*err) {
    // bad input: try again?
} else {
    // number is now a long converted from a valid binary string.
}

提供 base 2 告诉strtol转换二进制文字。

于 2009-03-23T05:26:48.497 回答
4

首先,我确实建议您使用 tgamblin 推荐的 strtol 之类的东西,最好使用 lib 提供给您的东西,而不是一遍又一遍地创建轮子。

但是由于你正在学习 CI 做了一个没有 strtol 的小版本,它既不快速也不安全,但我确实以位操作为例进行了一些操作。

int main()
{
    unsigned int data = 0;
    int i = 0;

    char str[] = "1001";

    char* pos;
    pos = &str[strlen(str)-1];

    while(*pos == '0' || *pos == '1')
    {
        (*pos) -= '0';
        data += (*pos) << i;

        i++;
        pos--;
    }

    printf("data %d\n", data);
    return 0;
}
于 2009-03-23T06:26:35.157 回答
1

为了获得最佳性能,您需要区分函数的可信输入和不可信输入。

例如,一个getBinNum()接受用户输入的函数应该检查有效字符并压缩以删除前导零。首先,我们将展示一个通用的就地压缩函数:

// General purpose compression removes leading zeroes.
void compBinNum (char *num) {
    char *src, *dst;

    // Find first non-'0' and move chars if there are leading '0' chars.
    for (src = dst = num; *src == '0'; src++);
    if (src != dst) {
        while (*src != '\0')
            *dst++ = *src++;
        *dst = '\0';
    }

    // Make zero if we removed the last zero.
    if (*num == '\0')
            strcpy (num, "0");
}

然后提供一个检查器函数,该函数返回传入的值,如果无效则返回 NULL:

// Check untested number, return NULL if bad.
char *checkBinNum (char *num) {
    char *ptr;

    // Check for valid number.
    for (ptr = num; *ptr == '0'; ptr++)
        if ((*ptr != '1') && (*ptr != '0'))
            return NULL;

    return num;
}

然后输入函数本身:

#define MAXBIN 256

// Get number from (untrusted) user, return NULL if bad.
char *getBinNum (char *prompt) {
    char *num, *ptr;

    // Allocate space for the number.
    if ((num = malloc (MAXBIN)) == NULL)
        return NULL;

    // Get the number from the user.
    printf ("%s: ", prompt);
    if (fgets (num, MAXBIN, stdin) == NULL) {
        free (num);
        return NULL;
    }

    // Remove newline if there.
    if (num[strlen (num) - 1] == '\n')
        num[strlen (num) - 1] = '\0';

    // Check for valid number then compress.
    if (checkBinNum (num) == NULL) {
        free (num);
        return NULL;
    }
    compBinNum (num);

    return num;
}

应该编写其他要相加或相乘的函数,以假设输入已经有效,因为它是由该库中的一个函数创建的。我不会为他们提供代码,因为它与问题无关:

char *addBinNum (char *num1, char *num2) {...}
char *mulBinNum (char *num1, char *num2) {...}

如果用户选择从 以外的其他地方获取数据getBinNum(),您可以允许他们调用checkBinNum()来验证它。

如果你真的很偏执,你可以检查每个传递给你的例程的数字并采取相应的行动(返回 NULL),但这需要相对昂贵的检查,而这是不必要的。

于 2009-03-23T07:12:24.453 回答
0

将字符串解析为整数,然后对整数进行数学运算不是更容易吗?

我假设这是一项学校作业,但我赞成你,因为你似乎付出了很大的努力。

于 2009-03-23T05:25:07.550 回答
-1

仅仅因为字符串仅由集合 {0,1} 中的数字组成而假设字符串是二进制数是危险的。例如,当您的输入是“11”时,用户可能指的是十进制的 11,而不是二进制的 3。正是这种粗心大意导致了可怕的错误。您的输入不完整,您应该真正要求用户也指定基础。

于 2010-11-27T20:27:16.113 回答