(This picture comes from: VTuberized Logos

Info

本笔记不应用于速成!

该笔记的语言部分基本参考程序设计入门 ——C 语言_中国大学 MOOC (慕课)。课程老师是浙江大学的翁恺老师。

⚠️不要用 MOOC 刷这门课程!MOOC 上的课程安排极为混乱,有些章节体现不出体系化,而且几个线上讨论区的质量也很低😓都是学生在水贴。B 站上传的版本编排会好一点,至少不会出现前脚讲判断,后脚开始讲循环的习题。MOOC 我上早八

Update:神奇的 MOOC 把一节课硬生生拆成入门和提高两个阶段……~~(深谙初高中课本编排)~~ 对于 MOOC 用户,请移步 2014 年的 C 语言程序设计_中国大学 MOOC (慕课) 以获取更优体验。

本笔记编排较为混乱,但能力有限,积重难返,请见谅。

我打不开参考链接!

国内的网络环境可能打不开笔记中给出的部分参考链接,请自备✈️/🪜。听不懂的自己去查。怕别用,用别怕

你的笔记怎么少了很多算法内容?

翁恺老师的课程以语言为主题,与谭书以及依托该书建立的本校课程语言 + 算法混杂的编排有所不同。如果你发现本笔记的算法部分有缺漏,请查找专门的算法教程,比如 Hello 算法

算法不是编程的全部。代码高天在一次直播中表示,只要刷会了 LeetCode 中的经典 150 题,就可以解决工作中 95% 的算法问题。

Lesson 1 什么是程序设计?

1.1 认识程序设计

学习程序设计的目的:理解计算机的工作方式,认识用计算机解决问题的优劣势。

人类和计算机处理问题的方式区别:

主体 方式 举例
人类 What to do(抽象目标) 打杯水
计算机 How to do(具体步骤) 1. 拿起水杯;2. 走到饮水机处;3. 将水杯放到出水口下;4. 打开出水开关;5. 达到一定水位后关闭出水开关;6. 拿开水杯
  • 程序用于描述 “How to do”,编程人员使用特殊的编程语言来编写对计算机的指令,告诉计算机如何解决问题

    Info

    编程语言按照抽象程度,从低级到高级分为 “机器语言”、“汇编语言(低级语言)” 和 “高级语言”。机器语言使用纯二进制(为了方便会转换成十六进制)编程,汇编语言使用英文助记符编程,高级语言使用接近自然语言的指令编程。C 语言就是一个高级语言 [1]。除非特别说明,否则后文所称 “编程语言” 均指 “高级语言”。

我们用计算机做计算,计算的步骤就是算法 (Algorithm),算法需要编程语言写出来。

下图展示了算法的基本模式:

(原图来自 Harvard CS50)


1.2 计算机如何解决一个方程?

给定一个方程,要求计算机给出方程的解,你要如何命令计算机做这件事?

先从算法角度思考一下,计算机可以用什么方法解决方程?

  • 直接开解:计算机表示没有 CAS(计算机代数系统)?!我不会!
  • 枚举:从起始值开始,一个值一个值验证过去。对计算机而言直接粗暴,但对于大规模数据的处理会偏慢
  • 逐步缩小未知数范围(二分法):~~(撕电话簿)~~ 先假设求解结果在某一区间内,然后不断二分。比枚举更快
  • And more…

再从程序角度思考一下,我们怎么告诉计算机用规划好的步骤解决方程?

  • 解释:用另一个程序去理解用户程序,理解结果由该解释程序直接执行。解释过程不产生任何东西,而只是做动作。下次执行用户程序时,需要再走一遍解释过程。
  • 编译:用另一个程序去翻译用户程序(翻译为汇编语言),翻译结果由新产生的可执行文件执行。由于编译过程会产生新的程序文件,下次执行用户程序时,不用再走编译过程。

然而,编程语言本身没有解释 / 编译之分,人们所说的 “解释语言 / 编译语言” 指的是语言常用的执行方式不同,不代表语言只能用解释 / 编译方式执行。有的编程语言甚至会混用解释和编译。

Tip

如果硬要分出区别,那么解释语言有特殊的计算能力,而编译语言有确定的运算性能。过去有人吐槽解释语言的运行效率偏慢,但随着计算机物理性能的提高,这种差异已经大大缩小了。

可以参考 1.2 程序被其他程序翻译成不同的格式 | 深入理解计算机系统(CSAPP)


Lesson 2 C 语言简介

详见 C 语言的历史和版本


Lesson 3 第一个 C 语言程序

3.1 认识你的工具

好了,现在我们要编写第一个 (?) C 语言程序了。但在这之前,让我们先来了解需要的开发工具吧。

我真的需要知道这些吗?

有的人会说:“我是来敲代码的,你们要干什么?” 认为开发环境设置是一个高级主题,初学者学习到一定程度 “自然而然地” 会接触到,不需要在一开始就介绍。对于他们而言,重要的是 “先让代码跑起来”。其中的一些激进者甚至表示:“初学者不需要本地安装任何开发软件,只需要使用类似于 OnlineGDB 的在线开发工具即可。” 而我也确实找到了一个仅使用 OnlineGDB 的 Python 小课程
你当然可以忽略下面的介绍。Dev-C++ 已经提供了开箱即用的 C/C++ 开发环境,一些教师也鼓励 “不求甚解”,允许学生 “暂时性屏蔽知识”。
但你要确保在之后能够想起来、返回来学习如何配置并适应开发环境。在 B 站学生 UP 主降星驰的视频中,即使在哈尔滨工业大学,大部分大二学生仍然没有完全适应开发环境:课程推荐的 Code::Blocks 的 debug 功能配置难度较高,而他们中没有人知道如何(使用这个 IDE)进行 debug!

教程采用的是 Dev-C++ (IDE),由于它自带 GCC 编译器 (虽然过于陈旧……)、开箱即用的特点,常用于教学用途,也是 OIers 的白月光。不过由于其开发时间较早,在功能性上会落后于当前常用的一些开发工具,因此也有开发者开发了 Dev-C++ 的改版,改善了 Dev-C++ 的一些痛点。这里推荐一下其中一个版本:小熊猫 C++。其自带的 GCC 版本比较新,界面对国内初学者也比较友好。

IDE, Compiler 和 Editor

注意编译器(Compiler)、集成开发环境(Integrated Development Environment, IDE)和编辑器(Editor)不是一个东西。

Well it’s an IDE (Integrated Development Environment), it means it integrates different tools and makes them work together. So CB is NOT a compiler (nor MS, nor Borland, nor GCC), it is not a debugger (nor MS, nor GDB), it is not a makefile system !

——How to use a nightly build - codeblock

Windows 平台上的编译器主要有 GCC/G++、LLVM Clang 和 MSVC 。它们是开发 C 语言程序时的必备程序,只做编译这一件事情。

集成开发环境主要有 Visual Studio、Clion 以及我们现在用的 Dev-C++。集成开发环境做的是一条龙服务,一般开发所需要的功能都具备。或者说,“开箱即用”。

编辑器主要有 Visual Studio Code、Sublime Text、Vim/NeoVim、GNU Emacs 等。顾名思义,编辑器主业是编写代码,但一些编辑器经过配置也可以承担 IDE 的工作。

它们的关系就像:

<img src=“https://githubpage-image.obs.cn-east-3.myhuaweicloud.com/img/20250127134154078.svg” style=“zoom: 50%;” /


3.2 Hello World

最经典的程序 Hello World 来了!下面是示例:

1
2
3
4
5
6
#include <stdio.h> // 这里也可以用""代替<>
int main() // "main"不要写成"mian"了。它是程序的入口,所有程序从main函数开始运行
{
printf("Hello World!\n"); // 记得语句末的分号
return 0;
}
Tip

新程序先编译一次再运行,之后不需要再编译

这里有一个程序框架

1
2
3
4
5
6
#include <stdio.h>
int main()
{
/*Your code*/
return 0;
}

当前阶段只需要先记住这个框架,然后每次新写一个程序就先写这个框架。

关于这个框架

部分教程会将 int main() 写为 void main(),在特定开发环境中这样写可以通过编译,但这不是标准规定的写法,换个开发环境可能就用不了或者发出警告。

Quote

Please do not email us saying the above works on your particular version of your particular compiler. It is s still illegal. The C++ language, as defined by the C++ standard, says it is illegal; that makes it illegal. We do not care whether your particular compiler generates good code.

Since the compiler is not required to generate sensible code, you don’t know what the compiler will do. The compiler is not required to do something sensible if you violate the rules.

C++ 标准说某个操作非法,那它就是非法的。特定的开发环境下可以通过不代表所有的开发环境都可以通过。当你违反了标准时,你不能寄希望于编译器帮你改正。编译器没有义务必须生成合理的程序。

—— 摘自 References, C++ FAQ

另外,对于标准中未定义或不强制要求的操作,不同编译器的应对行为不同,因此会出现代码的兼容性问题。


末尾的 return 0; 不是必需的。如果没有写这一行代码,编译器在编译时通常会隐式地在代码末尾添加这一行。这其实是用来检测程序退出情况的,如果程序正常退出,那么 main 函数就会返回 0;如果退出不正常,main 函数会返回其他的数字。不论何种情况,程序都会退出,只是退出时的状态不一样。这主要用于编写批处理文件:如果这个程序退出正常,接下来要执行什么指令;如果退出不正常,接下来又要执行什么指令。

可以参考:return 0 vs return 1 in C++ - GeeksforGeeks

[Solved] why do we have to use ‘return 0’ ? - CodeProject


ISO/IEC 9899:2024 标准规定了 main 函数的标准定义模式:

The function called at program startup is named main. The implementation (实现) declares no prototype (原型) for this function. It shall be defined with a return type of int and with no parameters:

1
int main(void) { /* ... */ }

or with two parameters (参数) (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

1
int main(int argc, char argv[]) { /* ... */ }

or equivalent (同等的); or in some other implementation-defined manner.

关于程序注释

我在程序中写了这样一句 /*Your code*/,这实际上是多行注释(也可用于单行),此外还有 // 代表单行注释注释插入在程序代码中,用来向读者提供解释信息。编译器会自动忽略注释,在预处理时移除注释,因此注释对程序的功能不会有任何影响,但能显著提高程序的易读性。写注释是一件麻烦事,但当你看别人的代码时,又会希望对方写了详尽的注释。

接下来解读一下不属于这个框架的代码:

1
printf("Hello World!\n");

这行代码起到输出作用。printf 是一个函数,顾名思义,“print” 说明程序会输出(打印)一些东西出来,而末尾的 “f” 指的是格式化 (Formatted)。“” 里面的内容叫做字符串\n 表示换行操作,不属于输出内容,程序执行时也不会输出 \n(实践出真知,问某个操作可不可行时可以先实验一下)

Info

字符是一个宽泛的概念,英文字母、数字、汉字甚至 Emoji 等都属于字符。多个字符连在一起就形成了字符串。字符同时是一个数据类型

接下来我们讲讲如何处理程序中的错误

  • 程序中出现的语法错误会在编译时被编译器发现。此时 Dev-C++ 会以红底突出错误所在的地方。
  • 编译器会提供具体的错误原因(英文,看不懂就 STFW bing 一下),列在 “编译器” 栏里。但有时编译器给出的错误原因不是很清晰(位置不对),可以往前往后看一看。
Tip

C 语言编译器会忽略代码中的空格与换行,如:

1
2
3
4
5
//Case 1
printf("Hello World!\n") ;
//Case 2
printf("Hello World\n")
;

以上两种形式均能通过编译,说明在语法层面上没有问题,但观感上实在不行。

请使用英文

注意应使用半角 / 英文标点符号,如果使用全角符号会出现语法错误。

尽量使用全英文编程,即使部分开发工具声称 “支持中文”。这能够有效避免开发中的编码问题。

例如,GCC 不支持中文文件名,导致它编译得到的程序名是乱码,使我不得不转向 Clang。

关于警告

除了可以终止代码编译的 “错误” (Error) 信息,我们还有 “警告” (Warning) 信息。有些警告信息会影响到程序运行,有些则可能是代码风格问题。你可以忽略这些警告,因为它们(在入门阶段)不会影响编译,但如果你想写出合乎标准的代码,那么你就必须处理所有警告
在大型项目中,可能就需要严肃考虑警告带来的低效问题了,例如 项目警告对构建速度的巨大影响 - XAOXUU 中的情形:屎山代码的警告数超过 IDE 的显示上限,本地构建项目时开发人员的 MacBook Pro 甚至出现长时间卡顿。


3.3 编程字体

我们建议你为你的代码编辑器配置一个编程字体。编程字体优化了一些字符以提高可读性,扩展了符号支持使其可以显示特殊图案。本笔记使用的编程字体为 Maple Mono NF CN。我们也有 Fira Code、Jetbrains Mono 等字体。


3.4 Calculator

还记得我们前面说的计算机的主业是计算吗?现在我们要编写一个简单的定值计算程序。这里假设我们已经套用了上面的程序框架。

1
2
3
printf("%d\n", 23 + 43);
//OR
printf("23+43=%d\n", 23 + 43);

%d 是一个格式字符串,起到占位作用,告诉程序这里有一个整数要输出。

如你需要查看有哪些格式字符串,可参考:C 库函数 – printf () | 菜鸟教程

下面总结一下 C 语言常用的算术运算符

数学运算符 C 语言对应符号 意义
++ +
- -
×× *
÷÷ /
% 取余
()() () 括号

3.5 C Compiler

介绍 C 语言编译器的历史,重点放在 GCC (the GNU Compiler Collection) 上

C 语言与 UNIX 系统一同成长,正是 C 语言赋予了 UNIX 以可移植性,因此所有的 UNIX 系统都会自带一个 CC (C Compiler)。早期的 CC 由系统厂商各自开发,之后 GCC 横空出世,一统江湖。

UNIX 系统的历史,参见 观点 | Unix 历史:一个伟大作品的诞生.
为什么强调可移植性?看看汇编语言吧。

DOS 系统上有微软的 MSCC,但没有 GCC。

之后 Linux 出现,同样有 GCC 的身影。

Windows 系统上原本只有微软自己的编译器可用。后来有人希望将 GCC 移植到 Windows 上,这时开发者就需要进行这些工作:

  1. 修改 GCC 本身,使 GCC 可以接入 Windows 的 API

  2. 修改库 (library),使得 GCC 可以在 Windows 上调用这些库

Info

我们可以把库看作是一套工具,不同的库里有不同的函数,具有不同的用途,就像螺丝刀套件有不同的刀头。做事情前,我们需要知道要用什么工具,因此在程序开头,我们要 include 这些库,让程序知道我需要这些库,执行下面的代码时就懂得调用。

由此,人们分化出了两种解决方案:

  1. 以 Cygwin 为代表的编译器集采用 UNIX 系统上的库,这种编译器编译出来的 C 语言程序本来是给 UNIX 系统运行的,但通过这个库中的某些接口,Windows 上也可以运行这些程序

  2. 以 MinGW 为代表的编译器集采用 Windows 系统上的库,这种编译器编译出来的 C 语言程序是直接运行在 Windows 上的


3.6 配置 GCC

Dev-C++ 提供的 GCC 版本过于古老,我们可以自己配置一个较新的 GCC。

MinGW 有多个维护者,这里介绍两个维护版本:TDM-GCC (Dev-C++ 选用) 和 MSYS2。

TDM-GCC 的配置流程是这样的:下载安装程序:tdm-gcc > 运行安装程序 > 选定安装文件夹 > 自定义安装项目(建议全装) > 检查安装是否成功(在终端中运行 “gcc” 命令,返回 gcc.exe: fatal error: no input files 说明已经安装成功,否则进行环境变量配置)

Tip

Win11 下配置环境变量的位置在设置 > 系统 > 系统信息 > 高级系统设置 > 环境变量。配置环境变量时,在 TDM-GCC 安装目录下找到 bin 文件夹,将 bin 文件夹的路径拷贝至环境变量中(用户变量 / 系统变量均可)

对于 MSYS2,MSYS2 项目官网有基础安装教程以供参考,此处不再赘述。

Info

其实 TDM-GCC 早在 2021 年后就停止更新了…… 如果要自己配置开发环境,我们更建议使用 MSYS2。VSCode 官方也推荐 MSYS2。


3.7 配置 VSCode

厌倦了 Dev-C++ 老掉牙的界面,希望来点现代化的东西?那就来尝试一下 VSCode 吧!
和自带编译和 Debug 环境的 Dev-C++ 不同,未经配置的 VSCode 无法走通开发流程,因此上手难度会高一点。
但 VSCode 配置难如登天吗?官方提供的教程大约 10 分钟就能阅读完毕,不出半小时就能得到一个基本可用的编译环境。
这不是新手不可逾越的大山。

可供参考的教程

官方配置教程:C++ programming with Visual Studio Code(English, GCC);

(建议优先参考 VSCode 官方的教程,因为我们还不熟悉 json 文件配置。有些作者擅自修改 json 内容,造成兼容性问题。点名某 SDN,有教程作者甚至下场喷人,翻完文档才发现是 TA 自己不按官方的来)

第三方教程参考:Windows/MacOS/Linux 上 VSCode 配置 C++: Clang + Clang-based Tools + CMake + Conan(简体中文,Clang)

(选择第三方教程时,请注意教程的时效性)


3.8 了解命令行

Note

不需要强行使用命令行。对于刚入门的新手,直观化的 GUI 界面更容易接受并配置。

目前为止,我们使用的开发工具都是图形化的,这给了一些人有偏差的观念:编译运行程序就是一个 ➤ 键的事。

但这个按键只是一个表象,它背后代表的是一行行命令。IDE 的 “输出” 窗口已经揭示了这一点。

小熊猫 C++ 的工具输出窗口

在这一长串命令中,核心的编译命令是 g++.exe E:/Cproject/Algorithm_Exercise/test.cpp -o E:/Cproject/Algorithm_Exercise\test.exe。后面的命令称为 “编译器标志 (flag)”,用来告诉编译器如何编译代码。

但这同样省略了一些东西。从源码到可执行文件不是一个 -o 标志即可搞定的,实际上经过:

从源码到可执行文件的过程


Lesson 4 计算

4.1 存储数据 —— 如何定义一个变量

前面我们所写的程序没有输入环节,输出结果都是写死在程序里的,这导致如果我们想要其他的输出结果,就得修改代码,再走一遍编译流程。这显然是很麻烦的,因此我们需要一个输入环节。

怎么设计一个输入环节呢?我们需要:

  • 1. 有地方存储输入的数据
  • 2. 有办法输入数据
  • 3. 输入的数据能参与计算

接下来给出代码示例(部分):

1
2
3
4
5
int price = 0;
printf("请输入金额(元):");
scanf("%d", &price);
int change = 100 - price;
printf("找您%d元。\n", change);

程序的输入操作在终端中进行。输入以行为单位进行,回车代表行结束,程序在这一步的输入操作也结束,程序正式读取用户输入。在行结束前,程序不会读取任何数据。

接下来我们将解读一下这段代码。

首先, int price = 0; 这一行定义了一个变量。变量是整数型变量,名字是 price,初始值是 0. 变量解决了我们上述设计输入环节的第 1、3 点:变量是一个保存数据的地方,数据通过变量参与到计算中去。

我们要如何定义一个变量呢?有以下规则要遵守:

  • 1. 变量定义的语法为:type_name var_name;。多个同类型变量可以在一行内定义,如:int a, b, c;。在变量定义时,我们可以为变量赋初始值,如我们上面的示例。

    Tip

    注意不同类型变量不能在一行内定义,比如 int a, float b, char c; 就是非法的

    另外,有些机构的代码规范不建议在一行内定义多个变量:DCL04-C. Do not declare more than one variable per declaration - SEI CERT C Coding Standard

  • 2. 变量需要一个名字作为 “标识符” 以区别于其他变量。基本的命名规则是:标识符只能由字母、数字和下划线组成,数字不能出现在第一个位置上,C 语言的关键字(保留字)不能作为标识符

    Tip

    关键字不需要去记忆,实践和编译器会告诉你哪些是关键字。例如,在小熊猫 C++ 中定义 int if 时,代码诊断会提示:[错误] expected unqualified-id before 'if'

    关键字对拼写敏感,就算字母的大小写不一致,也认为不是关键字。比如 Int 就是一个合法的变量名而不是关键字。

  • 3. 建议采用能够标示变量用途的名称作为变量的名字。a, b, c 在代码规模很小时固然简单好用,但在工程实践以及较大规模的项目中,我们可能得为这些简单的变量名编写注释,否则其他人甚至几天后的自己就无法理清变量用途了。

    Tip

    例如,同样是最大最小值,有人写的是 maxmin,有的人写的是 ab;我们说 ana_n,有的人偏偏要用 aba_b

C99 及之后的标准允许开发人员在程序的任何位置定义变量,因此像示例代码中的 int change = 100 - price; 这种复合写法是正确的。但 ANSI C 标准只允许用户在代码开头定义变量,也就不支持这种复合写法了

Info

但现在我们学习 C 语言应该不会遇到这种问题。即使是 Dev-C++ 5.11 自带的古老 TDM-GCC 4.9.4 也已经支持 C99 标准。


4.2 变量赋值与初始化

int price = 0; 中,除了变量定义外,我们还进行了变量的初始化,或者宽泛地说,变量的赋值。赋值操作由 = 这个赋值运算符进行,表示将 =右边的值赋给左边的变量。在变量定义时的赋值操作,叫做初始化。对于拥有运算符的式子,我们称为表达式

Warning

计算机的赋值运算符和数学中的等于号代表的意义并不相同。等于号代表一种关系,赋值运算符代表一种动作。关系是静态的,动作是动态的。在数学中 a=b 和 b=a 是等价的,而在计算机中,两者代表的动作完全相反。

在计算机中,我们用 ==(两个等号) 代表等于号。

初始化不是必要的,但你必须保证当你使用变量时(变量出现在赋值运算符的右边)变量存储了你需要使用的数据,不然计算机会给变量随机赋一个奇怪的值。

Tip

常量必须初始化,变量也只该在极罕见的情况下不初始化。 在准备好合适的值以前,别引入这个名称。

——1 基础知识・C++ 导览 第二版 简体中文版

[4] 在用到之前别定义变量,一旦定义就立刻初始化。 声明可以出现在 for 语句初始化部分、 在条件中以及任何允许声明语句出现的地方。

——16 历史及兼容性・C++ 导览 第二版 简体中文版

多个变量不能同时初始化,因为赋值本身是一个运算。

Warning

1
2
3
// int a = b = 2; // error: ‘b’ undeclared (first use in this function)
int a, b;
a = b = 2;

到这里我们是不是还没谈 int 这个东西?int 是一种数据类型,表示 price 这个变量能且只能存储整数数据,其他数据类型后续会学习。在 C 语言中,所有的变量在使用前必须定义或者声明,而在变量定义时,必须确定变量的数据类型,变量中只能存放对应类型的数据,程序运行时也不能中途改变变量的类型


4.3 读取输入 ——scanf 函数

好,变量已经解决了两个问题,现在还有一个问题:怎么输入数据呢?

我们来看这一行代码:

1
scanf("%d", &price);

scanf 也是一个函数,同样的,“scan” 说明程序会读取(扫描)用户输入的内容,“f” 指格式化。

Info

C 库函数 scanf 从标准输入 stdin 读取格式化输入。

下面是声明:

1
int scanf(const char *format, ...)
  • format – 这是 C 字符串,包含了以下各项中的一个或多个:空格字符、非空格字符format 说明符
    • 其中说明符格式为:[=%[*][width][modifiers]type=]

如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF

——C 库函数 – scanf () | 菜鸟教程scanf, fscanf, sscanf, scanf_s, fscanf_s, sscanf_s - cppreference.com

等等,price 前面的 & 是什么?这涉及到 C 语言一个重要的内容 —— 指针,在后面 (Lesson20) 我们会详细解读。现在请将 & 视作一个初始化符号,没有这个符号,scanf 就没法将数据赋值给变量 price

scanf 的格式化输入比较严格,下面看一组示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
scanf("price %d %d ", &a, &b);
printf("%d %d", a, b);
-------Terminal--------
Case 1:
>>price 1 2 //注意这里末尾有空格
>>1 2//正确输出
Case 2:
>>price 1,2
>>1 0//scanf没有读取到变量b的输入
Case 3:
>>price 1 2//注意末尾无空格
>> //程序判定输入未完成,按回车也没用
>>2//此时需要再输入任意一个字符充当空格
>>1 2 //输入结束,输出正确
Case 4:
>>1 2//我好懒我不想输入price
>>0 0 //出错啦QAQ
>>price 1 2 //格式字符串"price %d %d "中的所有东西都需要你输入,包括说明性的price。因此当我们需要输出说明文字时,先用printf,再用scanf
Case 5:
>>price QWQ QAQ//精神错乱(
>>114514 1919810 //scanf读到了两个不符合要求的数据,拒绝传递给变量,导致两个变量没有值,计算机内存给两个变量随机分配了值

我们再来看一个例子:

1
2
scanf("%d", &a);
scanf("%c", &c);

要怎么输入?正确的输入格式应该长这样:

1
22d

注意中间不要加空格与换行。空格和换行本身也视作字符,会被后面读取字符的 scanf 读取。

希望输入空格或换行?请在输入 intscanf 明确指出需要用户输入,即 scanf("%d\n", &a);

Tip

也有另外两种做法:

  1. 使用 stdlib.h 库中的 getchar() 来消耗掉输入结束时的换行和空格

  2. 在读取字符前加个空格,即写成 scanf(" %c", &c),这个空格可以让 scanf 忽略所有空白字符(包括换行)


4.4 常量

int change = 100 - price; 中,100 被称为常量,它的值不会因为外界输入而变化。这种直接写在程序里的常量,叫做直接量(literal)。直接量很直接,但如果你的程序有很多地方用到了这个常量,一旦日后这个常量有变化了,你会愿意一处一处修改吗?所以定义常量是更好的方式。

常量和变量有一定的相通之处。常量需要指定数据类型,需要常量名称,命名遵循变量命名的原则。但也有差别,例如:

1
const int AMOUNT = 100;

const 是一个修饰符,加在 int 的前面,给 “变量” 加上一个 constant(不变的)属性,表示这个变量的值在初始化过后就不能更改了。如果在初始化之后给常量赋值(在赋值运算符的左边),编译器就会报错:error: read-only variable is not assignable。如果希望这个常量的值由用户来决定,请使用变量。

为了突出常量,建议将常量名称全大写

接下来我们就可以将 100 更换为 AMOUNT 了。之后我们想更改常量的值时,只需要修改开头 AMOUNT 的值就行了。

Info

你还会看到有人这样 “声明” 常量:

1
#define AMOUNT 100

我们称之为宏 (Macros)。在 C 语言中,宏是一段在程序中被替换为宏值的代码。每当编译器遇到宏名时,它会用宏的定义替换该名称。

1
2
3
4
#define base 20

//...
int a = n + base;// actually `int a = n + 20`

宏通过#define 指令定义,必须在源文件开头定义(不能在函数中定义)。定义语句不需要以分号(;)结束。

上面定义的是对象宏 (Object-like Macros)。一个对象宏类似于一个简单的标识符,它通常用于将符号名称替换为表示为常量的数值 / 变量。

还有其他类型的宏。参考 Macros and its types in C - GeeksforGeeks 以及 Macros (GNU C Language Manual)

宏和 const 常量的不同之处可以参考 C++ 宏定义 #define 和常量 const 的区别c++ 宏和 const 的区别 - Neal_Pu

Quote

如果一个表达式计算出来的值超出了其类型的取值范围,那么这个表达式就不能被用作常量表达式,因为它的值在编译时无法确定

尽管编译器可能接受一些非标准的常量表达式,但这些表达式并不满足某些特定的要求,因此不能在需要整数常量表达式、算术常量表达式或地址常量表达式的场合中使用。

——Constant expressions - cppreference.com

小彩蛋

Ariel, listen to me
OO languages? is a mess.

Prgramining in C is better then anything they got over there
The syntax might seem much sweeter
Where objects and subtypes play
But frills like inheritance
Will only get in the way!
Admire C’s simple landscape
Efficiently dangerous!
No templates or fancy pitfalls…
like Java and C++!

Program in C
Program in C

Pointers, assembly
Manage your memory
With malloc() and free()!
Don’t sink your app with runtime bloat
Software in C will stay afloat
Do what you want there
Close to the hardware
Program in C

Resource: 🐢Vedal987 - Program in C

D 指导的中译:

阿瑞尔,听我说
面向对象?一团糟

用 C 编程胜过所有花哨
那里的语法看似甜美
对象与子类嬉闹
但继承之类的装饰
只会碍手碍脚!
仰望 C 简洁的风景
危险却高效!
没有模板或炫技天坑…
像 Java 和 C++!

用 C 编程
用 C 编程

指针,汇编
亲手管理内存
用 malloc () 和 free ()!
别让运行时臃肿沉船
C 语言软件永葆轻便
在这里随心所欲
直抵硅基心跳
用 C 编程


  1. 由于 C 语言允许直接操作计算机硬件和内存,因此它也被认为是 “低级的”。一些观点认为,C 属于 “中级语言”。
    参考:Computer programming language | Types & Examples | Britannica高级编程语言 - MDN Web 文档术语表:Web 相关术语的定义 | MDN ↩︎


©2025-Present Watermelonabc | 萌ICP备20251229号

Powered by Hexo & Stellar latest & Vercel & 𝙌𝙞𝙪𝙙𝙪𝙣 𝘾𝘿𝙉 & HUAWEI Cloud
您的访问数据将由 Vercel 和自托管的 Umami 进行隐私优先分析,以优化未来的访问体验

本博客总访问量:capoo-2

| 开往-友链接力 | 异次元之旅 | 中文独立博客列表

猫猫🐱 发表了 41 篇文章 · 总计 209.8k 字