【问题标题】:isdigit(c) - a char or int type?isdigit(c) - char 或 int 类型?
【发布时间】:2017-07-10 04:24:08
【问题描述】:

我编写了以下代码来测试给定的输入是否为数字。

#include<iostream>
#include<ctype.h>
#include<stdio.h>
using namespace std;

main()
{
    char c;

    cout<<"Please enter a digit: ";
    cin>>c;

    if(isdigit(c)) //int isdigit(int c) or char isdigit(char c)
    {
        cout<<"You entered a digit"<<endl;
    }
    else
    {
        cout<<"You entered a non-digit value"<<endl;
    }
}      

我的问题是:输入变量类型应该是什么?字符还是整数?

【问题讨论】:

  • stackoverflow.com/questions/35391765/… 的可能骗局(免责声明我已回答)
  • 是的,我确实读过那个答案。但我的书已将其作为字符处理功能。那么如果它是一个 int 类型,为什么它被用作或命名为字符处理函数呢?
  • 加上我是 C++ 新手,所以如果我的问题太基本,我深表歉意
  • 这并没有解决问题,但是您真的需要std::endl 需要的额外内容吗? '\n' 结束一行。

标签: c++


【解决方案1】:

不幸的是,情况比其他答案所说的要复杂一些。

首先:您的代码的第一部分是正确的(忽略多字节编码);如果您想通过cin 读取单个char,则必须将char 变量与&gt;&gt; 运算符一起使用。

现在,关于isdigit:为什么要用int 而不是char

这一切都来自C; isdigit 和它的伙伴生来就是为了与像 getchar() 这样的函数一起使用的,它从流中读取一个字符并返回一个 int。这样做是为了给字符 提供一个错误代码:getchar() 可以通过其返回代码返回 EOF(定义为一些实现定义的负常量),以表示输入直播已结束。

所以,基本思想是:否定=错误代码;正数 = 实际字符代码。

不幸的是,这给“常规”chars 带来了互操作性问题。

题外话:char 最终只是一个范围很小的整数类型,但特别愚蠢。在大多数情况下——当使用字节或字符代码时——你希望它默认为unsigned; OTOH,出于与其他整数类型(intshortlong、...)的一致性原因,您可能会说正确的做法是简单的 char 应该是 signed。标准选择了最愚蠢的方式:普通的charsignedunsigned,具体取决于编译器的实现者决定1

因此,您必须为char 成为signedunsigned 做好准备;在大多数实现中,默认情况下它是signed,这对上面的getchar() 安排造成了问题。

如果char 用于读取字节并且是signed,则意味着所有设置了高位的字节(AKA 字节,使用unsigned 8 位类型读取将>127)结果为为负值。这显然与使用负值的getchar() 不兼容EOF - 实际的“负”字符和EOF 之间可能存在重叠。

因此,当 C 函数谈论接收/提供字符到 int 变量时,合同始终假定该字符是一个 char,该字符已被转换为 unsigned char(因此它始终是正数) ,负值溢出到其范围的上半部分),然后放入int。这让我们回到了 isdigit 函数,除了它的伴随函数,它也有这个合约:

标题&lt;ctype.h&gt; 声明了几个对字符分类和映射有用的函数。在所有情况下,参数都是int,其值应表示为unsigned char 或应等于宏EOF 的值。如果参数有任何其他值,则行为未定义。

(C99,§7.4,¶1)

所以,长话短说:您的if 至少应该是:

if(isdigit((unsigned char)c))

问题不仅仅是理论上的问题:一些广泛使用的 C 库实现直接使用提供的值作为查找表的索引,因此负值将读入未分配的内存并导致程序段错误。

此外,您没有考虑流可能已关闭的事实,因此 &gt;&gt; 在不接触您的变量的情况下返回(该变量将处于未初始化的值);考虑到这一点,您应该在处理 c 之前检查流是否仍处于有效状态。


  1. 当然,这有点不公平;正如 @Pete Becker 在下面的评论中指出的那样,并不是他们都是白痴,而是标准主要试图与现有实现兼容,这些实现可能在未签名和签名 @987654368 之间平均分配@。这种分裂的痕迹可以在大多数现代编译器中找到,它们通常可以通过命令行选项更改char 的签名(-fsigned-char/-funsigned-char 用于 gcc/clang,/J 用于 VC++)。

【讨论】:

  • 该标准使 char 有符号或无符号,因为这是 C 标准化时不同编译器所做的。向后兼容性很重要,不应被随便视为“愚蠢”。除此之外,很好的答案!
【解决方案2】:

如果你想读取单个字符并检查它是否是数字,那么它应该是字符。

如果您将其设置为 int,则将读取多个字符,并且 isDigit 的结果将始终为真。

【讨论】:

  • 这个答案是错误的。由于另一个答案中所述的原因,第一句话是错误的。第二句话也是错误的,因为不一定会读取多个字符,并且不会总是使isdigit为真。
猜你喜欢
  • 1970-01-01
  • 2021-01-08
  • 1970-01-01
  • 2022-07-22
  • 1970-01-01
  • 1970-01-01
  • 2017-04-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多