课件

2.pdf

思维导图

2. 数据类型与表达式(1) - 图1

2.1 引言

引言

2.1.1 学习程序设计语言的困惑.mp4 (31.91MB)00:02:06 | 学习编程的流程

image.png

00:04:15 | 学习编程,实际上是在学什么?

学习如何与计算机沟通

notes

简述

本节主要就是打鸡血、顺带向编程小白介绍该如何学习编程。
学编程,要多撸代码,慢慢来……

老师提供的学习编程的建议

image.png

  • 核心:多撸代码
  • 不懂咋办?
    • 一开始写不来就查资料去抄代码
    • 抄完后一条语句一条语句地看
    • 看不懂就查资料,直到理解为止
    • 随着理解的语句越来越多,不知不觉地就学会了

理解学习编程实际上是在学什么

image.png

  • 学习编程本质就是在学习和计算机之间的沟通规则
  • 不同的编程语言仅仅是沟通的方式不同,本质上都是一样的,都是为了让计算机能够理解我们想要表达的意思
  • 学习编程,还需要知道 计算机 能够帮我们做哪些事儿
    1. 首先需要知道计算机能做什么
    2. 然后还需要知道如何告诉计算机帮我们做事儿

2.2 C 语言的符号

C语言符号

2.2.1 C语言符号.mp4 (37.96MB)

C语言的运算符

2.2.2 C语言的运算符.mp4 (40.84MB)

notes

简述

  • 2.2.2:认识 C 语言中的运算符、运算符的优先级、运算符的结合性。学会使用括号来控制复杂表达式哪些部分先执行,哪些部分后执行。
  • 在 2.6 中,也提到了运算符的优先级与结合性,到时候回看 2.2 的笔记即可,2.2 的笔记中记录的内容,涵盖了大部分 2.6 中老师在视频中介绍的点。

课件源码 | 通过一个打印 Hello world 的小 demo 来认识一下 C 语言中的部分符号

  1. /*An Example*/
  2. #include <stdio.h>
  3. int main(void) {
  4. // output "hello world"
  5. printf("hello world! \n");
  6. return 0;
  7. }
  • 注释:/*An Example*/ // output "hello world"
  • 标识符:main void printf
  • 关键字:int return
  • 分隔符:;
  • 复合语句标记:{}

注释(Comment)

  • 注释是对程序代码或功能的说明和解释
  • 注释使程序更容易阅读和理解
  • C 编译器在编译程序时对注释完全忽略
  • 注释是写给人看的,不是写给机器看的
  • 注释一般出现在三个位置:文件头、函数前、重点语句块前。
    • 程序开头的注释是用来说明程序的作者、程序版本、程序用途的。
    • 函数前面的注释,一般用来描述函数的功能、调用方法,有时还会描述函数的设计思想。
    • 有些较为复杂的语句,并不容易被看懂和理解,所以需要有一些注释来帮助理解。
  • 多行注释:用 /**/ 包含起来
  • 单行注释:写在 // 后边
  1. int a = 1; // 声明一个变量a并赋值为1
  1. int a = 1;
  2. /*
  3. 声明一个变量 a
  4. 并将 1 赋值给变量 a
  5. */

