深入解读C语言中的符号常量EOF

EOF是指文件的结束符,是一个宏定义
    借助于getchar 与putchar 函数,可以在不了解其它输入/输出知识的情况下编写出
数量惊人的有用的代码。最简单的例子就是把输入一次一个字符地复制到输出,其基本思想
如下:

  • 读一个字符
  • while (该字符不是文件结束指示符)
  • 输出刚读入的字符
  • 读下一个字符

将上述基本思想转换为C语言程序为:

#include <stdio.h>
/* copy input to output; 1st version */
main()
{
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
}

其中,关系运算符!=表示“不等于”。
      字符在键盘、屏幕或其它的任何地方无论以什么形式表现,它在机器内部都是以位模式
存储的。char 类型专门用于存储这种字符型数据,当然任何整型(int)也可以用于存储字
符型数据。因为某些潜在的重要原因,我们在此使用int类型。
      这里需要解决如何区分文件中有效数据与输入结束符的问题。C语言采取的解决方法是:
在没有输入时,getchar 函数将返回一个特殊值,这个特殊值与任何实际字符都不同。这个
值称为EOF(end of file,文件结束)。我们在声明变量c 的时候,必须让它大到足以存
放getchar函数返回的任何值。这里之所以不把c声明成char类型,是因为它必须足够大,
除了能存储任何可能的字符外还要能存储文件结束符EOF。因此,我们将c声明成int类型。
EOF 定义在头文件<stdio.h>中,是个整型数,其具体数值是什么并不重要,只要它与
任何char类型的值都不相同即可。这里使用符号常量,可以确保程序不需要依赖于其对应的
任何特定的数值。
       对于经验比较丰富的C 语言程序员,可以把这个字符复制程序编写得更精炼一些。在C
语言中,类似于

c = getchar()

之类的赋值操作是一个表达式,并且具有一个值,即赋值后左边变量保存的值。也就是说,
赋值可以作为更大的表达式的一部分出现。如果将为c赋值的操作放在while循环语句的测
试部分中,上述字符复制程序便可以改写成下列形式:

#include <stdio.h>
/* copy input to output; 2nd version */
main()
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
}

在该程序中,while 循环语句首先读一个字符并将其赋值给c,然后测试该字符是否为文件
结束标志。如果该字符不是文件结束标志,则执行while语句体,并打印该字符。随后重复
执行while语句。当到达输入的结尾位置时,while循环语句终止执行,从而整个main函
数执行结束。
      以上这段程序将输入集中化,getchar函数在程序中只出现了一次,这样就缩短了程序,
整个程序看起来更紧凑。习惯这种风格后,读者就会发现按照这种方式编写的程序更易阅读。
我们经常会看到这种风格。(不过,如果我们过多地使用这种类型的复杂语句,编写的程序可
能会很难理解,应尽量避免这种情况。)
对while语句的条件部分来说,赋值表达式两边的圆括号不能省略。不等于运算符!=的
优先级比赋值运算符=的优先级要高,这样,在不使用圆括号的情况下关系测试!=将在赋值=
操作之前执行。因此语句

c = getchar() != EOF

等价于语句

c = (getchar() != EOF)

验证与打印EOF

1. 验证表达式 getchar() != EOF 的值是 0 还是 1。

#include <stdio.h>

main()
{
 int c;

  while(c = (getchar() != EOF))
   printf("%d\n", c);
  printf("%d - at EOF\n", c);
}

程序会读取字符,当有字符可读时,getchar() 不会返回文件结束符(EOF),所以 getchar() != EOF 的取值为真,变量 c 将被赋值为 1。当程序遇到文件结束符时,表达式取值为假,此时变量将被赋值为 0,程序将运行结束。

对于一个判断表达式,它的返回值会是一个布尔值。

2. 请编写一个打印 EOF 值的程序

#include <stdio.h>

main()
{
 printf("EOF is %d\n", EOF);
}

符号常量 EOF 是在头文件 stdio.h 中定义的,在这个程序中,printf() 语句中双引号外的 EOF 将被替换为头文件 stdio.h 中紧跟在 #define EOF 之后的文本。

在我们的系统中, EOF 被定义为 -1,但在其它系统中,EOF 可能被定义成其它的值。这正是使用 EOF 等标准符号常量能够增加程序可移植性的原因所在。

