int c = getchar()?

27
我正在读这本书:《C程序设计语言 - Kernighan和Ritchie著》(第二版),在其中一个例子中,我对事情的运作方式感到困惑。
#include <stdio.h>

#define MAXLINE 1000

int getline(char line[], int maxline);
void copy(char to[], char from[]);

int main(int argc, char *argv[])
{
    int len;
    
    int max;
    char line[MAXLINE];
    char longest[MAXLINE];
    
    max = 0;
    while((len = getline(line, MAXLINE)) > 1)
    {
        if(len > max)
        {
            max = len;
            copy(longest, line);
        }
    }
    if(max > 0)
        printf("%s", longest);
        
    getchar();
    getchar();
    return 0;   
}

int getline(char s[], int lim)
{
    int c, i;
    
    for(i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
        s[i] = c;
    if(c == '\n')
    {
        s[i] = c;
        ++i;     
    }
    s[i] = '\0';
    
    return i;
}

void copy(char to[], char from[])
{
    int i;
    
    i = 0;
    while((to[i] = from[i]) != '\0')
        ++i;
}

在这行代码中:for(i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)中,当出现c = getchar()时,如何将一个整数赋值为从命令行输入的字符呢?整数可以,但是我输入的字符是如何存储的呢?

1
参见 如果使用 char c = getchar() 而不是 int 会发生什么 - Antti Haapala -- Слава Україні
一般规则是保留答案最佳的问题。时差并不重要。如何处理重复的问题? - phuclv
6个回答

37
与您可能使用过的其他语言不同,C 中的字符是整数。 char 只是另一种整数类型,通常是 8 位并且比 int 更小,但仍然是整数类型。
因此,在 C 中,您可以使用转换或仅通过赋值来在 char 和其他整数类型之间进行转换,无需像其他语言中那样使用 ord() 和 chr() 函数。
除非出现 EOF,否则 getchar() 被定义为返回 "转换为 int 的无符号 char" (相当于 fgetc),因此如果有帮助,您可以想象它读取一些字符 c,然后返回 (int)(unsigned char)c。
您可以通过转换或分配将其转换回 unsigned char,并且如果您愿意稍微损失理论上的可移植性,则可以通过转换或将其分配给 char 将其转换为 char。

在C语言中,字符和整数可以说是“相同”的。当执行类似于myInt = myChar的操作时,这是因为它们的ASCII值相同。 - Rhexis
4
@Flyphe:基本上是这样的。就C语言而言,一个字符就是它的数字值。实际上,在C语言中,像'a'这样的字符字面量的类型是int而不是char。这个数字值严格来说并不一定要是ASCII码,C语言实现其实可以使用另一种编码,比如EBCDIC,不过你很少会遇到这种情况。 - Steve Jessop
请注意,char类型是一种独立的类型:最小可能的整数类型,通常为1个字节宽。因此,它不仅用于存储ASCII字母,而且在处理0-255(无符号)或-128到127(有符号)之间的小数字时也经常使用,以节省内存。如果您使用int,则需要2或4个字节而不是1个字节。 - Lundin

11

getchar() 函数返回一个整数,该整数代表所输入字符的表示形式。如果输入字符A,则返回 'A'0x41(升级为int,当然假设你使用的是ASCII系统)。

它返回int而不是char是因为它需要能够存储任何字符及其结束标识EOF(End of File)。

此外,对于初学者来说,这并不是一本好书。它来自那个效率比可读性和可维护性更重要的时代。

尽管它展示了K&R等人的聪明才智,但您可能应该看一些更适合新手的资料。

无论如何,它的最后一版涵盖了C89,自那以后已经发生了很多变化。我们经历了C99,现在有了C11,但这本书没有更新以反映这些变化,因此它已经非常过时了。


1
你能推荐一本现代的 C 语言书籍吗?其实我是指那种具有类似于 K&R 或 SO 上经常回答 C 语言问题的二三十位专家所具备的严谨性,而不是那些普通教科书所能涵盖的内容。 - Steve Jessop
2
我使用的书没有问题,Dennis Ritchie 创造了 C 语言。跟着内容走相当直接。我相信随着时间的推移,我会掌握并理解所有内容。不过,老实说,这本书对初学者来说很好。 - Rhexis
3
我不同意:这本书对于初学者并不友好,你应该学习如何编写可读性强、易于维护的代码,而不是像那个例子那样过于复杂的单行代码。一个良好的编译器会给出相同的底层机器码,无论你输入那个怪物代码还是等价的五行优秀源代码。如果你写出像那样的代码,在第一次代码审查中让我知道结果如何吧 :-) - paxdiablo
效率在今天仍然像过去一样重要。鉴于数据规模和计算任务的成本,大多数高性能计算任务需要尽可能利用高效的计算。但无论如何,这只是我的看法。 - xbsd
@xbsd,有些领域仍然需要效率,但现在大多数代码的主要问题是可维护性而不是速度。如果我必须维护这段代码,我宁愿让它易读,即使这意味着代码运行速度慢了3%。今天的编译器已经不是过去“愚蠢”的野兽了 :-) - paxdiablo
显示剩余6条评论