标识符(Identifier)

  • 标识符(Identifier)是 用来标识变量、函数、数组、结构体、联合体等各种程序实体的名称,也可以理解为 程序中的各种元素的名字
  • 根据标识符是否是用户自定义的,可以将标识符分为两类:
    • 系统预定义标识符(C Standard Identifier):C 语言中自带的一些标识符,它们一开始就存在于 C 语言中,是 C 语言中内置的标识符,在程序中使用时,不需要进行任何声明或定义操作
    • 用户自定义标识符:表示用户新建的标识符,比如变量名、函数名,需要注意的是 用户自定义的标识符不能够与系统自带的标识符重名,否则会导致编译错误。
  • 区分大小写
    • C 语言中的所有标识符都是 区分大小写
    • 比如:
      • sleepSleep 不是一个东西
      • printfPRINTF 不是一个东西
  • 命名规则:
    • 每个标识符都是由 字母、数字和下划线 组成的字符串,其中 第一个字符必须是字母或下划线
    • 标识符是区分大小写的,大写的 A 和小写的 a 是两个不同的标识符
    • 标识符 不能使用 C 语言中的关键字作为名称,如 if、while、int……
  • 命名规范:
    • 标识符可以理解为我们代码中元素的昵称,但是昵称通常是不会随便乱取的,而是按照一定的规范来的。
    • 好的标识符命名可以提高程序的可读性和可维护性,让程序更加清晰明了。以下记录的是前辈们约定俗成的,比较好的命名规范:
      • 标识符的长度是没有限制的,但 不建议使用过长的标识符
      • 标识符的 命名应该有意义(语义化),能够反映其所代表的实体的含义。
      • 变量名:使用小写字母,多个单词之间用下划线分隔,如 student_name
      • 函数名:使用小写字母,多个单词之间用下划线分隔,如 calculate_sum()
      • 宏定义:使用大写字母,多个单词之间用下划线分隔,如 MAX_SIZE
      • 结构体、联合体、枚举类型:使用首字母大写的驼峰命名法,如 StudentInfo
  • C 语言中一些常见的系统预定义标识符:
    • int、float、double、char:基本数据类型
    • void:表示无返回值类型
    • sizeof:表示一个数据类型所占的字节数
    • if、else、switch、case、default:用于条件判断和分支控制
    • for、while、do、break、continue:用于循环控制
    • return:用于函数返回
    • static:表示静态变量
    • const:表示常量
    • enum:定义枚举类型
    • struct、union、typedef:定义结构体、联合体和类型别名
    • 补充:对于上述提到的这些系统预定义标识符,无需刻意去记忆,在后续不断地编程中,用多了,记住它们就是一个自然而然的过程。

关键字(Keywords)

  • C 语言中的关键字其实就是系统预定义标识符,它们描述的都是一个东西,只不过叫法不同罢了。
  • 关键字在语言中具有特殊的含义,不能被用作标识符的名称。
  • C 语言中的关键字共有 32 个,它们分别是:auto、break、case、char、const、continue、default、do、double、else、enum、extern、float、for、goto、if、int、long、register、return、short、signed、sizeof、static、struct、switch、typedef、union、unsigned、void、volatile、while

分隔符(Separator)

  • C 语言中的分隔符是指在程序中 用于分隔语句、表达式、变量等各种程序元素的符号或字符
  • 分隔符可以将一个复杂的程序分解为小的程序块,使程序的结构更加清晰,从而提高程序的可读性和可维护性
  • 分隔符是编译器识别和解析程序的重要依据,可以帮助编译器准确地确定程序的语法结构,帮助编译器正确识别程序的不同部分,使程序能够被正确地解析和编译
  • 如果程序中的分隔符使用不当,就可能会导致编译错误或语法错误
  • 在编写 C 语言程序时,需要注意使用正确的分隔符,并保持良好的代码风格和规范
  • 常见的分隔符及其作用
    • 空格、制表符和换行符:用于分隔单词和行。
    • 圆括号 ():用于表示函数的参数列表和优先级。
    • 方括号 []:用于表示数组的下标。
    • 大括号(复合语句标记) {}:用于表示代码块。
    • 尖括号 <>:用于表示头文件。
    • 逗号 ,:用于分隔语句中的参数、变量和数组元素等。
    • 冒号 ::用于分隔标签和值、表示条件表达式和三目运算符中的分隔符。
    • 分号 ;:用于表示语句的结束符。
    • 反斜杠 \:用于表示转义字符。
    • 单引号 ':用于表示字符常量。
    • 双引号 ":用于表示字符串常量。
    • 注释符号 ///* */:用于表示注释。

运算符(Operator)

  • C 语言中的运算符是指 用来对一个或多个操作数进行特定操作的符号或关键字
  • C 语言中常见的运算符包括:
    • 算术运算符:算术运算符用于进行数学运算,包括加法(+)、减法(-)、乘法(*)、除法(/)、求余(%)等,操作数可以是整型、实型等数值型数据。
    • 赋值运算符:赋值运算符用于将右侧的值赋给左侧的变量,包括等于号(=)以及复合赋值运算符(如+=、-=、*=、/=等)。
    • 比较运算符:比较运算符用于比较两个值的大小关系,包括等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)、小于等于(<=)等。
    • 逻辑运算符:逻辑运算符用于进行逻辑判断,包括逻辑与(&&)、逻辑或(||)、逻辑非(!)等。
    • 位运算符:位运算符用于对二进制位进行操作,包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)等。
    • 条件运算符:条件运算符用于根据某个条件来选择执行不同的代码,包括问号(?)和冒号(:)。
    • sizeof 运算符:计算数据类型或变量占用的存储空间大小
      • 注意:不要误以为 sizeof 是函数
    • ……
  • “操作符”和“运算符”是同义词
    • 在编程语言中,操作符和运算符通常被视为 同义词,都用于表示完成某种特定操作的符号或标记。
    • 在 C 语言中,操作符和运算符这两个术语几乎可以互换使用,它们都表示完成某种特定操作的符号或标记。
    • 补充:
      • 其实很多相同的技术概念会使用不同的中文来描述,主要就是在翻译的时候,不同的人习惯不同导致的。
      • 有些人习惯将 Operator 翻译为操作符,而有些人习惯将 Operator 翻译为运算符,但实际上它们的英文都是 Operator。
      • 举一反三:其实很多其它中文描述的技术名词,很多时候可能就是叫法不同罢了,实际上表示的都是一个含义,到时可以查查它们的英文,也许就豁然开朗了。
  • 运算符是有 优先级结合性

