学习 C 鹏哥C语言 HJX_C语言史 Mr.He 2025-08-18 2025-09-15 HJX_C语言进化史 初识C语言 C语言初阶
一. 分支语句和循环语句
分支语句:
if
switch
循环语句:
while
for
do while
goto 语句
什么是语句?
C语句可分为以下五类:
表达式语句
函数调用语句
控制语句
复合语句
空语句
控制语句
控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有九种控制语句。
可分为以下三类:
分支语句(条件判断语句):if 语句、switch 语句
循环语句:do while 语句、while 语句、for 语句
转向语句:break 语句、goto 语句、continue 语句、return 语句
1. 分支语句(选择结构)
if 语句
switch 语句
1.1 if 语句
语法结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 if (表达式) 语句; if (表达式) 语句; else 语句; if (表达式1 ) 语句1 ; else if (表达式2 ) 语句2 ; ` ` ` else if (表达式3 ) 语句3 ; else 语句4 ;
示例
if 语句 1 2 3 4 5 6 7 #include <stdio.h> int main () { if (3 == 3 ){ printf ("Hello, World!" ); } return 0 ; }
if...else 语句 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> int main () { int age = 10 ; if (age < 18 ){ printf ("未成年\n" ); printf ("不能饮酒!\n" ); }else { printf ("成年\n" ); } return 0 ; }
if...else if...else 语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> int main () { int age = 0 ; scanf ("%d" , &age); if (age < 18 ){ printf ("青少年\n" ); } else if (age >= 18 && age < 25 ){ printf ("青年\n" ); } else if (age >=25 && age<40 ){ printf ("中年\n" ); } else if (age >= 40 && age <60 ){ printf ("壮年\n" ); } else if (age >= 60 && age < 100 ){ printf ("老年\n" ); } else { printf ("寿星\n" ); } return 0 ; }
练习
判断一个数是否为奇数 1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> int main () { int num = 0 ; scanf ("%d" , &num); if (num%2 == 1 ){ printf ("奇数\n" ); } else { printf ("偶数\n" ); } return 0 ; }
输出 1~100 的奇数 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> int main () { int num = 0 ; while (num <= 100 ){ if (num%2 == 1 ){ printf ("%d\n" , num); } num++; } return 0 ; }
注:
if 中表达式结果为真,则执行语句
else 遵循就近原则
如果表达式为多条语句,应加上{}以区分。{}为一个代码块
适当的增加{}、空格、空行、换行让代码更有可读性,逻辑清晰
0 为假,非 0 为真
If语句实操
1.2 switch 语句
常用于多分支的情况
语法结构
1 2 3 4 5 switch (整型表达式){ case 整型常量表达式: 语句; }
示例
打印星期
if...else if...else 语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <stdio.h> int main () { int day = 0 ; scanf ("%d" , &day); if (day == 1 ){ printf ("星期一\n" ); } else if (day == 2 ){ printf ("星期二\n" ); } else if (day == 3 ){ printf ("星期三\n" ); } else if (day == 4 ){ printf ("星期四\n" ); } else if (day == 5 ){ printf ("星期五\n" ); } else if (day == 6 ){ printf ("星期六\n" ); } else if (day == 7 ){ printf ("星期天\n" ); } return 0 ; }
switch 语句 break:跳出所在单层循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> int main () { int day; scanf ("%d" , &day); switch (day){ case 1 : printf ("星期一\n" ); break ; 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 ("输入错误\n" ); break ; } }
default 标签
default:所有 case 都不匹配则直接从 dafault 开始执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdio.h> int main () { int day; scanf ("%d" , &day); switch (day){ case 1 : case 2 : case 3 : case 4 : case 5 : printf ("weekday\n" ); break ; case 6 : case 7 : printf ("weekend\n" ); break ; default : printf ("输入错误\n" ); break ; } return 0 ; }
switch 嵌套
switch 语句是可以嵌套的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <stdio.h> int main () { int n = 1 ; int m = 2 ; switch (n){ case 1 : m++; case 2 : n++; case 3 : switch (n){ case 1 : n++; case 2 : m++; n++; break ; } case 4 : m++; break ; default : break ; } printf ("m = %d, n = %d\n" , m, n); return 0 ; }
注:
switch 语句根据整型表达式的值,来判断从哪个 case 进入(如没有 break ,则程序会继续执行到 switch 语句结束)
switch 语句是可以嵌套的
break 语句是中断、停止的意思(直接使得 switch 语句结束)
break 只能跳出它所在的 switch 语句
default 如所有 case 都不匹配则直接从 dafault 开始执行
switch语句实操
2. 循环语句
while 语句
for 语句
do while 语句
2.1 while 语句
语法结构
示例
打印 1~10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> int main () { int a = 1 ; while (a <= 10 ){ a++; if (5 == a) continue ; printf ("%d\n" , a); } return 0 ; }
break:跳出所在单层循环
continue:跳过本次循环,回到 while 判断
注:
while 语句中
break 语句:永久终止它所在的那层 while 循环
continue 语句:跳过本次循环 con~ 后面的代码,直接回到 while 再次判断
while语句实操
延申:
scanf 和 getchar 的读取方式:
注:
键盘和 scanf 和 getchar 中间有一个缓冲区,键盘输入的内容会先进入缓冲区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <stdio.h> int main () { char password[20 ] = {0 }; printf ("请输入密码:" ); scanf ("%s" , password); while ((getchar()) != '\n' ); printf ("请确认密码(Y/N)" ); int ret = getchar(); if ('Y' == ret){ printf ("Yes" ); } else { printf ("No" ); } return 0 ; }
2.2 for语句
语法结构
1 2 3 4 5 for (表达式1 ;表达式2 ;表达式3 ) 循环语句;
示例
建议不要再 for 循环体内修改迭代变量,防止失控
建议 for 循环变量采用 “前闭后开区间” 写法
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> int main () { for (int i = 1 ; i <= 10 ; i++){ if (i == 5 ) continue ; printf ("%d\n" , i); i = 12 ; } return 0 ; }
break:终止 for 循环
continue:跳过本次循环,回到 for 判断
for 循环,初始化部分省略
for循环初始化部分省略,意味着初始化在外部进行
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { int i = 0 ; for (; i <= 10 ; i++){ printf ("%d\n" , i); } return 0 ; }
for 循环,判断部分省略
for循环判断部分省略,意味着判断恒成立
1 2 3 4 5 6 7 #include <stdio.h> int main () { for (;;){ printf ("hello world\n" ); } }
while 循环太分散不容易管理
注:
for 循环的初始化部分,判断部分,调整部分是可以省略的,但是不建议初学时省略,容易导致问题
for 循环判断部分省略,意味着判断恒成立
for 循环中初始化变量,一些老旧编译器可能报错无法识别,建议将初始化部分放在 for 循环外,提高兼容性
建议不要再 for 循环内修改循环变量,防止for循环失去控制
建议 for 循环的循环控制变量的取值采用“前闭后开区间”写法
for语句实操
2.3 do while语句
语法结构
示例
do while 循环至少循环一次
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> int main () { int i = 0 ; do { i++; if (i == 5 ) break ; printf ("%d\n" , i); } while (i < 10 ); return 0 ; }
注:
do while 至少循环一次,使用场景有限,不经常使用
循环语句:
break: 永久终止本次循环,只能终止它所在的一层
continue: 跳过本次循环(continue),进行下一次循环
3. go to语句
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号
从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码
但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。
例如:一次跳出两层或多层循环。 多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环
示例
关机程序
go to 语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <stdio.h> #include <string.h> #include <windows.h> int main () { char input[20 ] = {0 }; system("shutdown -s -t 180" ); printf ("你的电脑将在180秒内关机\n" ); again: printf ("请输入:“No”,以取消关机\n" ); scanf ("%s" , &input); if (strcmp (input, "No" ) == 0 ){ system("shutdown -a" ); printf ("已取消关机!" ); Sleep(3000 ); } else { goto again; } return 0 ; }
while 语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdio.h> #include <windows.h> #include <string.h> int main () { char input[20 ] = {0 }; system("shutdown -s -t 180" ); while (1 ){ printf ("你的电脑将在180秒内关机,请输入:No, 取消关机\n" ); scanf ("%s" , input); if (strcmp (input, "No" ) == 0 ){ system("shutdown -a" ); printf ("以取消关机!" ); Sleep(3000 ); break ; } } return 0 ; }
go to 语句实操
练习 示例
分支语句和循环语句练习
二. 函数
函数是什么
库函数
自定义函数
函数参数
函数调用
函数的嵌套调用和链式访问
函数的声明和定义
函数递归
一. 函数是什么?
在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组 成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软 件库。
二. C语言中函数的分类
1. 库函数
C语言常用的库函数都有:
IO函数
字符串操作函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其他库函数
学会使用库函数
MSDN(Microsoft Developer Network)
www.cplusplus.com http://en.cppreference.com(英文版)
http://zh.cppreference.com(中文版)
注:
使用库函数,必须包含 #include 对应的头文件
2. 自定义函数 自定义函数和库函数一样,有函数名,返回值类型和函数参数
语法结构
1 2 3 4 5 6 7 8 ret_type fun_name (para1, * ) { statement; }
三. 函数的参数 1. 实际参数(实参)
真实传给函数的参数,叫实参
实参可以是:常量、变量、表达式、函数等
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参
2. 形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中猜实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
注:
形参实例化之后其实相当于实参的一份临时拷贝
四. 函数的调用 1. 传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参
2. 传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式
这种传参方式可以让函数和函数外边的变量建立起真正的练习,也就是函数内部可以直接操作函数外部的变量
3.练习 写一个函数,判断一个数是不是素数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <stdio.h> #include <math.h> int is_prime (int x) { int i = 0 ; for (i = 2 ; sqrt (n) >= i; i++){ if (x % i == 0 ){ return 1 ; } } return 0 ; } int main () { int a = 0 ; scanf ("%d" , &a); if (is_prime(a)){ printf ("该函数是素数" ); } else { printf ("该函数不是素数" ); } return 0 ; }
延申:布尔类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <stdio.h> #include <stdbool.h> #include <math.h> bool is_prime (int x) { int y = 2 ; for (y = 2 ; sqrt (x) >= y; y++){ if (x % y == 0 ) return false ; } return true ; } int main () { int i = 0 ; printf ("这是一个判断素数的程序\n" ); printf ("请输入一个数:\n" ); scanf ("%d" , &i); if (is_prime(i)){ printf ("该函数是素数\n" ); } else { printf ("该函数不是素数\n" ); } return 0 ; }
写一个函数,判断一个年份是不是闰年
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> int is_leap_year (int y) { if ((y % 400 == 0 ) || ((y % 4 == 0 ) && (y % 100 != 0 ))) return 1 ; else return 0 ; } int main () { int year = 0 ; printf ("请输入一个年数,以判断该年数是否为闰年:" ); scanf ("%d" , &year); if (is_leap_year(year)){ printf ("该年数是闰年" ); } else { printf ("该年数不是闰年" ); } return 0 ; }
写一个函数,实现一个整型有序数组的二分查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <stdio.h> int binary_search (int x[], int y , int z) { int left = 0 ; int right = z - 1 ; while (left <= right){ int mind = left + (right - left) / 2 ; if (x[mind] > y){ right = mind - 1 ; } else if (x[mind] < y){ left = mind + 1 ; } else { return mind; } } return -1 ; } int main () { int i = 0 ; int arr[] = {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }; int sz = sizeof (arr) / sizeof (arr[0 ]); printf ("已知数组 arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}" ); printf ("输入一个 1~10 的数字,以查找该数在数组内的下标" ); scanf ("%d" , &i); int ret = binary_search(arr, i, sz); if (ret == -1 ){ printf ("找不到\n" ); } else printf ("找到了,下表是%d\n" , ret); return 0 ; }
注:
数组传参实际上传递的是数组首元素的地址,而不是整个数组,所以在函数内部计算一个函数参数部分的数组的元素个数是不靠谱
形参x看似是数组,本质上是指针变量
写一个函数使每调用一次函数 num 的值加 1
将num地址传给函数,修改其本身 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> void Add (int * x) { (*x)++; } int main () { int num = 0 ; Add(&num); printf ("%d\n" , num); Add(&num); printf ("%d\n" , num); return 0 ; }
将 num 的值传给函数,直接返回 x+1 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> int Add (int x) { return x + 1 ; } int main () { int num = 0 ; num = Add(num); printf ("%d\n" , num); num = Add(num); printf ("%d\n" , num); }
五. 函数的嵌套调用和链式访问 1. 嵌套调用 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> void new_line () { printf ("hehe\n" ); } void three_line () { int i = 0 ; for (i=0 ; i<3 ; i++) { new_line(); } } int main () { three_line(); return 0 ; }
函数可以嵌套调用,但是不能嵌套定义(即将一个函数的定义放在另一个函数内部),函数的地位是平等的
2. 链式访问
把一个函数的返回值作为另一个函数的参数
示例
1 2 3 4 5 6 #include <stdio.h> int main () { printf ("%d" , printf ("%d" , printf ("%d" , 43 ))); return 0 ; }
注:
printf 函数的返回值是打印在屏幕上字符的个数
注:
链式访问的前提条件是函数有返回值
函数没写返回类型的时候,默认返回类型是 int
int 类型没写返回值的时候,默认返回最后一条语句的返回值
明确说明 main 函数不需要参数,本质上 main 函数有参数 main(int argc , char* argv [], char* envp [])
六. 函数的声明和定义 1. 函数声明
告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了
函数的声明一般出现在函数的使用之前,要满足先声明后使用
函数的声明一般要放在头文件中
2. 函数定义
函数的定义是指函数的具体实现,交代函数的功能实现
示例
加法_主函数 1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> #include "Add.h" int main () { int a = 0 ; int b = 0 ; printf ("请输入两个数字: " ); scanf ("%d %d" , &a, &b); int sum = Add(a, b); printf ("%d" , sum); return 0 ; }
加法_函数模块 add.c 与 add.h 叫函数模块
函数声明_add.h 函数定义_add.c 1 2 3 4 int Add (int x, int y) { return x + y; }
七. 函数递归 1. 什么是递归?
程序调用自身的编程技巧成为递归
递归作为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的
一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略
只需要少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量
递归的主要思考方式在于:把大事化小
2. 递归的两个必要条件
存在限制条件,当满足这个限制条件的时候,递归便不再继续
每次递归调用之后越来越接近这个限制条件
练习 接受一个无符号整型,按照顺序打印它的每一位
接受一个整型值(无符号),按照顺序打印它的每一位
例如:
输入:1234,输出 1 2 3 4
创建数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> int main () { unsigned int num = 0 ; int arr[4 ] = {0 }; int i = 0 ; scanf ("%d" , &num); for (i = 0 ; num; i++){ arr[i] = num % 10 ; num /= 10 ; } int j = i-1 ; while (j >= 0 ){ printf ("%d" , arr[j]); j--; } return 0 ; }
递归 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> void print (unsigned int n) { if (n > 9 ){ print(n / 10 ); } printf ("%d" , n % 10 ); } int main () { unsigned int num = 0 ; scanf ("%u" , &num); print(num); return 0 ; }
编写函数不允许创建临时变量,求字符串长度
编写函数不允许创建临时变量,求字符串的长度
创建变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int my_strlen (char * str) { int count = 0 ; while (*str != '\0' ){ count++; str++; } return count; } int main () { char arr[] = {"abc" }; int len = my_strlen(arr); printf ("%d\n" , len); return 0 ; }
递归 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int my_strlen (char * str) { if (*str != '\0' ){ return 1 + my_strlen(str + 1 ); } else return 0 ; } int main () { char arr[] = {"abc" }; int len = my_strlen(arr); printf ("%d" , len); }
3. 递归与迭代 练习 求 n 的阶乘(不考虑溢出)
求 n 的阶乘。(不考虑溢出)
递归 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> int fac (int x) { if (x > 1 ){ return x * fac(x-1 ); } else return 1 ; } int main () { int i = 0 ; scanf ("%d" , &i); int ret = fac(i); printf ("%d" , ret); }
迭代 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int fac (int n) { int i = 0 ; int ret = 1 ; for (i = 1 ; i <= n; i++){ ret *= i; } return ret; } int main () { int n = 0 ; scanf ("%d" , &n); int ret = fac(n); printf ("%d" , ret); return 0 ; }
求第 n 个斐波那契数(不考虑溢出)
递归 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 include <stdio.h> int fib (int x) { if (x > 2 ){ return fib(x-1 )+fib(x-2 ); } else if (x <= 2 ){ return 1 ; } } int main () { int n = 0 ; printf ("请输入你要求的第 n 个斐波那契数:" ); scanf ("%d" , &n); int ret = fib(n); printf ("%d" , ret); return 0 ; }
迭代 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> int fib (int x) { int a = 1 ; int b = 1 ; int c = 0 ; while (x > 2 ){ c = a + b; a = b; b = c; x--; } return 1 ; } int main () { int n = 0 ; printf ("请输入你要求的第 n 个斐波那契数:" ); scanf ("%d" , &n); int ret = fib(n); printf ("%d" , n); return 0 ; }
注:
许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰
但是这些问题的迭代显示往往比递归实现效率更高,虽然代码的可读性稍微差些
当一个问题相当复杂,难以用迭代实现时的简洁性便可以补偿它所带来的运行时开销
I am going to study on 9-16
完结撒花🌸
还在持续学习中ing····
目前进度:
函数_练习
学习来源 :鹏哥C语言
Hello World
1 2 3 4 5 6 #include <stdio.h> int main () { printf ("Hello World!" ); return 0 ; }
内存
栈区
局部变量
函数的形参
每一次函数的调用都会在栈区申请空间
堆区
静态区