国内C语言教材中几种值得商榷的说法
作者:巨同升
“C语言程序设计”这门课程在国内高校普遍开设已有近三十年,课程的建设和研究取得了长足的进步,涌现出了数量众多、各具特色的C语言教材。尽管如此,在许多C语言教材中还或多或少地存在着一些不准确甚至是值得商榷的说法。下面将对国内教材中常见的几种说法进行辨析,并期望与广大同行商榷。
1. 关于C语言的输入输出语句
有的教材中说“C语言中没有输入输出语句,只有输入输出函数”,果真如此吗?
完整的C语言是由语言标准和标准库两部分组成的[1]。语言标准相当于C语言的内核,标准库(主体是库函数)相当于C语言的扩展部分。这种设计极大地增强了C语言实现的灵活性和程序的可移植性,因为可以根据某类计算机的硬件特点而单独修改C语言标准库部分的实现。
在C语言的语言标准部分的确没有输入输出语句,也没有输入输出函数。不过在C语言的标准库部分,明确地定义了各种输入输出函数,从而可以在C语言程序中以语句的形式来调用这些输入输出函数。
既然这种函数调用语句可以实现输入输出的功能,将它们称为“输入输出语句”(而不必称为输入输出函数调用语句)也是顺理成章的。
因此正确的说法是:在C语言的语言标准部分没有输入输出语句,但在C语言的标准库部分有输入输出函数,从而在完整的C语言中有输入输出语句。
2.关于八进制和十六进制负整数
有的教材中说“在C语言中,八进制和十六进制整数只有正数,没有负数”,这种说法是有问题的。实际上,在C语言中,八进制和十六进制整数既可以使用正数,也可以使用负数。下面的程序就是一个很好的证据。
#include <stdio.h>
int main(void)
{int a,b,c,d;
a=0127;
b=-0127;
c=0x1af;
d=-0x1af;
printf("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);
printf("a=%o,b=%o,c=%x,d=%x\n",a,b,c,d);
return 0;
}
该程序的运行结果如下图所示。
从该程序的运行结果可以发现,程序中可以使用八进制和十六进制的负整数,但是不能以负数形式输出八进制和十六进制的整数。
因此正确的说法是:在C语言中,以八进制和十六进制形式输出整数时,只能输出正数和0,不能输出负数。
3. 关于整数在内存中的表示形式
有的教材中说“在C语言中,整数是以二进制补码的形式在内存中存储的”。其实,这种说法是不准确的。在C语言中,有符号整数的确是以二进制补码形式在内存中存储的,其最高位为符号位,用以表示该整数的正负。但是对于无符号整数来说,由于不存在负数,并不需要表示正负的符号位,因而它的每一位都是数值位,这种表示形式应称为“无符号二进制形式”。
因此正确的说法是:在C语言中,有符号整数是以二进制补码形式在内存中存储的,无符号整数是以无符号二进制形式在内存中存储的。
4. 关于后自增(减)运算符的优先级
国内绝大多数的C语言教材都说“在C语言中,后自增(减)运算符的优先级与前自增(减)运算符相同”。其实,这种说法是有问题的。
在传统C语言(即C89标准之前的C语言)规范中,后自增(减)的优先级的确是与前自增(减)相同的,都是2级。但是,从C89标准开始,已经将后自增(减)的优先级调整为1级,而前自增(减)的优先级依然为2级,从而使得后自增(减)的优先级高于前自增(减)[1] [2]。遗憾的是,这种调整并未在国内的绝大多数C语言教材中反映出来。
5. 关于scanf函数中的第二个参数
为什么scanf函数中的第二个参数只能是变量的地址,而不能是变量名本身呢?大多数教材中并未给出解释,而有的教材则认为是为了提高编程的灵活性,使得scanf函数中的第二个参数既可以是若干个变量的地址,也可以是指向某些内存单元的指针。其实,真正的原因在于C语言中函数参数的单向传递规则,即只能将实参的值传递给对应的形参,而不能将形参的值传递给对应的实参[3]。
在程序中调用scanf函数完成数据的输入,是通过执行该函数的函数体语句实现的。不过在执行该函数的函数体时所输入的数据,首先存放于函数体中定义的局部变量中。调用scanf函数时,若直接以待存储数据的变量名作为实参,则函数体中局部变量(包括形参)的值并不能直接传递给实参;若改用待存储数据的变量的地址作为实参,就可以通过跨函数间接引用的方式,将函数体中局部变量的值传递给主调函数中的变量了。
6. 关于malloc函数的返回值类型
在大多数教材中,在调用malloc函数时,总是要对其返回值进行强制类型转换。例如,
int *p;
p=(int *)malloc(sizeof(int));
这些教材中认为这种强制类型转换是必不可少的,其实并非如此。在传统C语言(即C89标准之前的C语言)规范中,malloc函数的返回值为char *类型,由于这种类型与其他的指针类型都不是赋值兼容的,因此不能直接进行赋值运算,而必须先进行强制类型转换,再进行赋值运算[2]。
不过,从C89标准开始,已经将malloc函数的返回值类型改为void *类型。void *是C89标准中定义的通用指针类型,通用指针与所有其他类型的指针都是赋值兼容的,即可以不经过强制类型转换而直接相互赋值[2]。例如,
int *p;
p=malloc(sizeof(int));
此外,calloc函数和realloc函数的用法与malloc函数是类似的。
7. 关于文本打开方式与二进制打开方式
C语言中的各类文件打开方式均包括两种,如读方式包括“r”和“rb”,写方式包括“w”和“wb”。在大多数C语言教材中,将文件的这两种打开方式称为“打开文本文件”与“打开二进制文件”。
这种叫法很容易使人误解为用第一种打开方式创建的就是文本文件,用第二种打开方式创建的就是二进制文件。其实一个新创建的文件是文本文件还是二进制文件的决定因素,是向文件中写入数据的函数,而不是文件的打开方式[3]。一般而言,使用fprintf、fputc和fputs等函数创建的文件,是文本文件;而使用fwrite函数创建的文件,则是二进制文件。相应地,fscanf、fgetc和fgets等函数用于读取文本文件;而fread函数则用于读取二进制文件。
既然如此,为什么还要将文件的打开方式区分为这两种方式呢?其实,这源于两类操作系统对于回车换行的不同处理方式。在第一类操作系统(如UNIX和Linux)的文本编辑软件中,采用与C语言相同的处理方式,只需用一个换行符,即可实现回车换行。而在第二类操作系统(如DOS和Windows)的文本编辑软件中,则采用与C语言不同的处理方式,需要用一个回车符和一个换行符的组合,方可实现回车换行。
因此,在第二类操作系统中,当用第一种方式打开文件并对文件进行写入操作时,将会把每一个换行符替换为一个回车符和一个换行符的组合;当用第一种方式打开文件并对文件进行读出操作时,将会进行相反的替换。然而,当用第二种方式打开文件并对文件进行写入和读出操作时,将不会对字符进行任何替换。
而在第一类操作系统中,第一种打开方式与第二种打开方式是没有区别的。不论用哪种方式打开文件,在对文件进行写入和读出操作时,都不会对字符进行任何替换。
可见,这两种打开方式均是既可以打开文本文件,也可以打开二进制文件,区别在于对回车换行的处理方式不同。因此,正确叫法应该是“文本打开方式”与“二进制打开方式”[3]。
参考文献 :
[1]Samuel P. Harbison Ⅲ.C语言参考手册(第5版).北京:机械工业出版社,2003.
[2]K.N.King.C语言程序设计现代方法(第2版).北京:人民邮电出版社,2010.
[3]巨同升.C语言程序设计新思路[M].北京:科学出版社,2020.
国内C语言教材中几种值得商榷的说法相关推荐
- 语言abline画不出线_教材中定性分析的R语言实例
我们的年级由于疫情,统计这一章被甩在了高二,和前一个版本的教材<必修三>相比,也发生了一些变化. 对于统计图表的呈现,由于个人学艺不精,Geogebra有统计功能,但自己的使用存在着局限性 ...
- python语言包含的错误,Python语言程序中包含的错误,一般分为三种,以下____________不是其中的一种...
Python语言程序中包含的错误,一般分为三种,以下____________不是其中的一种 答:编译错误 人体体温能自动调控在37度,其原因是( ). 答:人体内产生的热能是分批放出的 人体内有完善的 ...
- c语言共有几种运算符_【填空题】C语言一共有 ()个关键字,()中控制语句,()种运算符...
[填空题]C语言一共有 ()个关键字,()中控制语句,()种运算符 更多相关问题 [填空题] 对煤进行工业分析的目的,是为了判断煤的(). [填空题] 钛的比重是不锈钢的一半,抗腐蚀性是不锈钢的(). ...
- 请描述定时器初值的计算方式_单片机C语言编程中定时器初值计算的两种方法...
单片机C语言编程中,定时器的初值对于初学者真的是比较不好计算,因此我总结了以下几种方法. 第1种方法: #define FOSC 11059200L //晶振的频率 #define TIMS (655 ...
- C语言中几种输入方式
当我们输入一串字符或者数字时,需要一种标志作为输入结束的标志,所以我总结了以下几种: 1.输入一串字符串以回车键作为输入结束的标志 char ch; while(((ch=getchar())!='n ...
- keil c语言 延迟程序,Keil C51程序设计中几种精确延时方法
前几天时间在做一个基于51单片机开发板的等精度频率计,用LCD1602液晶显示的,晶振是22.1184MHZ,用得是测频率法,目的是想做到能够测试0--900KHZ的信号. 液晶显示部分花了我好几天才 ...
- 在c语言程序设计中函数有两种类型 和,在C语言程序设计中函数有两种类型:__________和__________...
在C语言程序设计中函数有两种类型:__________和__________ 以下程序的输出结果是()intA:6B:3C:2D:1 红楼梦中提到的名菜有A:酒酿清蒸鸭子B:奶油松瓤卷酥C:四喜丸子D ...
- C语言中三种大括号格式的规范
C语言中三种大括号格式的使用规范 简介: 在C中,使用大括号的方法无所谓对还是错--只要每个开括号后都有一个闭括号,你的程序中就不再会出现与大括号有关的问题.然而,有三种著名的大括号格式:Kernig ...
- C语言中几种报错类型的解决方案
C语言中几种报错类型的解决方案 声明:本人小白,第一次写博客,由于查报错原因比较困难,为了方便记下自己敲代码遇到的问题而随笔写下,文中有诸多错误,希望路过的大佬能够对我的文章批评指正.我也会不断修改完 ...
- c语言和python语言分别是一种什么语言_作为入门语言,C语言和Python哪一种更值得选择?...
初学编程,应该学习哪一门编程语言,有不少人感到困惑,那么我们到底该如何选择呢? C语言和Python作为多种语言中两种语言,只是语法不同而已.以其作为入门语言的话,那还是各有千秋,各有各的好处的. 有 ...
最新文章
- leetcode-92 反转链表II
- 易点租加速电脑租赁市场布局
- 蚂蚁金服“刷脸”支付技术解读:错误率低于百万分之一
- html鼠标悬停出现新元素,CSS:我如何将鼠标悬停在一个元素上,并显示另一个元素?...
- MySQL: Connection Refused,调整 mysql.ini中的 max_connections
- Qt for Python使用Qt中的Properties
- 前端学习(983):jquery概念
- 浏览器对象模型(BOM)
- cad lisp 两侧偏移并删除_CAD做钣金件展开的原理你知道吗?
- Oracle oradebug 命令 使用说明
- Java开发逻辑思维题
- 指标权重确定方法之熵权法
- 使用Java将中文转化为拼音
- python的十句名言_程序员的二十句励志名言,看看你最喜欢哪句?
- 宝付国际跨境知识小课堂 | 人民币外汇市场是个啥?
- 计算机和通讯技术对我们生活的改变,信息技术的发展,带来了哪些方面的改变?...
- 日语学习-多邻国-问候
- 有的人活着他已经死了,有的人死了他还活着
- 【深度学习】目标检测之YOLOv3算法
- python 矢量化计算