运算符的优先级

运算符的优先级从高到低的顺序如下:

  1. ():括号,用于改变运算的优先级和明确运算的顺序
  2. []:方括号,用于数组下标运算。
  3. ->:箭头,用于结构体指针的成员访问。
  4. .:点号,用于结构体成员访问。
  5. ++--:自增和自减,用于改变变量的值。
  6. ~!-+:按位取反,逻辑取反,负号,正号。
  7. */%:乘法,除法,取模。
  8. +-:加法,减法。
  9. <<>>:左移,右移。
  10. <<=>>=:小于,小于等于,大于,大于等于。
  11. ==!=:等于,不等于。
  12. &:按位与。
  13. ^:按位异或。
  14. |:按位或。
  15. &&:逻辑与。
  16. ||:逻辑或。
  17. ?::三目运算符,用于条件判断。
  18. =+=-=*=/=%=<<=>>=&=^=|=:赋值运算符和复合赋值运算符。

在表达式中,运算符的优先级决定了运算的顺序。例如,对于表达式 a + b * c,乘法运算的优先级高于加法运算,因此先计算 b * c,然后再将 a 加上结果。

补充:

  • 对于上述提到的这些运算符的优先级,无需刻意去记忆,多写多看,自然就熟悉了。
  • 如果非得记的话,只要记第一个 () 即可,知道它的优先级是最高的,我们可以将那些我们希望它优先执行的内容用小括号包裹起来,以改变运算的优先级。

运算符的结合性

如果表达式中有多个同级运算符,那么它们的计算顺序会根据运算符的 结合性(从左到右或从右到左)而定。例如,对于表达式 a = b = c,赋值运算符的结合性是从右到左,因此先计算 b = c,然后再将结果赋值给 a。

运算符的优先级和结合性是两个不同的概念,它们共同作用于表达式的计算过程,帮助程序员准确地理解和预测表达式的计算结果。

  • 运算符的 优先级 用于 确定表达式中哪些运算符优先于其他运算符计算
    • 例:2 + 3 * 4
    • 乘法运算符 * 的优先级高于加法运算符 +
    • 所以表达式 2 + 3 4 会先计算 3 4,再将其结果与 2 相加,最终得到 14
  • 运算符的 结合性 用于 确定当表达式中出现相同优先级的运算符时,它们的计算顺序是从左到右还是从右到左
    • 例:int a = 5, b = 10, c = 15; a = b = c;
    • 赋值运算符 = 是右结合性的
    • 因此表达式 a = b = c 会从右到左进行计算,即先将 c 赋值给 b,再将 b 的值赋值给 a
    • 最终 a b c 都是 15

小结:优先级决定了运算符之间的计算顺序,而结合性则决定了相同优先级的运算符之间计算的先后顺序

  • 当表达式中出现多个 同级 运算符时,我们才需要考虑结合性
  • 结合性分为左结合、右结合
    • 左结合:指运算符从左到右进行计算
    • 右结合:指运算符从右到左进行计算

复合语句块

  • 概念:一组语句构成的块,在 C 语言中用花括号 **{}** 包围
  • 作用:
    • 复合语句块 通常用于控制程序的流程和变量的作用域
    • 复合语句块 可以将多条语句封装在一起,形成一个逻辑单元,使得程序更加清晰易读
  • 块级作用域:
    • 在复合语句块中定义的变量具有 块作用域
    • 只在该块中有效,出了该块就无法访问了
    • 当执行完最后一条语句后,控制权会返回到上一级的语句中

“规则”和“规范”之间的差异