5

C语言中的char类型是8位的,这意味着它可以存储整数范围从(取决于是否带符号,如果没有指定C标准不指定它)-128到127或0到255(255个独立的值; 这是ASCII的范围)。 getchar()返回int,它至少会占用16位(现代计算机通常为32位)。 这意味着它可以存储char的范围以及更多的值。

返回类型为int的原因是当到达输入流的末尾时,将返回特殊值EOF。 如果返回类型为char,则没有办法表明已经遇到了流的结尾(除非它获取指向记录此条件的变量的指针)。


2
C 字符类型不能保证是有符号的,它很常见地是无符号的。例如,在 ARM 上使用 gcc 和其他编译器时,默认情况下就是无符号的。 - Steve Jessop
据我所知,它也不能保证是无符号的。 - cdhowie
2
正确,它也不保证是无符号的。它保证与 signed charunsigned char 中的一个具有相同的表示形式,因此其范围与其中一个相同。你只是不能确定它是 -128 到 127。你也不能确定它是8位,但它非常流行是8位,例外是古老的9位主机和一些具有16位或32位字符的DSP芯片。 - Steve Jessop
你不需要发送指针,有feof()和ferror()函数可用,但遗憾的是它们没有快速的宏版本。将字符扩展为int只是为了能够挤入特殊的EOF返回值,这似乎是一个非常糟糕的优化选择,使C语言变得更加复杂和受限制。就像NULL和'\0'结尾的字符串一样。C++修正了EOF/char/int错误:你测试流,而不是“字符”。 - potrzebie
C语言中的char类型不能保证恰好为8位。它保证至少为8位,但CHAR_BIT可能不恰好为8。 - Govind Parmar

0

现在让我们来玩一场逻辑游戏。

Char也是一种整数类型,其范围比int小,具体为8位,即1字节。众所周知,整数类型包括有符号(默认)和无符号。对于char而言,有符号的范围是-127~128,无符号的范围是0~255。现在我们知道了有符号和无符号char的类型和“能力”。

我们人类理解字符,而计算机只认识二进制序列。因此,各种编程语言必须提供一种模型来处理从字符到二进制序列的转换。ASCII码是映射的标准,在C和许多其他编程语言中应用。它使用0-255来编码基本字符,如0-9、a-z和A-Z,以及常见的特殊字符。

你可能会想unsigned char才是确切的选择。然而,程序应该知道何时停止。最简单的方法是遇到一个特殊值,负值是一个不错的选择,因为更大的正值可能被用于其他语言。最后,C选择了-1,这更常被称为EOF。

现在我们明白了。有符号字符不足以编码ASCII字符,而无符号字符则没有终止值的空间。为了平衡这一点,我们需要更大的范围,即int类型。明白了吗?

感谢@cdhowie的答案,它实际上激发了我。


-1

命令行中输入的每个字符(包括数字)都被读取为一个字符,并且每个字符都有一个基于其ASCII代码的整数值http://www.asciitable.com/


C标准实际上并不保证ASCII是所使用的字符集,虽然你可能需要走很远的路,甚至去博物馆才能找到一个不使用ASCII的C实现。 - Steve Jessop
是的,但为了简单起见,我认为这已经被假定了。 - Matt
@Steve:嗯,我们每天都在使用EBCDIC的机器上工作。事实上,我敢打赌你所有的银行交易最终都会到达这样的机器。那些令人尊敬的System z大型机,在经过这么多年之后仍在运行着全球金融业务 :-) - paxdiablo
@paxdiablo:太好了,我没有意识到。希望他们不会让人们走进门就开始编写运行银行系统的特定程序。我的观点只是你必须努力摆脱ASCII,这可能不会在你不注意的时候发生。 - Steve Jessop

-4

你的问题已经得到回答。但是还有一件事情需要补充。

由于你将变量c声明为整型,因此很明显你正在使用ASCII值为48-570到9之间的值。 所以你只需要在代码中添加一行-

c = c-48


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接