getchar返回int类型

#include <stdio.h>

/* copy input to output; 2nd version */
main()
{
int c;
c = getchar();
while(c != EOF){
putchar(c);
c = getchar();
}
}

  直觉告诉我getchar返回值应该是char类型的,这个地方为什么不能用char类型来存储getchar()的返回值呢?

  其实文中解释的很清楚,可当时没有看明白:

  在键盘或者屏幕上的字符都是用char类型存储的,当然也可以用int类型来存储。这个地方使用int来存储字符有一个微妙但很重要的原因:为了把有效数据和输入的结束(EOF)区分开来。getchar()在没有更多输入数据时返回一个特殊值,这个值不会跟任何实际的字符混淆。这个值称为 EOF(end of file,文件结束)。我们必须把c变量声明成一个大到足够存储任何getchar()返回的值的类型。我们不能用char类型,因为c必须大到足够容纳任意可能的char还有EOF。因此我们使用int类型。

  如果你看到这里就明白了,或者早就知道原因,那可以不用接着看了。下面是我理解这个原因的思路。

  1.  getchar的函数声明

  虽然看着getchar(),直觉告诉我这应该返回char类型吧,但还是让我们看看C语言中 getchar()的函数声明:  

int getchar ( void );

  嗯?返回值是int?(不靠谱的直觉啊)在Linux下输入命令:man getchar(),结果更加详细:

NAME
fgetc, fgets, getc, getchar, gets, ungetc - input of characters and strings
[…]
DESCRIPTION
fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on
end of file or error.

  这样我们就明白了,getchar()从标准输入(stdin)流中读取一个字符,把它当作一个unsigned char,然后强制转化成int类型来做为返回值,如果遇到文件末尾或者错误,返回EOF。

  2. EOF是什么

  用google搜索时,首先看到了这样的一个帖子: EOF的定义和如何有效的使用它

  EOF不是:

  •  一个char类型 (a char)
  • 不是一个在文件末尾出现的值 (a value that exists at the end of a file)
  • 不是一个可能在文件中间出现的值(a value that could exist in the middle of a file)

  C99标准规定(见 7.19.1 Introduction):  

EOF
which expands to an integer constant expression, with type int and a negative value, that
is returned by several functions to indicate end-of-file,that is, no more input from a
stream;

  好,我们明白了 EOF 是一个宏,展开后为一个整数常量表达式(integer constant expression),是int类型(C语言中整数常量是int类型的),而且值是负值。一些函数用它作为返回值,表示流中没有更多的输入。

  让我们去定义它的头文件<stdio.h>中去看看:  

#define EOF (-1)

  那么 EOF 在计算机中十六进制表示形式是 0xFFFFFFFF(有符号数在计算机中是一般用补码(two’s-complement)表示)。通过getchar函数的定义,我们知道getchar() 从标准输入(stdin)流中读取一个unsigned char类型的字符0xXX,然后强制转化成int 类型 0x000000XX(对无符号数,进行零扩展),此时这个值是大于等于零的。

  所以,EOF(0xFFFFFFFF)不可能出现在文件中间(文本文件中),它与字符(character)是截然不同的值。

  3.使用char类型存储getchar()这类函数的返回值  

/* copy input to output; 2nd version */
main()
{
char c;
c = getchar();
while(c != EOF){
putchar(c);
c = getchar();
}
}

  上述这段代码中,c = getchar(); 会将getchar()的返回值int强制转化为char类型,就将32位的int截断为8位的char。之后的 c != EOF,又会将c强制转化为int类型,就将8位的char类型进行扩展,扩展为32位int类型。在扩展时,如果char类型为无符号数,进行零扩展,如果char类型为有符号数,进行符号扩展。下面的两个表分别展示了上面的这两个转换过程。为了制表方便,假设int是16位。  

——————————— ———————————————-
| int到char转化(截断) | | char到int转化(扩展) |
——————————— ———————————————-
| 十进制 | int | char | | char |unsigned char=>int| signed char=>int|
|———|————-|——-| |——-|——————|—————–|
| 2 |00 00 00 02 | 02 | | 02 | 00 00 00 02 |00 00 00 02 |
| 1 |00 00 00 01 | 01 | | 01 | 00 00 00 01 |00 00 00 01 |
| 0 |00 00 00 00 | 00 | | 00 | 00 00 00 00 |00 00 00 00 |
| EOF(-1) |FF FF FF FF | FF | | FF | 00 00 00 FF |FF FF FF FF |
| -2 |FF FF FF FE | FE | | FE | 00 00 00 FE |FF FF FF FE |
——————————– ———————————————-

  可见,如果char是无符号的,那么上面那段代码中,当getchar()返回EOF时,c!=EOF 条件仍然满足。此时程序不能正常终止。

  大家能不能自己写代码验证一下C语言中从char到int的、int到char的强制类型转化呢?