(0)

相关推荐

  • 新手小心:c语言中强符号与弱符号的使用

    声明:下面的实例全部在linux下尝试,window下未尝试.有兴趣者可以试一下.文章针c初学者.c语言的强符号和弱符号是c初学者经常容易犯错的地方.而且很多时候,特别是多人配合开发的程序,它引起的问题往往非常行为怪异而且难以定位.什么是强符号和弱符号?在c语言中,函数和初始化的全局变量是强符号,未初始化的全局变量时弱符号.强符号和弱符号的定义是连接器用来处理多重定义符号的,它的规则是:不允许多个强符号:如果一个强符号和一个弱符号,这选择强符号:如果多个弱符号,则任意选一个.它的陷阱:上代码:

  • 浅谈C语言中的强符号、弱符号、强引用和弱引用

    首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引用.在看到3.5.5节弱符号和强符号时,我感觉有些困惑,所以写下此篇,希望能和同样感觉的朋友交流也希望高人指点. 首先我们看一下书中关于它们的定义. 引入场景:(1)文件A中定义并初始化变量i(int i = 1), 文件B中定义并初始化变量i(int i = 2).编译链接A.B时会报错b.o:(.data+0x0): multiple definition of `i':a.o:(.d

  • C语言中的强符号和弱符号介绍

    之前在extern "C" 用法详解中已经提到过符号的概念,它是编译器对变量和函数的一种标记,编译器对C和C++代码在生产符号时规则也是不一样的,符号除了本身名字的区别外,还有强符号和弱符号之分 我们先看一段简单的代码 复制代码 代码如下: /* test.c */  void hello();  int main()  {      hello();      return 0;  } 很显然,这段代码是没法链接通过的,它会报错undefined reference to hello

  • C语言中无符号数和有符号数之间的运算

    C语言中有符号数和无符号数进行运算(包括逻辑运算和算术运算)默认会将有符号数看成无符号数进行运算,其中算术运算默认返回无符号数,逻辑运算当然是返回0或1了. unsigned int和int进行运算 直接看例子来说明问题吧 #include <iostream> using namespace std; int main() { int a = -1; unsigned int b = 16; if(a > b) cout<<"负数竟然大于正数了!\n";

  • 举例讲解C语言链接器的符号解析机制

    1. 符号分类 (1)全局符号:非静态全局变量,非静态函数 (2)外部符号:定义于其它模块,而被本模块引用的全局变量和函数 (3)本地符号:静态变量(包括全局和局部),静态函数 对于静态局部变量,编译器会为其生成唯一的名字.如x.fun1,x.fun2.本地符号对链接器来说是不可见的. 2. 符号决议 当编译器遇到一个不是本模块定义的符号时,会假设该函数由其它模块定义,并生成一个链接器符号表条目,交由链接器处理.如果链接器在它的任何输入模块都没有找到该符号,会给出一个类似undefined re

  • 详解C语言中的符号常量、变量与算术表达式

    C语言中的符号常量 在结束讨论温度转换程序前,我们再来看一下符号常量.在程序中使用 300.20 等类似的"幻数"并不是一个好习惯,它们几乎无法向以后阅读该程序的人提供什么信息,而且使程序的修改变得更加困难.处理这种幻数的一种方法是赋予它们有意义的名字.#define 指令可以把符号名(或称为符号常量)定义为一个特定的字符串: #define 名字 替换文本 在该定义之后,程序中出现的所有在 #define 中定义的名字(既没有用引号引起来,也不是其它名字的一部分)都将用相应的替换文本

  • 深入解读C语言中的符号常量EOF

    EOF是指文件的结束符,是一个宏定义     借助于getchar 与putchar 函数,可以在不了解其它输入/输出知识的情况下编写出 数量惊人的有用的代码.最简单的例子就是把输入一次一个字符地复制到输出,其基本思想 如下: 读一个字符 while (该字符不是文件结束指示符) 输出刚读入的字符 读下一个字符 将上述基本思想转换为C语言程序为: #include <stdio.h> /* copy input to output; 1st version */ main() { int c;

  • C语言中求余运算符的使用解读

    目录 C语言中求余运算符的使用 C语言中求余运算符的注意事项 总结 C语言中求余运算符的使用 C语言的算数运算符中最有特点的是求余运算符%,它可以应用到很多问题的求解中,下面是几个例子. 例1:输出1到100的整数,要求每行输出5个. 每行输出5个,意味着在5,10,15,20…等5的倍数的数字后面需要输出换行,而这些数的共同的特点是能被5整除,余数为0,描述成条件即为:i%5==0. #include "stdio.h" int main() {     int i;     for

  • Go语言中更优雅的错误处理

    从现状谈起 Go语言受到诟病最多的一项就是其错误处理机制.如果显式地检查和处理每个error,这恐怕的确会让人望而却步.下面我们将给大家介绍Go语言中如何更优雅的错误处理. Golang 中的错误处理原则,开发者曾经之前专门发布了几篇文章( Error handling and Go和 Defer, Panic, and Recover.Errors are values )介绍.分别介绍了 Golang 中处理一般预知到的错误与遇到崩溃时的错误处理机制. 一般情况下,我们还是以官方博客中的错误

  • C语言中基础小问题详细介绍

    1.printf格式输出函数 如果格式控制说明项数多于输出表列个数,则会输出错误数据:如果输出表列个数多于格式控制说明数,则多出数不被输出.%md,m指的是输出字段的宽度.如果输出字段位数小于m,则左端以空格补齐,若大于m,则按照实际位数输出.%-md,基本同上,只不过不同之处在于,空格在右端补齐printf参数可以是常量,变量或表达式,VC++ 6.0中采用从右向左顺序求值,从左向右输出如 复制代码 代码如下: int x = 5; printf("%4d%4d%4d", x, ++

  • C语言中常用的几个头文件及库函数

    不完全统计,C语言标准库中的头文件有15个之多,所以我主要介绍常用的这四个头文件stdio.h,string.h,math.h,stdlib.h,以后用到其他的再做补充.下面上干货: 1.<stdio.h>:定义了输入输出函数.类型以及宏,函数几乎占了标准库的1/3. (1)文件访问. FILE *fopen("filename","mode"): 以mode模式打开地址为'filename'的文件,并返回文件指针. 访问模式主要是"r&quo

  • 浅析C语言中的数组及字符数组

    我们来编写一个程序,以统计各个数字.空白符(包括空格符.制表符及换行符)以及所有其它字符出现的次数.这个程序的实用意义并不大,但我们可以通过该程序讨论 C 语言多方面的问题. 所有的输入字符可以分成 12 类,因此可以用一个数组存放各个数字出现的次数,这样比使用 10 个独立的变量更方便.下面是该程序的一种版本: #include <stdio.h> /* count digits, white space, others */ main() { int c, i, nwhite, nothe

  • 详细解析C语言中的开方实现

    关于C语言中的开方计算,首先想到的当然是sqrt()函数,让我们先来回顾一下它的基本用法: 头文件:#include <math.h> sqrt() 用来求给定值的平方根,其原型为: double sqrt(double x); 参数 x 为要计算平方根的值. 如果 x < 0,将会导致 domain error 错误,并把全局变量 errno 的值为设置为 EDOM. 返回值 返回 x 平方根. 注意,使用 GCC 编译时请加入-lm. 实例计算200 的平方根值. #include

  • C语言中函数的声明、定义及使用的入门教程

    对函数的"定义"和"声明"不是一回事.函数的定义是指对函数功能的确立,包括指定函数名,函数值类型.形参及其类型以及函数体等,它是一个完整的.独立的函数单位.而函数的声明的作用则是把函数的名字,函数类型以及形参的类型.个数和顺序通知编译系统,以便在调用该函数时进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致),它不包括函数体.--谭浩强 ,<C程序设计>(第四版),清华大学出版社,2010年6月,p182 这段论述包含了许多概念性错误,这

  • Java语言中的内存泄露代码详解

    Java的一个重要特性就是通过垃圾收集器(GC)自动管理内存的回收,而不需要程序员自己来释放内存.理论上Java中所有不会再被利用的对象所占用的内存,都可以被GC回收,但是Java也存在内存泄露,但它的表现与C++不同. JAVA中的内存管理 要了解Java中的内存泄露,首先就得知道Java中的内存是如何管理的. 在Java程序中,我们通常使用new为对象分配内存,而这些内存空间都在堆(Heap)上. 下面看一个示例: public class Simple { public static vo

随机推荐