国内C语言教材中几种值得商榷的说法

article/2025/11/5 16:29:02

 作者:巨同升

“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.


http://chatgpt.dhexx.cn/article/5EaVQDXm.shtml

相关文章

既然谭浩强的C语言教材不好,那应该选什么书作C语言教材?

易道云学院C语言/C语法学习不在于你看了多少书&#xff0c;而在于你实实在在写了多少有效代码。易道云学院 回到这个问题&#xff0c;其实我个人认为&#xff0c;看什么样的书&#xff0c;也是需要应对不一样的场景易道云学院去有目的地涉猎。我暂时想到了几种情况&#xff0c;…

c语言课本答案解析宋士银,c语言教材

22.40定价&#xff1a;28.00(8折) /2007-02-01 根据教育部高等学校计算机科学与技术教学指导委员会提出的《关于进一步加强高等学校计算机基础教学的意见暨计算机基础课程教学基本要求》的有关要求&#xff0c;编者组织了一批多年工作在教学一线且有丰富教学经验的教师编写了《…

C语言的环境变量配置

一、编辑器选择与安装 这边选用的编辑器是比较常见的devc&#xff0c;当然vscode和vc也都可以用来学习C语言&#xff0c;要软件和C语言环境变量的话可以加QQ群&#xff1a;373270625 第一步全部默认勾选就行无脑下一步 第二步选择一个放软件的文件夹&#xff0c;我这边选择的…

Jmeter环境变量配置

解压后 电脑桌面----》“计算机”图标----》鼠标右键选择“属性”----》点击高级系统设置----》高级---》环境变量页面 1.在系统变量框&#xff0c;点击“新建”&#xff0c;建立一个变量&#xff1a;JMETER_HOME,值为你解压的jmeter安装路径 2.配置classpath变量&#xff0c…

java设置环境变量jre_JRE环境变量配置图解

JRE(Java Runtime Environment,Java运行环境),运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。如果大家需要查看JRE环境变量配置图解过程,看完本文你的问题也就迎刃而解了。 我们这里使用jre-7u67-windows-i586的32位JRE安装包,大小只有20多M,比JDK7小了…

Anaconda环境变量配置

Anaconda Anaconda环境变量配置 学习自用 解决问题 例如在VSCode中使用虚拟环境运行python代码导致的问题 *ImportError: DLL load failed while importing win32gui*新建系统变量&#xff0c;添加Anaconda安装路径 以后想用其他环境直接修改变量值就可以了 新建环境变量 变…

npm环境变量配置

NPM 使用介绍 NPM是随同NodeJS一起安装的包管理工具&#xff0c;能解决NodeJS代码部署上的很多问题&#xff0c;常见的使用场景有以下几种&#xff1a; 允许用户从NPM服务器下载别人编写的第三方包到本地使用。允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。…

windows 10中R的环境变量配置

创建于&#xff1a;20221113 修改于&#xff1a;20221113 文章目录 1、情况介绍2、环境变量配置3、参考资料 1、情况介绍 win10 64bit系统中&#xff0c;已经安装好了R&#xff0c;Rtools &#xff0c;Rstudio&#xff0c;并且已经配置了java开发环境。 上述两个链接讲述的非…

SVN环境变量配置

1、环境变量配置 2、复制地址 3、环境变量配置&#xff1a; 步骤: 右击电脑属性、高级系统设置、高级、环境变量、 系统变量、找到Path、双击进入、新建粘贴到刚刚复制的地址&#xff0c;确定即可。

Mac环境变量配置(Java)

1.打开终端&#xff1a; 2.输入命令&#xff1a;【/usr/libexec/java_home -V】,查看默认的jdk下载地址&#xff08;绿色下划线的就是jdk默认路径&#xff09;&#xff08;注意⚠️&#xff1a;命令行终端是区分大小写的【-v 是不对的&#xff0c;必须是大写 -V】&#xff09; …

adb环境变量配置

关于adb 什么ADB如何配置CMD命令窗口的使用 什么是adb 首先&#xff0c;我们先说说&#xff0c;adb是什么&#xff0c;他的全称叫安卓调试桥&#xff0c;也就是Android debug bridge &#xff0c;它是连接Windows平台和安卓手机之间的软件调试工具&#xff0c;很适合软件开发…

Android环境变量配置

1 先再到本地SDK位置&#xff0c;C:\Users\27322\AppData\Local\Android\Sdk ​ 本地SDK路径&#xff0c;android studio安装默认路径 2 新建环境变量&#xff1a;ANDROID_HOME&#xff0c;值&#xff1a;C:\Users\27322\AppData\Local\Android\Sdk ​ 右键我的电脑&#xff0…

node环境变量配置

首先&#xff0c;node被我安装在D盘 我下一步需求是&#xff0c;将系统默认的全局包下载路径&#xff08;C:\Users\Administrator\AppData\Roaming&#xff09;换到D盘下面&#xff0c;以减少C盘存储压力。 具体实施&#xff1a; 第一&#xff1a;在nodejs文件夹下&#xff0c…

python环境变量配置

python现在的版本&#xff0c;主要是python2和python3两个大版本&#xff0c;这两个版本有很大的不同。 当我们在自己电脑上同时安装了python2.x和python3.x版本的解释器的时候&#xff0c;就需要对环境变量的配置进行一定的修改。 【大概解释一下&#xff0c;我对环境变量的…

nodejs环境变量配置

1.右键"此电脑 - 属性" 2.左侧点击"高级系统设置" 3.点击环境变量 4.在系统变量下 新建【NODE_PATH】,设置第三方依赖包安装目录 D:\Program Files\nodejs\node_global\node_modules // node.js安装目录 5.在系统变量下的【Path】中&#xff0c;添加上n…

QT环境变量配置

QT官网地址&#xff1a;https://my.qt.io/ 一、windows系统 1.1 QT下载安装 1.2 进入Qt安装目录 1.3 拷贝gcc所在的路径&#xff08;不同安装目录有所差异 C:\Qt\Qt5.5.0\Tools\mingw492_32\bin 1.4 设置windows环境变量 计算机&#xff08;右击&#xff09;&#xff0d;> 属…

java环境变量 的配置与详解(全网最详细教程)

笔者这学期开始学习java课程&#xff0c;学习java开发首先需要配置java运行环境变量。虽然上课老师也讲了如何配置java环境变量&#xff0c;可是笔者的同学还是有好多都不会配置&#xff0c;所以笔者最近配置了特别多次java环境变量。如下笔者详细解释从JDK安装到环境变量的装配…

js向数组里添加元素

js中对于数组的操作很常见&#xff0c;下面记录一下js向数组添加元素的方法。 let myArray[11,22,33]; console.log(原数组&#xff1a;,myArray); myArray.push(44,55); console.log(用push在数组后面插入元素&#xff1a;,myArray); myArray.unshift(66,77); console.log(用…

JS | 数组新增元素

数组可以在一个变量中存放多个数据&#xff0c;相比于变量来说&#xff0c;存取数据是十分便捷的。今天我们来说一下如何给数组新增元素。 1.新增数组元素&#xff0c;修改length长度 var arr [red,green,blue];console.log(arr.length);arr.length 5;console.log(arr);cons…

js向对象中添加元素(对象,数组)

一、添加一个元素 对象名[“属性名”] 值 (值:可以是一个值,可以是一个对象,也可以是一个数组) 这样添加进去的元素,就是一个值 或 对象 或 数组 var obj {"name":"leo"}; console.log(obj); <!-- 往对象中添加元素 对象名["属性名"] 值…