其实这两者之间没有实质性的差异,很多时候我们会默认为它们是等价的。不过笔记中提到的规则和规范,这两者之间是有一定差异的。对于“规则”、“规范”这组词,我们需要知道的是:

  • 规则
    • 强约束,必须按照规则来
    • 如果使用“规则”来描述一个事物,那么表示这是“强”制的要求,如果不遵循,那么程序是没法正常运行的
    • 例:标识符的命名规则,我们如果不按照这个规则来给标识符命名,那么程序会报错,这是强制的约束。
  • 规范
    • 弱约束,相当于是建议
    • 如果使用“规范”来描述一个事物,那么表示这是“弱”约束,可以理解为建议,只是说按照建议来,也许会更好,但是即便我们非要走自己的那一套习惯也是不会影响程序运行的
    • 例:标识符的命名规范,主要是前辈们约定俗成的一套规范,目的是为了让程序的可读性、可维护性更好,以便程序更容易被人(程序员)理解。

运算符的优先级和结合性

  1. #include <stdio.h>
  2. int main() {
  3. int a = 5, b = 10, c = 15;
  4. int result1 = a + b * c; // 优先级:乘法 > 加法
  5. int result2 = (a + b) * c; // 优先级:括号 > 乘法 > 加法
  6. int result3 = a + b - c; // 优先级:加法、减法结合性从左到右
  7. int result4 = a * b / c; // 优先级:乘法、除法结合性从左到右
  8. printf("a:%d\tb:%d\tc:%d\n", a, b, c);
  9. printf("a + b * c = %d\n", result1);
  10. printf("(a + b) * c = %d\n", result2);
  11. printf("a + b - c = %d\n", result3);
  12. printf("a * b / c = %d\n", result4);
  13. return 0;
  14. }
  15. /* 运行结果:
  16. a:5 b:10 c:15
  17. a + b * c = 155
  18. (a + b) * c = 225
  19. a + b - c = 0
  20. a * b / c = 3
  21. */
  1. #include <stdio.h>
  2. int main() {
  3. int a = 2, b = 3, c = 4;
  4. int result = a = b = c;
  5. printf("result: %d\n", result); // => result: 4
  6. return 0;
  7. }

以上代码中的赋值运算符的结合性为从右向左。

  • 首先 b = c 执行赋值操作,将 b 的值赋为 4
  • 然后 a = b 执行赋值操作,将 a 的值赋为 4
  • 最后整个表达式的值为 4 被赋给了 result

2.3 数据类型

数据类型

2.3.1 数据类型.mp4 (71.16MB)

notes

数据

  • 数据是程序处理的基本对象
  • 根据数据的不同性质和用途可分为不同的数据类型
  • 数据是指经过处理、表示、传递和存储等操作后,具有特定含义的数字、字符、符号、图像、声音等各种形式的信息。
  • 数据在计算机科学中非常重要,它是计算机程序处理和生成的基础,也是人们进行信息交流和决策的重要资源。

C 语言中都有哪些数据类型

  • 基本数据类型:
    • 整型:int, short, long, long long
    • 浮点型:float, double, long double
    • 字符型:char
  • 构造数据类型:
    • 数组:用于存储一组相同类型的数据元素
    • 结构体:用于存储不同类型的数据元素
    • 联合体:用于存储多个数据类型中的一个
  • 指针类型:
    • 指针:用于存储变量的地址
  • 枚举类型:
    • 枚举:用于定义一组具有不同值的常量

注意:

  • 在 C99 标准中增加了复合数据类型 _Bool,用于表示布尔值
  • 在 C11 标准中增加了 _Static_assert 和 _Alignas 等新的类型特性

补充:上述提及的这些数据类型,在后续章节都会介绍到,先有个简单的印象就好。

定义变量时,为何要指明变量的数据类型

C 语言之所以需要明确变量的数据类型,是为了 确保程序能够正确地处理变量的数据

  1. 明确变量的数据类型可以告诉编译器在内存中为变量分配多少空间
    • 不同的数据类型在内存中占用的空间大小是不同的
    • 例:int 类型通常占用4个字节的空间,而 char 类型只占用 1 个字节的空间
    • 如果程序中定义了一个变量但没有指定其数据类型,编译器将无法确定为该变量分配多少内存空间,从而导致程序在运行时发生内存错误
  2. 明确变量的数据类型可以告诉编译器如何对变量进行操作
    • 不同的数据类型支持不同的操作
    • 如果程序中使用了不支持的操作,编译器将会报错
  3. 明确变量的数据类型可以提高程序的运行效率和可读性
    • 如果程序中的变量类型不明确,编译器可能需要进行额外的运行时类型检查,从而影响程序的性能
    • 清晰的数据类型定义可以帮助程序员更好地理解代码,并在调试和维护代码时更加方便