PS:

  MSVC中char类型默认是有符号的char类型,可以在编译时加入 /J 参数来把默认的char类型从signed char 改变到 unsigned char

  gcc中,char类型默认也是有符号的,可以在编译时加入参数 -funsigned-char 或者 -fsigned-char 来指定char的符号类型。

总结:感觉此问题是函数编写这考虑问题不全面引起的,属于设计缺陷,单单为了兼容eof而使得整个函数返回值为int类型,在内存稀缺的情况下是不明智的。完全可以通过在ASCII码中位eof找到一个位置来避免这个问题,当然如果是出于历史原因比如ascii在前而函数的编写在后,或者ascii码已满,无法为eof留下位置,则是一种好的折中方式。当然,也有可能函数的编写这考虑的更远,为了兼容两个字节的unicode编码方式,也是可能的。不管怎么样,只需要留意getchar返回值是int类型即可.

转载:http://www.chawenti.com/articles/11000.html

时间: 2024-10-25 15:27:26

getchar返回int类型的相关文章

使用MyBatis查询int类型字段,返回NULL值时报异常的解决方法

当配置mybatis返回int类型时 select id="getUserIdByName" parameterType="string" resultType="int"> SELECT id FROM user WHERE userName = #{userName} </select> 会报错如下: org.springframework.web.util.NestedServletException: Request p

C#控制台基础 返回类型为void的 int 类型 参数委托与匿名函数

1 code 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace ConsoleApplication2 8 { 9 //返回类型为void的 int 类型 参数委托 10 public delegate void MyFirst(int num); 11 class P

【C语言】编写一个函数,传入a,b两个int类型的变量,返回两个值的最大公约数。

/*编写一个函数,传入a,b两个int类型的变量,返回两个值的最大公约数. 例如:输入传入(0 , 5)函数返回5,传入(10 , 9)函数返回1,传入(12 , 4)函数返回4 */ #include <stdio.h> int yue(int a,int b) { int temp; int n; if (a>b) { temp=a; a=b; b=temp; } n=a; if(a==0) return b; else while(n!=0) { if( a%n==0 &&

oc对象函数什么时候返回值类型使用instancetype

oc中定义对象函数时经常会返回本类类型的对象,此时返回值类型用instancetype或者本类对象*都可以,什么区别呢? 其实主要区别在返回值是不是self并且有继承 如果返回值是self并且作为父类,那么返回值最好写成instancetype 举例说明: 父类的声明 @interface Father : NSObject @property(readwrite,nonatomic,assign)NSInteger item; //元素自增 为了比较自增返回类型定为instancetype -

JAVA函数的返回值类型详解以及生成随机数的例题

函数的四要素:函数名.输入.输出(返回).加工. 函数分为两种:一种是有返回值得函数,一种是没有返回值的函数. 1. 定义:没有返回值的函数:(当我不需要函数的计算结果再拿出来进行运算的时候,我就不需要有返回值,直接在函数中运算完成加工即可) static void 函数名(参数){ 函数体 } 调用:调用没有返回值的函数: 函数名(实际参数): 在函数中的参数叫做虚参,我们需要给他一个实际的参数才能完成整个运算流程. 例如:我要打印一两个数字的和? public class HS { publ

Android java传递int类型数据给C

本文根据<Android jni简便开发流程>中的开发流程来实现一个java传递int类型数据给C 新建项目,进行简单的布局 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent&quo

POI读取excel单元格,获取单元格各类型值,返回字符串类型

package a; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.text.DecimalFormat; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.h

关于Action返回结果类型的事儿(下)

原文:关于Action返回结果类型的事儿(下) using System;  using System.Collections.Generic;  using System.Linq;  using System.Web;  using System.Web.Mvc;  using System.Web.Mvc.Ajax;     using System.IO;     namespace MVC.Controllers  {      /// <summary>      /// Cont

C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解

原文地址:http://www.cnblogs.com/landeanfen/p/5501487.html 使用过Webapi的园友应该都知道,Webapi的接口返回值主要有四种类型 void无返回值 IHttpActionResult HttpResponseMessage 自定义类型 此篇就围绕这四块分别来看看它们的使用. 一.void无返回值 void关键字我们都不陌生,它申明方法没有返回值.它的使用也很简单,我们来看一个示例就能明白. public class ORDER { publi