C语言中为什么不能用char类型来存储getchar()的返回值
2012-12-23 17:05
821 查看
代码:
直觉告诉我getchar返回值应该是char类型的,这个地方为什么不能用char类型来存储getchar()的返回值呢?
其实文中解释的很清楚,可当时没有看明白:
在键盘或者屏幕上的字符都是用char类型存储的,当然也可以用int类型来存储。这个地方使用int来存储字符有一个微妙但很重要的原因:为了把有效数据和输入的结束(EOF)区分开来。getchar()在没有更多输入数据时返回一个特殊值,这个值不会跟任何实际的字符混淆。这个值称为 EOF(end of file,文件结束)。我们必须把c变量声明成一个大到足够存储任何getchar()返回的值的类型。我们不能用char类型,因为c必须大到足够容纳任意可能的char还有EOF。因此我们使用int类型。
如果你看到这里就明白了,或者早就知道原因,那可以不用接着看了。下面是我理解这个原因的思路。
嗯?返回值是int?(不靠谱的直觉啊)在Linux下输入命令:man getchar(),结果更加详细:
这样我们就明白了,getchar()从标准输入(stdin)流中读取一个字符,把它当作一个unsigned char,然后强制转化成int类型来做为返回值,如果遇到文件末尾或者错误,返回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 是一个宏,展开后为一个整数常量表达式(integer constant expression),是int类型(C语言中整数常量是int类型的),而且值是负值。一些函数用它作为返回值,表示流中没有更多的输入。
让我们去定义它的头文件<stdio.h>中去看看:
那么 EOF 在计算机中十六进制表示形式是 0xFFFFFFFF(有符号数在计算机中是一般用补码(two's-complement)表示)。通过getchar函数的定义,我们知道getchar() 从标准输入(stdin)流中读取一个unsigned char类型的字符0xXX,然后强制转化成int 类型 0x000000XX(对无符号数,进行零扩展),此时这个值是大于等于零的。
所以,EOF(0xFFFFFFFF)不可能出现在文件中间(文本文件中),它与字符(character)是截然不同的值。
上述这段代码中,c = getchar(); 会将getchar()的返回值int强制转化为char类型,就将32位的int截断为8位的char。之后的 c != EOF,又会将c强制转化为int类型,就将8位的char类型进行扩展,扩展为32位int类型。在扩展时,如果char类型为无符号数,进行零扩展,如果char类型为有符号数,进行符号扩展。下面的两个表分别展示了上面的这两个转换过程。为了制表方便,假设int是16位。
可见,如果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的符号类型。
#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的符号类型。
相关文章推荐
- C语言中为什么不能用char类型来存储getchar()的返回值
- C语言中为什么不能用char类型来存储getchar()的返回值
- C语言中为什么不能用char类型来存储getchar()的返回值
- 关于C语言中为什么getchar的返回类型不是char而是int的说明
- C语言const与指针——为什么不能用char **给const char **赋值
- C语言const与指针――为什么不能用char **给const char **赋值
- C语言中关于char类型存储的分析 以及signed与unsigned的区别
- C语言中 char 类型的取值范围为什么是-128~127
- 为什么在存储过程中局部变量不能申明text,ntext,image类型的,高手帮我解决一下啊?(急)
- 如果把float改成char又对了 为什么运行的时候岀问题了 C语言 类型问题
- 为什么不能通过返回值类型区分重载?
- C语言学习9: malloc动态内存存储,动态内存分配去空格字符增长版,动态内存分配去符号incr增长版,型参和返回值都是int型的函数的指针,main函数的地址也可以用指针指向,typedef定义函数指针,函数定义与嵌套的作用,返回函数指针类型,const作用
- C语言const与指针——为什么不能用char **给const char **赋值
- 为什么不能用返回值类型来判断方法是否重载呢?
- C语言中typedef不能与存储类型指示符同时用
- c语言中各数据类型在内存中的存储方式
- java泛型类为什么不能声持有类型的静态变量
- oracle函数与存储过程中的类型问题---char 与 varchar2
- C语言之变量类型和存储方式
- 关于java重载为什么不能用不同返回值区分