不同数据类型之间主要存在哪些差异

不同的数据类型具有不同的存储长度、取值范围、允许的操作。

  • 取值范围不同
    • 不同数据类型可以表示的取值范围不同
    • 例:char 类型可以表示 -128 到 127 的整数,而 int 类型可以表示更大的整数范围
  • 存储空间不同
    • 不同数据类型所占用的存储空间也不同
    • 例:int 类型通常占用 4 个字节,而 char 类型只占用 1 个字节
  • 运算符支持不同
    • 不同数据类型所支持的运算符也不同
    • 例:浮点数可以进行浮点数运算,而整数运算则不能包含浮点数。
  • 精度不同
    • 不同数据类型的精度也不同
    • 例:float 类型可以表示大约 7 位有效数字,而 double 类型可以表示大约 15 位有效数字。
  • 表示方式不同
    • 不同数据类型的表示方式也不同
    • 例:字符类型用单引号括起来表示 'a',而字符串类型用双引号括起来表示 "a"
  • 转换方式不同
    • 不同数据类型之间的转换方式也不同
    • 例:
      • 将一个整数类型的变量赋值给一个浮点数类型的变量时需要进行类型转换,会自动加上小数部分 1 -> 1.00
      • 将一个浮点数类型的变量赋值给一个整数类型的变量时需要进行类型转换,会自动丢弃小数部分 1.23 -> 1

了解不同数据类型之间的差异可以帮助程序员更好地选择适合的数据类型来存储数据,以提高程序的效率和准确性。

判断一个数据应该定义为什么类型的标准

问:什么样的场景,选择使用什么样的数据类型来处理数据?
答:得通过不断的编程,积累经验才行

根据数据的实际需求和特点来选择合适的数据类型,同时需要考虑以下因素:

  • 取值范围
    • 需要根据数据的取值范围来选择适合的数据类型
    • 例:
      • 如果数据取值范围较小,则可以选择 char 或 short 类型
      • 如果数据取值范围较大,则需要选择 long 或 long long 类型
  • 精度
    • 需要根据数据的精度要求来选择适合的数据类型
    • 例:如果需要表示小数点后多位的数值,则需要选择 float 或 double 类型
  • 存储空间
    • 需要考虑数据类型所占用的存储空间大小,以便 在内存使用效率和计算效率之间找到平衡点
    • 例:如果数据量较大,则需要选择较大的数据类型,但同时也要避免过度占用内存空间
  • 运算符支持
    • 需要根据数据类型所支持的运算符来选择适合的数据类型
    • 例:如果需要进行浮点数运算,则需要选择浮点数类型
  • 转换方式
    • 需要考虑不同数据类型之间的转换方式,以便在程序设计中避免类型转换错误和精度丢失等问题。

注意:选择适合的数据类型需要综合考虑数据本身的特点和使用场景,同时也需要遵守 C 语言的数据类型规范

课件源码

  1. #include <stdio.h>
  2. int main () {
  3. int ix = 3, iy = 5, iz;
  4. float fx = 2.0, fy = 5.5, fz;
  5. char cx = 'A', cy = 'D', cz;
  6. iz = ix + iy;
  7. fz = fx + fy;
  8. cz = cx + cy;
  9. printf("%d+%d=%d\n", ix, iy, iz);
  10. printf("%.2f+%.2f=%.2f\n", fx, fy, fz);
  11. printf("%c+%c=%c\n", cx, cy, cz);
  12. printf("%d+%d=%d\n", cx, cy, cz);
  13. return 0;
  14. }
  15. /* 运行结果:
  16. 3+5=8
  17. 2.00+5.50=7.50
  18. A+D=
  19. 65+68=-123
  20. */
  1. #include <stdio.h>
  2. int main() {
  3. int ix = 3, iy = 5, iz;
  4. float fx = 2.0, fy = 5.5, fz;
  5. char cx = 'A', cz;
  6. iz = ix + iy;
  7. fz = fx + fy;
  8. cz = cx + 1;
  9. printf("%d+%d=%d\n", ix, iy, iz);
  10. printf("%.2f+%.2f=%.2f\n", fx, fy, fz);
  11. printf("%c+%c=%c\n", cx, 1, cz);
  12. printf("%d+%d=%d\n", cx, 1, cz);
  13. return 0;
  14. }

Visual Studio 2022lightly