1 ASCII表
在计算机中,所有数据都以二进制形式存储和处理,使用0和1来表示信息。这包括字母(如a、b、c、d)、数字(如0、1)以及常用符号(如*、#、@)。为了确保不同计算机之间能够正确地交换和理解这些数据,必须使用统一的编码规则。
ASCII(美国信息交换标准代码)就是一种广泛使用的编码标准,它为128个字符(包括大写和小写字母、数字和一些符号)分配了唯一的二进制值。通过使用相同的ASCII编码,计算机能够相互通信而不产生混淆。
1.1 常见的ASCII值
ASCII表中可以记下部分特殊的值(十进制)(字母从A到Z,从a到z,ASCII值依次递增)
- 'A':65
- 'Z':90
- 'a':97
- 字符0:48
- 数字0:0
1.2 字母的大小写转换
由于ASCII表中的大小写字母对应的ASCII值相差32,所以我们在编写大小写转换程序的时候,就非常便捷。
- 大写转小写:ASCII值+32
- 小写转大写:ASCII值-32
代码示例:字母的大小写转换
#include <stdio.h>
void convertCase(char* str)
{
for (int i = 0; str[i] != '\0'; i++)
{
if (str[i] >= 'A' && str[i] <= 'Z')
{
// 转换为小写
str[i] += 32; // 或者使用 str[i] | 32
}
else if (str[i] >= 'a' && str[i] <= 'z')
{
// 转换为大写
str[i] -= 32; // 或者使用 str[i] & ~32
}
}
}
int main()
{
char str[100];
printf("请输入字符串: ");
fgets(str, sizeof(str), stdin); // 读取输入字符串
convertCase(str); // 调用转换函数
printf("转换后的字符串: %s\n", str); // 输出结果
return 0;
}
2 语句与结构
C语言是一种结构化的程序设计语言,它有三种结构:顺序结构,选择结构,循环结构
- 顺序结构:简单说就是从头到尾,一条路走到头
- 选择结构:简单说就是到一个路口进行多个选择
- 循环结构:简单说就是循环执行一段语句
2.1 选择结构
C语言中提供了两种选择语句
- if语句
- switch语句
2.1.1 if语句
a、if-else语句
if (表达式)
语句1;
else
语句2;
- 如果满足表达式,即表达式为真,则执行语句1;
- 若不满足表达式,即表达式为假,则执行语句2。
- 那何为真假? 0表示假,非0表示真
代码示例:
#include <stdio.h>
int main()
{
int number;
printf("请输入一个整数: ");
scanf("%d", &number); // 读取用户输入的整数
// 判断数字的正负性
if (number > 0)
{
printf("该数字是正数。\n");
}
else if (number < 0)
{
printf("该数字是负数。\n");
}
else
{
printf("该数字是零。\n");
}
return 0;
}
2.1.2 switch语句
switch语句从字面上讲,可以称为开关语句,是一种多分支选择结构,一般与case、break、default配合使用,对流程进行控制。
switch(表达式)
{
case 常量表达式1: 语句1; break;
case 常量表达式2: 语句2; break;
……
case 常量表达式n: 语句n; break;
default: 语句n+1; break;
}
代码示例:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int day;
printf("请输入一个数字(1-7)表示星期几: ");
scanf("%d", &day);
switch (day)
{
case 1:
printf("星期一\n");
break; // 退出 switch 语句
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
break;
default:
printf("输入无效,请输入1到7之间的数字。\n");
break; // 退出 switch 语句
}
return 0;
}
思考:如果没有使用break,会发生什么情况?
如果在 switch 语句中没有使用 break,程序会发生“fall-through”现象,即在匹配到一个 case 后,程序会继续执行后面的所有 case 代码,直到遇到 break 或者 switch 语句的结束。这可能导致意外的输出或逻辑错误。
switch语句规则:
- 数据类型限制:只能针对基本数据类型中的整型类型使用 switch:switch 语句通常用于 int、char 和 enum 类型。其他类型(如 float、double、string 等)不能用于 switch。
- 参数类型:不能使用实型:switch 的参数必须是可以自动转换为整型的类型。实数型(如 float 和 double)无法自动转换为整型,因此不能作为 switch 的参数。
- case 标签:必须是常量表达式:case 标签必须是编译时已知的常量,比如数字字面量(如 62)或字符字面量(如 '8')。不能使用变量或计算表达式。
- 唯一性:case 标签必须唯一:在同一个 switch 语句中,不能有两个 case 标签具有相同的值。这会导致编译错误。
- 语句块:多个语句:在 case 后可以有多个语句,而不需要使用 {} 括起来。这使得 case 可以直接包含多条语句。
- 顺序:case 和 default 的顺序:case 和 default 的顺序可以变化,不会影响程序的执行结果。default 可以放在任何位置。
- default 子句:可以省略 default:default 子句是可选的。如果没有提供,且没有匹配到任何 case,则不会执行任何代码。
2.2 循环语句
C语言中提供了三种循环语句:
- while循环
- for循环
- do...while循环
2.2.1 while循环
while语句可以在条件表达式为真的情况下,循环执行指定的一段代码,直到表达式不为真的时结束。
while(表达式)
{
语句块
}
- 先执行while(表达式),条件为真,则执行语句块;
- 执行完语句块,回到while(表达式)继续执行;
- 直到表达式为假,才退出循环,执行while后面的代码 ;
示例代码:循环结束后,打印从 1 到 10 的总和。
#include <stdio.h>
int main()
{
int sum = 0; // 初始化总和
int i = 1; // 初始化计数器
// 使用 while 循环计算总和
while (i <= 10)
{
sum += i; // 将当前计数器值累加到总和
i++; // 计数器加 1
}
// 输出结果
printf("The sum of numbers from 1 to 10 is: %d\n", sum);
return 0;
}
2.2.2 for循环
for 循环是一种控制流语句,用于重复执行一段代码,直到满足特定条件为止。它通常用于已知循环次数的场景。for 循环的基本结构包括初始化、条件判断和迭代步骤。
for (初始化; 条件; 迭代)
{
// 循环体
}
- 初始化:在循环开始前执行一次,用于定义和初始化循环控制变量。
- 条件:在每次循环开始前进行判断,如果条件为真(非零),则执行循环体;如果为假(零),则退出循环。
- 迭代:在每次循环结束后执行,通常用于更新控制变量。
代码示例:计算从1到10的总和
#include <stdio.h>
int main()
{
int sum = 0; // 初始化总和
// 使用 for 循环计算总和
for (int i = 1; i <= 10; i++)
{
sum += i; // 将当前计数器值累加到总和
}
// 输出结果
printf("The sum of numbers from 1 to 10 is: %d\n", sum);
return 0;
}
无限循环: for循环也可以这样写
for (; ;)
{
// 循环体
}
2.2.3 do...while循环
do while 语句是一种控制流语句,用于重复执行一段代码,直到满足特定条件为止。与 while 循环不同的是,do while 循环会至少执行一次循环体,因为条件判断是在循环体执行之后进行的。
- 循环体:包含要重复执行的代码块。
- 条件:在每次循环结束后进行判断,如果条件为真(非零),则继续执行循环;如果为假(零),则退出循环。
代码示例:计算从 1 到 10 的总和
#include <stdio.h>
int main()
{
int sum = 0; // 初始化总和
int i = 1; // 初始化计数器
// 使用 do while 循环计算总和
do
{
sum += i; // 将当前计数器值累加到总和
i++; // 计数器加 1
} while (i <= 10); // 条件判断
// 输出结果
printf("The sum of numbers from 1 to 10 is: %d\n", sum);
return 0;
}
3 函数
函数是一个独立的代码块,用于执行特定的任务。函数可以接受输入(参数),并可以返回输出(返回值)。使用函数的主要目的是提高代码的可重用性、可读性和组织性。
3.1 库函数
在 C 语言中,库函数是预先定义好的函数,通常包含在标准库中,供程序员在编写程序时使用。这些库函数提供了许多常用的功能,如输入输出、字符串处理、数学计算等。使用库函数可以提高开发效率,减少代码的重复编写。
常见的标准库:
1、<stdio.h>:标准输入输出库
函数示例:printf():格式化输出
scanf():格式化输入
fgets():从文件或标准输入读取字符串2、<stdlib.h>:标准库
函数示例:malloc():动态内存分配
free():释放动态分配的内存
exit():终止程序3、<string.h>:字符串处理库
函数示例:strlen():计算字符串长度
strcpy():复制字符串
strcmp():比较两个字符串4、<math.h>:数学库
函数示例:sqrt():计算平方根
pow():计算幂
sin()、cos()、tan():三角函数
3.2 自定义函数
在 C 语言中,自定义函数是程序员根据需要定义的函数,用于执行特定的任务。自定义函数可以帮助组织代码,提高可读性和可重用性。
自定义函数的组成部分
- 函数声明(或原型):告知编译器函数的名称、返回类型和参数类型。
- 函数定义:包含函数的具体实现。
- 函数调用:在程序中执行函数的过程。
自定义函数的语法:
1、函数声明
返回类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...);
2、函数定义
返回类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...)
{
// 函数体
// 执行任务的代码
return 返回值; // 可选
}
3.3 函数参数
函数的参数分为实参和形参,即实际参数和形式参数。
形参(Formal Parameters)
- 定义:形参是函数定义时指定的参数,它们只是占位符,用于接收调用函数时传入的值。
- 作用域:形参的作用域仅限于函数内部,函数结束后形参的值不再可用。
代码示例:
void add(int a, int b)
{
// a 和 b 是形参
printf("Sum: %d\n", a + b);
}
实参(Actual Parameters)
- 定义:实参是函数调用时传入的具体值或变量,它们被传递给形参。
- 作用域:实参的作用域在函数调用时有效,函数调用结束后,实参的值仍然可以在调用位置使用。
代码示例:
int main()
{
int x = 5, y = 10;
add(x, y); // x 和 y 是实参
return 0;
}
形参与实参的关系
- 对应关系:在函数调用时,实参的值会被传递给形参。每个实参对应一个形参,按照顺序匹配。
- 数据传递:值传递:对于基本数据类型(如 int、float),实参的值会被复制到形参中,形参的修改不会影响实参。
- 引用传递:对于指针或引用类型,实参的地址会被传递给形参,形参可以直接修改实参的值。
3.4 函数调用
3.4.1 传值调用
- 函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
代码示例:
#include <stdio.h>
void modifyValue(int x)
{
// 形参 x 是值传递
x = x + 10; // 修改的是局部变量
}
int main()
{
int a = 5;
printf("Before: %d\n", a); // 输出: 5
modifyValue(a); // 传递 a 的值
printf("After: %d\n", a); // 输出: 5 (未改变)
return 0;
}
3.4.2 传址调用
- 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式;
- 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
- 可以节省内存,特别是对于大数据结构(如数组、结构体等)。
代码示例:
#include <stdio.h>
void modifyValue(int *x)
{
// 形参 x 是指针
*x = *x + 10; // 修改的是指针指向的值
}
int main()
{
int a = 5;
printf("Before: %d\n", a); // 输出: 5
modifyValue(&a); // 传递 a 的地址
printf("After: %d\n", a); // 输出: 15 (已改变)
return 0;
}
3.5 函数的嵌套调用和链式访问
3.5.1 嵌套调用
嵌套调用是指一个函数在其内部调用另一个函数。这种方式可以让代码更加模块化,便于重用和维护。嵌套调用可以是直接的,也可以是间接的(即函数 A 调用函数 B,函数 B 又调用函数 C)。
代码示例:
#include <stdio.h>
int square(int x)
{
return x * x; // 计算平方
}
int sumOfSquares(int a, int b)
{
return square(a) + square(b); // 嵌套调用
}
int main()
{
int result = sumOfSquares(3, 4); // 计算 3 和 4 的平方和
printf("Sum of squares: %d\n", result); // 输出: 25
return 0;
}
3.5.2 链式访问
链式访问是将一个函数的返回值直接作为另一个函数的参数进行调用。这种方式常见于方法链(method chaining),尤其是在面向对象编程中,但在 C 语言中也可以实现。
代码示例:
#include <stdio.h>
int doubleValue(int x)
{
return x * 2; // 返回值是 x 的两倍
}
int square(int x)
{
return x * x; // 返回值是 x 的平方
}
int main()
{
int result = square(doubleValue(5)); // 链式访问
printf("Result: %d\n", result); // 输出: 100
return 0;
}
3.6 函数声明与定义
3.6.1 函数声明
函数声明用于告知编译器函数的名称、返回类型和参数类型,但不提供函数的具体实现。它通常出现在函数定义之前,以便在调用函数时,编译器能够知道其类型信息。
代码示例:
#include <stdio.h>
// 函数声明
int add(int a, int b);
int main()
{
int result = add(5, 3); // 调用函数
printf("Result: %d\n", result);
return 0;
}
// 函数定义
int add(int a, int b)
{
return a + b; // 返回 a 和 b 的和
}
注意:
- 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了;
- 函数的声明一般出现在函数的使用之前,要满足先声明后使用;
- 函数的声明一般要放在头文件中 。
- 在未来的工程中,代码是比较多的,函数一般是放在.h文件中声明,在.c文件中实现的 。
3.6.2 函数定义
函数定义是函数的具体实现,包括函数的名称、参数、返回类型和函数体。函数体包含了执行的具体代码。
代码示例:
// 函数定义
int add(int a, int b)
{
return a + b; // 返回 a 和 b 的和
}
3.7 函数的递归
在C语言中,递归是一种函数调用自身的编程技术。递归通常用于解决可以分解为更小子问题的问题。
每个递归函数都应具有两个主要部分:
- 基准情况(Base Case):递归停止的条件。
- 递归情况(Recursive Case):函数调用自身以解决更小的子问题。
递归的两个必要条件
- 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
- 每次递归调用之后越来越接近这个限制条件。
代码示例:计算一个整数的阶乘
#include <stdio.h>
// 递归函数计算阶乘
int factorial(int n)
{
// 基准情况:n为0或1时,阶乘为1
if (n == 0 || n == 1)
{
return 1;
}
// 递归情况:n! = n * (n-1)!
else
{
return n * factorial(n - 1);
}
}
int main()
{
int number;
printf("Enter a positive integer: ");
scanf("%d", &number);
if (number < 0)
{
printf("Factorial is not defined for negative numbers.\n");
}
else
{
printf("Factorial of %d is %d\n", number, factorial(number));
}
return 0;
}