简介:C语言是一种广泛应用的高级编程语言,本课程课件涵盖了从基本概念到高级特性的全面知识点,包括数据类型、变量、运算符、流程控制、函数、数组、指针、字符串、结构体、预处理器、输入/输出操作、文件操作、错误处理和编程规范。通过刘桂山教授的课件和复习资料,你可以深入理解这些知识点,并通过实例练习巩固技能,为掌握C语言打下坚实基础。
1. C语言概述
C语言是一种古老而强大的编程语言,自1972年由丹尼斯·里奇在贝尔实验室开发以来,一直是计算机科学领域的中流砥柱。它以其简洁、效率和可移植性而闻名,在操作系统、嵌入式系统和高性能计算等广泛的应用中发挥着至关重要的作用。
1.1 C语言的历史与发展
C语言最初是为Unix操作系统开发的,它继承了B语言的语法和特性,并进行了改进和扩展。随着时间的推移,C语言不断演变,加入了新的特性和标准,以满足不断变化的编程需求。1989年,美国国家标准协会(ANSI)发布了第一个C语言标准,称为ANSI C,它定义了语言的语法和语义,并被广泛采用。2011年,国际标准化组织(ISO)发布了C11标准,引入了新的特性,如多线程和匿名结构。最新的C语言标准是C23,于2023年发布,它进一步扩展了语言的功能,包括模块化和泛型编程。
2. 数据类型与变量
2.1 数据类型的分类与表示
2.1.1 整型
整型是用于表示整数的数据类型,在 C 语言中,整型根据其长度和有无符号分为多种类型,包括:
-
short int
:16 位有符号整数,取值范围为 -32768 至 32767 -
int
:32 位有符号整数,取值范围为 -2147483648 至 2147483647 -
long int
:64 位有符号整数,取值范围为 -9223372036854775808 至 9223372036854775807 -
unsigned short int
:16 位无符号整数,取值范围为 0 至 65535 -
unsigned int
:32 位无符号整数,取值范围为 0 至 4294967295 -
unsigned long int
:64 位无符号整数,取值范围为 0 至 18446744073709551615
2.1.2 浮点型
浮点型是用于表示实数的数据类型,在 C 语言中,浮点型根据其精度分为两种类型:
-
float
:32 位浮点型,精度约为 7 位有效数字 -
double
:64 位浮点型,精度约为 15 位有效数字
2.1.3 字符型
字符型是用于表示单个字符的数据类型,在 C 语言中,字符型使用 char
关键字表示,占 1 个字节,取值范围为 0 至 255,对应于 ASCII 码表中的字符。
2.1.4 枚举类型
枚举类型是一种自定义的数据类型,用于表示一组离散的值,在 C 语言中,枚举类型使用 enum
关键字定义,其语法如下:
enum <枚举类型名> {
<枚举常量1>,
<枚举常量2>,
...
<枚举常量n>
};
枚举常量是枚举类型中定义的唯一值,它们从 0 开始自动分配整数值。
2.2 变量的定义与使用
2.2.1 变量的声明
变量是用于存储数据的内存空间,在 C 语言中,变量需要在使用前进行声明,其语法如下:
<数据类型> <变量名>;
例如,声明一个名为 x
的整型变量:
int x;
2.2.2 变量的赋值
变量声明后,可以通过赋值运算符 =
将值赋予变量,其语法如下:
<变量名> = <值>;
例如,将值 10 赋予变量 x
:
x = 10;
2.2.3 变量的初始化
变量在声明时可以同时进行初始化,其语法如下:
<数据类型> <变量名> = <值>;
例如,声明并初始化一个名为 y
的浮点型变量:
float y = 3.14;
3. 运算符与表达式
运算符是用来对操作数进行操作的符号。表达式是由操作数和运算符组成的公式,用于计算一个值。C语言中提供了丰富的运算符,可以满足各种计算需求。
3.1 算术运算符
算术运算符用于对数值进行算术运算,包括加法(+)、减法(-)、乘法(*)、除法(/)和求余(%)。
int a = 10, b = 5;
printf("a + b = %d\n", a + b); // 输出:15
printf("a - b = %d\n", a - b); // 输出:5
printf("a * b = %d\n", a * b); // 输出:50
printf("a / b = %d\n", a / b); // 输出:2
printf("a %% b = %d\n", a % b); // 输出:0
3.2 关系运算符
关系运算符用于比较两个操作数的大小或相等性,包括等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)和小于等于(<=)。
int a = 10, b = 5;
printf("a == b = %d\n", a == b); // 输出:0(false)
printf("a != b = %d\n", a != b); // 输出:1(true)
printf("a > b = %d\n", a > b); // 输出:1(true)
printf("a < b = %d\n", a < b); // 输出:0(false)
printf("a >= b = %d\n", a >= b); // 输出:1(true)
printf("a <= b = %d\n", a <= b); // 输出:0(false)
3.3 逻辑运算符
逻辑运算符用于对布尔值(真或假)进行逻辑运算,包括与(&&)、或(||)和非(!)。
int a = 1, b = 0;
printf("a && b = %d\n", a && b); // 输出:0(false)
printf("a || b = %d\n", a || b); // 输出:1(true)
printf("!a = %d\n", !a); // 输出:0(false)
printf("!b = %d\n", !b); // 输出:1(true)
3.4 位运算符
位运算符用于对二进制位进行操作,包括按位与(&)、按位或(|)、按位异或(^)和按位取反(~)。
int a = 10, b = 5; // 10 = 0b1010, 5 = 0b0101
printf("a & b = %d\n", a & b); // 输出:0(0b0000)
printf("a | b = %d\n", a | b); // 输出:15(0b1111)
printf("a ^ b = %d\n", a ^ b); // 输出:15(0b1111)
printf("~a = %d\n", ~a); // 输出:-11(0b1111111111111110)
3.5 表达式的求值顺序与优先级
表达式的求值顺序和优先级由运算符的优先级决定。优先级较高的运算符先执行。如果两个运算符具有相同的优先级,则从左到右执行。
| 运算符 | 优先级 | 结合性 | |---|---|---| | () | 最高 | 无 | | ++, -- | 高 | 右结合 | | *, /, % | 中 | 左结合 | | +, - | 中 | 左结合 | | <, >, <=, >= | 低 | 无 | | ==, != | 低 | 无 | | && | 低 | 左结合 | | || | 低 | 左结合 |
例如,在表达式 a + b * c
中,乘法运算符 * 具有比加法运算符 + 更高的优先级,因此先执行乘法运算,然后执行加法运算。
graph LR
subgraph 表达式求值顺序
a[a] --> b[b]
b[b] --> c[c]
c[c] --> d[+ a + b * c]
end
4. 流程控制
流程控制是 C 语言中用于控制程序执行顺序的重要机制,它允许程序根据特定条件执行不同的代码块。C 语言中提供了丰富的流程控制结构,包括顺序结构、选择结构和循环结构。
4.1 顺序结构
顺序结构是最基本的流程控制结构,它按照代码编写的顺序执行语句。在顺序结构中,一条语句执行完毕后,程序会自动执行下一条语句,直到代码块结束。
4.2 选择结构
选择结构用于根据条件执行不同的代码块。C 语言中提供了两种选择结构: if-else
语句和 switch-case
语句。
4.2.1 if-else 语句
if-else
语句用于根据一个或多个条件执行不同的代码块。其语法如下:
if (condition) {
// 条件为真时执行的代码块
} else {
// 条件为假时执行的代码块
}
其中, condition
是一个布尔表达式,如果为真则执行 if
代码块,否则执行 else
代码块。
4.2.2 switch-case 语句
switch-case
语句用于根据一个变量的值执行不同的代码块。其语法如下:
switch (variable) {
case value1:
// variable 等于 value1 时执行的代码块
break;
case value2:
// variable 等于 value2 时执行的代码块
break;
default:
// variable 不等于任何指定值时执行的代码块
break;
}
其中, variable
是要判断的变量, value1
和 value2
是要匹配的值。当 variable
的值与某个 case
的值相匹配时,将执行该 case
对应的代码块。如果 variable
的值与任何 case
的值都不匹配,则执行 default
代码块。
4.3 循环结构
循环结构用于重复执行一段代码块,直到满足某个条件。C 语言中提供了三种循环结构: for
循环、 while
循环和 do-while
循环。
4.3.1 for 循环
for
循环用于根据一个或多个初始化、条件和增量表达式重复执行一段代码块。其语法如下:
for (initialization; condition; increment) {
// 循环体
}
其中, initialization
是循环开始时的初始化操作, condition
是循环继续执行的条件, increment
是每次循环结束后执行的增量操作。
4.3.2 while 循环
while
循环用于只要条件为真就重复执行一段代码块。其语法如下:
while (condition) {
// 循环体
}
其中, condition
是循环继续执行的条件。
4.3.3 do-while 循环
do-while
循环与 while
循环类似,但它会先执行循环体,然后再检查条件。其语法如下:
do {
// 循环体
} while (condition);
其中, condition
是循环继续执行的条件。
5. 函数
5.1 函数的定义与调用
函数是 C 语言中组织代码和实现模块化的基本单元。它允许将代码块封装成一个独立的实体,可以根据需要多次调用。函数的定义语法如下:
returnType functionName(parameterList) {
// 函数体
}
其中:
-
returnType
:函数的返回值类型,可以是void
(无返回值)或其他数据类型。 -
functionName
:函数的名称,遵循 C 语言标识符命名规则。 -
parameterList
:函数的参数列表,可以为空或包含多个参数,每个参数都有自己的类型和名称。 -
函数体
:包含函数执行的代码块。
要调用函数,只需使用其名称并传递适当的参数即可。例如:
int sum(int a, int b) {
return a + b;
}
int main() {
int result = sum(10, 20);
printf("The sum is: %d\n", result);
return 0;
}
5.2 函数参数与返回值
函数参数允许将数据从调用方传递给函数,而返回值允许函数将结果返回给调用方。
参数
- 值传递: 参数传递时,将参数的值复制给函数中的局部变量。因此,在函数中对参数的修改不会影响调用方的原始值。
- 引用传递: 通过使用指针作为参数,可以实现引用传递。这允许函数直接修改调用方的原始值。
返回值
- void: 如果函数不需要返回任何值,则将其返回值类型指定为
void
。 - 非 void: 如果函数需要返回一个值,则将其返回值类型指定为相应的数据类型。函数通过
return
语句返回一个值。
5.3 函数的类型与作用域
C 语言中函数有两种类型:
- 库函数: 由 C 语言标准库提供的预定义函数。
- 用户定义函数: 由程序员定义的函数。
函数的作用域是指函数中变量的可见性范围。函数中的变量分为局部变量和全局变量:
- 局部变量: 仅在函数体内可见,函数调用结束后销毁。
- 全局变量: 在整个程序中可见,可以在任何函数中使用。
5.4 函数的递归
递归是一种函数调用自身的编程技术。递归函数的定义如下:
returnType functionName(parameters) {
// 基线条件
if (condition) {
return baseValue;
}
// 递归调用
return functionName(updatedParameters);
}
其中:
-
基线条件
:终止递归调用的条件。 -
递归调用
:函数调用自身,更新参数并继续执行。
递归函数在解决某些问题时非常有用,例如:
- 遍历数据结构(如链表、树)
- 求解数学问题(如阶乘、斐波那契数列)
6. 数组
6.1 数组的概念与定义
数组是一种数据结构,它存储一组具有相同数据类型的元素。每个元素都有一个唯一的索引,用于标识数组中的位置。数组可以是单维的(一维数组)或多维的(多维数组)。
一维数组
一维数组是一个元素按顺序排列的线性数据结构。每个元素都有一个唯一的索引,从 0 开始。例如,以下代码定义了一个包含 5 个整数元素的一维数组:
int numbers[5] = {1, 2, 3, 4, 5};
多维数组
多维数组是一个包含多个维度的数组。每个维度都有自己的索引。例如,以下代码定义了一个二维数组,其中包含 3 行和 4 列的整数元素:
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
6.2 数组的元素访问与遍历
元素访问
要访问数组中的元素,可以使用索引。索引是一个整数,表示元素在数组中的位置。例如,以下代码访问一维数组 numbers
中的第一个元素:
int first_element = numbers[0];
数组遍历
遍历数组是指访问数组中的每个元素。可以使用 for
循环遍历数组。例如,以下代码遍历一维数组 numbers
并打印每个元素:
for (int i = 0; i < 5; i++) {
printf("%d\n", numbers[i]);
}
6.3 多维数组
多维数组的元素访问和遍历与一维数组类似。但是,多维数组的索引必须指定每个维度的索引。例如,以下代码访问二维数组 matrix
中第一行第二列的元素:
int element = matrix[0][1];
遍历多维数组可以使用嵌套 for
循环。例如,以下代码遍历二维数组 matrix
并打印每个元素:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
6.4 数组的应用实例
数组在 C 语言中有着广泛的应用,包括:
- 存储数据:数组可以用来存储各种类型的数据,例如整数、浮点数、字符和结构体。
- 查找和排序:数组可以用来查找和排序数据,可以使用二分查找和冒泡排序等算法。
- 表格和矩阵:数组可以用来表示表格和矩阵,这在数学和科学计算中非常有用。
- 动态内存分配:数组可以与指针一起使用,用于动态分配内存。这允许程序在运行时根据需要分配内存。
7. 指针
7.1 指针的概念与类型
指针是一种特殊的数据类型,它存储的是另一个变量的地址。指针变量的类型由其指向的变量类型决定。例如,一个指向整型变量的指针变量的类型为 int *
。
7.2 指针的运算
指针可以进行以下运算:
- 取值运算(
*
):获取指针指向的变量的值。 - 取地址运算(
&
):获取一个变量的地址,并将其存储在指针变量中。 - 比较运算(
==
、!=
):比较两个指针是否指向同一个变量。 - 加法运算(
+
):将一个整数加到指针上,指向下一个元素。 - 减法运算(
-
):将一个整数减去指针,指向前一个元素。
7.3 指针的应用
指针在 C 语言中有着广泛的应用,包括:
7.3.1 动态内存分配
指针可以用于动态分配内存。使用 malloc()
函数可以分配一块指定大小的内存,并返回指向这块内存的指针。使用 free()
函数可以释放这块内存。
int *ptr = (int *) malloc(sizeof(int));
*ptr = 10;
free(ptr);
7.3.2 函数指针
指针可以指向函数。这允许我们传递函数作为参数,并在运行时动态调用它们。
void print_message(char *message) {
printf("%s\n", message);
}
int main() {
void (*func_ptr)(char *) = &print_message;
func_ptr("Hello, world!");
return 0;
}
简介:C语言是一种广泛应用的高级编程语言,本课程课件涵盖了从基本概念到高级特性的全面知识点,包括数据类型、变量、运算符、流程控制、函数、数组、指针、字符串、结构体、预处理器、输入/输出操作、文件操作、错误处理和编程规范。通过刘桂山教授的课件和复习资料,你可以深入理解这些知识点,并通过实例练习巩固技能,为掌握C语言打下坚实基础。