isdigit(c) - 字符或整数类型?

isdigit(c) - a char or int type?

本文关键字:整数 类型 字符 isdigit      更新时间:2023-10-16

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

#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;
}
}      

我的问题是:输入变量类型应该是什么?char 还是 int?

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

首先:代码的第一部分是正确的(不考虑多字节编码(;如果你想用cin读取单个char,你必须使用带有>>运算符的char变量。

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

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

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

不幸的是,这带来了与"常规"char的互操作性问题。

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

因此,你必须为charsignedunsigned做好准备;在大多数实现中,它是默认signed的,这给上面的getchar()安排带来了问题。

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

因此,当 C 函数谈论在变量中接收/提供字符时int契约总是假定该字符是已强制转换为unsigned charchar(因此它始终是正值,负值溢出到其范围的上半部分(,然后放入int中。这让我们回到了isdigit函数,它连同其配套函数,也有这样的契约:

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

(C99, §7.4, ¶1(

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

if(isdigit((unsigned char)c))

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

此外,您没有考虑到流可能被关闭的事实,因此>>返回而不触及您的变量(它将处于未初始化的值(;要考虑到这一点,您应该在处理c之前检查流是否仍处于有效状态。


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

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

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