文章目录
第1章 回顾
1. 数组
- 本质:相同类型数据的集合,元素地址连续。
- 数组名:代表数组首元素地址(如
int a[5];中,a == &a[0])。 - 多维数组示例:
int a[5]; // 一维整型数组 char a[5][80]; // 二维字符数组(常用于存储多个字符串) float a[3][6][20]; // 三维浮点型数组 a[1][3][10]; // 三维数组元素访问 - 字符数组操作:偏向字符串处理,依赖字符串函数(
strlen、strcmp、strcpy、strcat、gets、puts、memset)。 - 初始化:未显式初始化时,全局/静态数组默认值为0,局部数组为垃圾值(随机值)。
2. 指针
- 核心原则:指针类型与保存的地址类型一致,不一致需强制转换。
- 地址偏移:
指针 ± n = 指针当前地址 + n × 空间大小(空间大小由指针类型决定)。
示例:64位系统中,(short (**))p + 2与(long *)p + 2偏移量均为16字节(short(**)为8字节指针,2×8=16;long*为8字节指针,2×8=16)。 const修饰:常用于系统库函数传参,限制指针操作(如常量指针、指针常量)。
3. typedef(类型重命名)
- 作用:给已有数据类型起别名,简化复杂类型定义。
- 注意点:
typedef修饰的是“类型”,而非变量,需区分变量定义与类型重命名。
| 示例代码 | 含义说明 |
|---|---|
typedef int INT; INT b; | 给int起别名INT,b是int类型变量(占4字节,64位系统) |
typedef int (*p)[30]; p a; | 给“指向30个int的数组指针”起别名p,a的类型是int (*)[30] |
typedef int * INT; INT a, b; | 给int*起别名INT,a和b均为int*类型指针(各占8字节,64位系统) |
int *a, b; | a是int*指针(8字节),b是int变量(4字节)(未用typedef,仅修饰a) |
第2章 必背知识点
本章需熟记内存分区、变量特性、关键字作用。
2.1 C语言5大分区
C程序运行时,内存分为5个独立区域,各区域存储的数据特性不同:
| 分区名称 | 存储内容 | 管理方式 |
|---|---|---|
| 栈区(Stack) | 局部变量、临时变量、函数形参 | 系统自动管理(函数调用时分配,函数结束时释放),空间小、速度快 |
| 堆区(Heap) | 程序员自主申请的内存(如动态数组) | 手动管理(malloc()申请,free()释放),空间大、需避免内存泄漏 |
| 静态全局区 | 全局变量、静态修饰的局部变量(static修饰) | 系统管理(程序启动时分配,程序结束时释放),默认初始值为0 |
| 文字常量区 | 常量(如123、"hello")、const修饰的常量 | 只读区域(不可修改),程序结束时释放 |
| 二进制代码段 | 程序的二进制指令(如函数体代码) | 只读区域,程序加载时分配 |
2.2 局部变量
| 特性 | 说明 |
|---|---|
| 关键字 | 无(无需特殊修饰) |
| 存储位置 | 栈区 |
| 定义位置 | 函数内部(或代码块内部,如if、for语句块) |
| 使用范围 | 仅在定义它的函数/代码块内部有效(超出范围不可访问) |
| 生命周期 | 函数/代码块运行时创建,运行结束后自动释放(值丢失) |
| 初始值 | 未初始化时为垃圾值(随机值,不可直接使用) |
2.3 全局变量
| 特性 | 说明 |
|---|---|
| 关键字 | 无(无需特殊修饰) |
| 存储位置 | 静态全局区 |
| 定义位置 | 函数外部(通常在文件开头) |
| 使用范围 | 整个当前文件(所有函数均可访问),若用extern声明可跨文件访问 |
| 生命周期 | 程序启动时创建,程序结束时释放(全程有效) |
| 初始值 | 未显式初始化时,默认值为0(静态全局区特性) |
2.4 静态局部变量
由static修饰的局部变量,兼具局部变量的“作用范围”和全局变量的“生命周期”:
| 特性 | 说明 |
|---|---|
| 关键字 | static |
| 存储位置 | 静态全局区(而非栈区,区别于普通局部变量) |
| 定义位置 | 函数内部 |
| 使用范围 | 仅在定义它的函数内部有效(与普通局部变量一致) |
| 生命周期 | 函数第一次运行时创建,程序结束时释放(全程有效,值可继承) |
| 初始值 | 未显式初始化时默认值为0;显式初始化时,仅第一次函数调用时初始化 |
示例代码(静态局部变量的值继承):
#include <stdio.h>
void fun(void);
int main() {
for (int i = 0; i < 3; i++) {
fun(); // 调用3次fun,观察a的值变化
}
return 0;
}
void fun(void) {
static int a = 0; // 仅第一次调用时初始化a=0
a++; // 每次调用继承上次的值,依次为1、2、3
printf("a=%d\n", a); // 输出:a=1 → a=2 → a=3
}
2.5 外部声明变量(了解)
由extern修饰,用于“声明”外部文件的全局变量,而非“定义”变量。
| 特性 | 说明 |
|---|---|
| 关键字 | extern |
| 存储位置 | 静态全局区(变量实际定义在外部文件中) |
| 核心作用 | 声明“该变量来自外部文件”,避免重复定义(仅声明,不分配空间) |
| 使用场景 | 多文件编程时,跨文件访问全局变量(需确保外部文件已定义该变量) |
示例(多文件协作):
A.c(定义变量):int a = 5;(分配空间,定义全局变量a)B.c(声明变量):extern int a;(仅声明,不分配空间,可使用A.c中的a)
2.6 关键字
2.6.1 static(静态修饰符)
static是C语言核心关键字,主要有两大作用:
-
修饰局部变量:延长生命周期
- 普通局部变量(栈区):函数结束后值丢失;
- 静态局部变量(静态全局区):函数结束后值保留,下次调用继承。
-
修饰全局变量/函数:私有化(限制作用域)
- 未修饰的全局变量/函数:默认“外部可见”,可通过
extern跨文件访问; - 静态全局变量/函数:仅在当前文件内可见,外部文件无法访问(避免命名冲突)。
- 有些别人封装的函数有时加上staic,如果需要在另外的文件中使用需要删除staic关键字
- 未修饰的全局变量/函数:默认“外部可见”,可通过
2.6.2 extern(外部声明符)
extern仅用于“声明”,不用于“定义”,核心作用:
- 声明外部全局变量:告诉编译器“该变量已在其他文件定义,此处仅使用”。
- 声明外部函数:多文件编程时,声明其他文件定义的函数(若函数定义在当前文件后,也可用于提前声明)。
注意:extern不能初始化变量(如extern int a = 5;错误,会被视为定义,而非声明)。
2.7 局部变量与全局变量能否同名?
可以同名,遵循就近原则:
- 查找变量时,优先在“当前代码层”(如函数内部、
if块)查找; - 若当前层无该变量,则向上一层查找(如函数内部→文件全局)。
示例代码(就近原则验证):
#include <stdio.h>
int a[5] = {6,6,7,7,8}; // 全局变量(最外层)
int main() {
int a[5] = {1,2,3,4,5}; // 局部变量(main函数层,覆盖全局)
if (1) {
int a[5] = {1,1,1,1,1}; // 临时变量(if块层,覆盖main局部)
for (int i=0; i<5; i++) {
printf("%d ", a[i]); // 输出:1 1 1 1 1(优先if块的a)
}
printf("\n");
}
for (int i=0; i<5; i++) {
printf("%d ", a[i]); // 输出:1 2 3 4 5(main函数的a)
}
printf("\n");
return 0;
}
2.8 实参&形参
实参(实际参数)与形参(形式参数)是函数传参的核心概念,二者需满足“三个一致”(类型一致、数量一直、顺序一致)。
| 对比维度 | 实参(实际参数) | 形参(形式参数) |
|---|---|---|
| 定义时机 | 函数调用时传入(如fun(1,2)中的1、2) | 函数定义时声明(如void fun(int a, int b)中的a、b) |
| 本质 | 具体的值或变量(有实际内存与值) | 临时变量(仅在函数内部有效,函数调用时分配空间,结束后释放) |
| 作用 | 向函数传递数据 | 接收实参传递的数据,用于函数内部运算 |
示例代码(实参与形参):
#include <stdio.h>
void fun(int a, int b); // 函数声明(形参a、b在此声明)
int main() {
fun(1, 2); // 函数调用(实参1、2传入)
return 0;
}
// 函数定义(形参a、b在此定义,接收实参值)
void fun(int a, int b) {
int c = 0;
c = a + b;
printf("%d\n", c); // 输出:3(实参1+2的结果)
}
第3章 函数
函数是C语言的“代码封装单元”,用于实现模块化编程,提高代码复用率,减少重复代码。
3.1 函数的作用
- 封装代码:将一个功能(如求和、排序)封装为一个函数,逻辑更清晰。
- 复用代码:同一功能可在多个地方调用,无需重复编写。
- 简化维护:修改功能时,仅需修改函数内部代码,无需修改所有调用处。
- 模块化协作:多文件编程时,可按功能拆分函数,便于团队协作。
3.2 函数分类
按功能与地位,函数分为主函数和子函数。
3.2.1 主函数(main())
- 特殊地位:C程序的唯一入口和出口,程序从
main()开始执行,从main()结束。 - 特性:
- 有且仅有一个(一个程序不能有多个
main()); - 可调用子函数;
- 返回值类型为
int(默认返回0,代表程序正常结束)。
- 有且仅有一个(一个程序不能有多个
- 基本格式:
int main() { // 代码逻辑 return 0; // 程序正常结束,返回0 }
3.2.2 子函数
- 本质:实现特定功能的代码块,被调用时才执行。
- 特性:
- 可多个(根据功能需求定义);
- 子函数之间可互相调用;
- 不能调用主函数(主函数是入口,子函数调用主函数会导致死循环);
- 命名需遵守标识符规则(字母、数字、下划线,首字符非数字)。
3.3 函数命名
遵循“望文生义”原则,名称直接体现函数功能,便于阅读与维护:
- 示例:求和函数命名为
sum,求字符串长度函数命名为str_len。 - 辅助:复杂函数需加注释,说明功能、参数含义、返回值含义。
3.4 函数定义格式
函数定义需明确“返回值类型”“函数名”“形参”和“函数体”,基本格式:
函数类型 函数名(形参类型1 形参1, 形参类型2 形参2, ...) {
// 函数体(封装的代码逻辑)
[return 返回值;] // 若函数类型非void,需返回对应类型的值
}
- 函数类型:与返回值类型一致(如返回
int则类型为int,无返回值则为void)。 - 形参列表:无参数时,可写
void或空(如void fun(void)或void fun())。
3.5 函数传参
函数传参是“实参向形参传递数据”的过程,分为值传参和地址传参,核心区别是“是否影响实参的值”。
3.5.1 值传参
- 原理:实参将“值的副本”传递给形参,形参是独立的临时变量。
- 特点:修改形参的值,不会影响实参(形参与实参内存地址不同)。如下代码fun1里再怎么增加b的值a还是5不变。
- 示例代码:
#include <stdio.h> void fun1(int b); // 值传参函数声明 int main() { int a = 5; fun1(a); // 实参a的值(5)传递给形参b printf("a=%d\n", a); // 输出:a=5(形参b修改不影响a) return 0; } void fun1(int b) { b++; // 修改形参b(b从5变为6) printf("b=%d\n", b); // 输出:b=6 }
3.5.2 地址传参
- 原理:实参将“变量的地址”传递给形参(形参为指针类型),形参通过地址间接操作实参内存。
- 特点:修改形参指向的内存值,会直接影响实参(形参与实参指向同一内存)。
- 示例代码:
#include <stdio.h> void fun2(int *p); // 地址传参函数声明(形参为int*指针) int main() { int a = 5; fun2(&a); // 实参为a的地址(&a),传递给指针p printf("a=%d\n", a); // 输出:a=6(形参p通过地址修改了a) return 0; } void fun2(int *p) { ++(*p); // 通过指针p间接修改a的值(a从5变为6) printf("fun2=%d\n", *p); // 输出:fun2=6 }
3.6 函数类型
函数类型由“返回值类型”决定,不同类型对应不同用法,核心规则:
- 返回值唯一:一个函数只能返回一个值(若需返回多个值,需用地址传参或结构体,不需要一定有return,把多个值写入一个结构体或者数组,将结构体或数组的地址传入,直接就能修改其中的值)。
- return作用:执行
return后,函数立即结束,后续代码不再执行。
常见函数类型示例:
| 函数类型 | 含义 | 示例代码 |
|---|---|---|
void | 无返回值 | void fun(void) { printf("Hello"); }(无需return,或return;) |
int | 返回整型值 | int sum(int a, int b) { return a + b; }(返回a+b的结果) |
float | 返回浮点型值 | float avg(int a, int b) { return (a + b)/2.0; }(返回平均值) |
char* | 返回字符指针(地址) | char* get_str() { return "hello"; }(返回字符串首地址) |
示例(有返回值函数的使用):
#include <stdio.h>
int fun(void); // 函数声明(返回int类型)
int main() {
int ret = fun(); // 接收函数返回值
printf("%d\n", ret); // 输出:6(fun返回a=6)
return 0;
}
int fun(void) {
int a = 5;
a++; // a从5变为6
return a; // 返回a的值(6)
}
3.7 函数的声明
函数声明的作用是“告诉编译器函数的存在”,避免“未定义函数”错误,适用场景:
- 函数定义在
main()之后; - 函数定义在其他文件中。
声明格式
函数定义的“第一行”末尾加;,即:
函数类型 函数名(形参类型1 形参1, 形参类型2 形参2, ...);
- 简化写法:形参名可省略,仅保留类型(如
void fun(int, int);)。
示例代码
#include <stdio.h>
// 函数声明(fun定义在main之后,需提前声明)
void fun(int a, int b);
int main() {
fun(1, 2); // 调用fun,编译器已知fun存在
return 0;
}
// 函数定义(在main之后)
void fun(int a, int b) {
printf("%d\n", a + b); // 输出:3
}
3.8 函数调用
函数调用是“执行函数代码”的过程,格式与注意事项:
- 调用格式:
函数名(实参1, 实参2, ...);(无参数则写函数名();)。 - 核心要求:实参与形参需满足“三一致”(类型、数量、顺序)。
- 返回值处理:
- 有返回值函数:可将返回值赋值给变量(如
int ret = sum(1,2);),或直接使用(如printf("%d", sum(1,2));)。 - 无返回值函数(
void):直接调用(如fun();),无需处理返回值。
- 有返回值函数:可将返回值赋值给变量(如
3.9 递归函数
递归函数是“自己调用自己”的函数,需满足“终止条件”(避免死循环),核心思想是“分治”(将大问题拆为小问题)。
3.9.1 固定模板
递归函数必须包含两大要素:递归终止条件(停止调用自己)和递归调用语句(调用自己,缩小问题规模):
函数类型 递归函数名(参数) {
// 1. 递归终止条件(必须有,否则死循环)
if (终止条件) {
return 基础值; // 问题最小化时的结果
}
// 2. 递归调用(调用自己,参数需向终止条件靠近)
return 递归表达式;
}
3.9.2 递归示例代码
-
递归求和:计算1+2+3+…+n
思路:sum(n) = n + sum(n-1),终止条件n=1(sum(1)=1)。#include <stdio.h> int sum(int n); int main() { printf("%d\n", sum(5)); // 输出:15(1+2+3+4+5) return 0; } int sum(int n) { if (n == 1) { // 终止条件:n=1时返回1 return 1; } return n + sum(n-1); // 递归调用:sum(5)=5+sum(4),直至sum(1) } -
递归转二进制:输入十进制数,输出对应的二进制(逆序输出,需再处理)
思路:十进制数 % 2取余数(二进制位),十进制数 / 2缩小规模,终止条件十进制数=0。#include <stdio.h> void dec_to_bin(int n); int main() { dec_to_bin(10); // 输出:1010,即10的二进制 return 0; } void dec_to_bin(int n) { if (n == 0) { // 终止条件:n=0时停止 return; } dec_to_bin(n / 2); // 递归调用,先处理高位 printf("%d", n % 2); // 输出余数(二进制位) } -
练习:计算1-2+3-4+5-…±n(提示:终止条件
n=1,递归表达式n * (n%2==1?1:-1) + fun(n-1))。
3.10 指针函数&函数指针
二者是“指针与函数”的结合,核心区别是“本质是函数还是指针”。
1. 指针函数(返回值为指针的函数)
- 本质:函数,其返回值是一个地址(指针)。
- 定义格式:
返回值类型 * 函数名(形参列表) { ... }。 - 示例:返回两个数中较大值的地址
#include <stdio.h> int *get_max(int *a, int *b); // 指针函数声明 int main() { int x=3, y=5; int *p = get_max(&x, &y); // 接收返回的指针(指向y) printf("%d\n", *p); // 输出:5 return 0; } int *get_max(int *a, int *b) { return (*a > *b) ? a : b; // 返回较大值的地址 }
2. 函数指针(指向函数的指针)
- 本质:指针,其指向的内存是函数的入口地址。
- 定义格式:
返回值类型 (*指针名)(形参类型列表);(()不可省略,否则变为指针函数)。 - 核心用法:通过函数指针调用函数(与直接调用函数效果一致)。
示例代码:
#include <stdio.h>
void fun(void); // 函数声明
int main() {
void (*p)(void) = fun; // 定义函数指针p,指向fun
fun(); // 直接调用fun
p(); // 通过函数指针p调用fun(与fun()等价)
return 0;
}
void fun(void) {
printf("Hello Function Pointer!\n"); // 输出:Hello Function Pointer!
}
3. 函数指针数组(存储函数指针的数组)
- 定义格式:
返回值类型 (*数组名[数组大小])(形参类型列表); - 用途:批量管理同类型函数(如多个同参数、同返回值的函数)。
示例代码:
#include <stdio.h>
// 两个同类型函数(int返回值,两个int参数)
int fun1(int a, int b) { return a + b; }
int fun2(int n, int m) { return n * m; }
int main() {
// 定义函数指针数组p,存储fun1和fun2的地址
int (*p[2])(int, int) = {fun1, fun2};
printf("%d\n", p[0](5,6)); // 调用fun1(5,6),输出:11
printf("%d\n", p[1](1,2)); // 调用fun2(1,2),输出:2
return 0;
}
3.11 函数传参传递数组的3种形式(重点)
数组传参的本质是“传递数组首元素地址”(而非整个数组),因此形参有3种等价写法,均为指针类型。
假设有字符数组char a[50] = "hello";,函数传参的3种形式如下:
| 形参形式 | 函数声明示例 | 说明 |
|---|---|---|
指针形式(char *p) | void fun(char *p); | 最直观,p接收数组首元素地址(p == a),通过p[i]或*(p+i)访问元素 |
数组形式(char p[]) | void fun(char p[]); | 语法糖,编译器会将p[]解析为char *p,与指针形式完全等价 |
定长数组形式(char p[50]) | void fun(char p[50]); | 定长无意义,编译器仍解析为char *p,数组长度不影响传参 |
示例代码(3种形式等价验证):
#include <stdio.h>
// 3种形参形式的函数声明(等价)
void fun1(char *p);
void fun2(char p[]);
void fun3(char p[50]);
int main() {
char a[50] = "hello";
fun1(a); // 传递数组首地址
fun2(a);
fun3(a);
return 0;
}
// 指针形式:通过*(p+i)访问元素
void fun1(char *p) {
for (int i=0; p[i]!='\0'; i++) {
printf("%c", *(p+i)); // 输出:hello
}
printf("\n");
}
// 数组形式:通过p[i]访问元素
void fun2(char p[]) {
printf("%s\n", p); // 输出:hello(p是数组首地址,%s打印字符串)
}
// 定长数组形式:与前两种等价
void fun3(char p[50]) {
printf("%s\n", p); // 输出:hello
}
3.12 函数示例代码
以下示例覆盖“无参无返回值”“有参有返回值”“地址传参”“全局变量传参”等场景,均实现“两数求和”功能。
示例1:无参无返回值(函数内部输入输出)
#include <stdio.h>
void fun(void); // 函数声明
int main() {
fun(); // 调用函数
return 0;
}
void fun(void) {
int a=0, b=0, c=0;
scanf("%d%d", &a, &b); // 函数内部输入
c = a + b;
printf("%d\n", c); // 函数内部输出
}
示例2:有参无返回值(主函数输入,函数输出)
#include <stdio.h>
void fun(int a, int b); // 函数声明
int main() {
int a=0, b=0;
scanf("%d%d", &a, &b); // 主函数输入
fun(a, b); // 传递实参
return 0;
}
void fun(int a, int b) {
int c=0;
c = a + b;
printf("%d\n", c); // 函数输出
}
示例3:有参有返回值(主函数输入+接收结果+输出)
#include <stdio.h>
int fun(int a, int b); // 函数声明(返回int类型)
int main() {
int a=0, b=0, ret=0;
scanf("%d%d", &a, &b); // 主函数输入
ret = fun(a, b); // 接收函数返回值
printf("%d\n", ret); // 主函数输出
return 0;
}
int fun(int a, int b) {
int c=0;
c = a + b;
return c; // 返回求和结果
}
示例4:地址传参(函数内部修改主函数变量)
#include <stdio.h>
void fun(int *a, int *b); // 形参为指针
int main() {
int a=0, b=0;
scanf("%d%d", &a, &b);
fun(&a, &b); // 传递变量地址
return 0;
}
void fun(int *a, int *b) {
int c=0;
c = *a + *b; // 通过指针访问主函数变量
printf("%d\n", c); // 输出结果
}
示例5:全局变量传参(函数直接访问全局变量)
#include <stdio.h>
void fun(void);
int a=0, b=0, c=0; // 全局变量(函数可直接访问)
int main() {
scanf("%d%d", &a, &b); // 主函数给全局变量赋值
fun(); // 函数直接使用全局变量
return 0;
}
void fun(void) {
c = a + b; // 直接访问全局变量a、b
printf("%d\n", c);
}
第4章 回顾函数
本章汇总函数相关核心知识点,形成知识体系,重点掌握“内存分区”“变量特性”“关键字作用”“函数传参”四大模块。
4.1 核心知识点汇总
1. 五大分区(再梳理)
| 分区 | 关键存储内容 | 核心特性 |
|---|---|---|
| 栈区 | 局部变量、形参、临时变量 | 自动分配释放,生命周期短(随函数) |
| 堆区 | 动态内存(malloc申请) | 手动管理,生命周期长(需free释放) |
| 静态全局区 | 全局变量、静态局部变量(static) | 自动分配释放,生命周期长(随程序),默认值为0 |
| 文字常量区 | 常量、const变量 | 只读,不可修改 |
| 二进制代码段 | 函数体代码 | 只读,程序加载时分配 |
2. 局部变量与全局变量对比(表格版)
| 对比维度 | 局部变量 | 全局变量 |
|---|---|---|
| 存储位置 | 栈区 | 静态全局区 |
| 作用范围 | 函数/代码块内部 | 当前文件(全局可见) |
| 生命周期 | 随函数/代码块(创建→结束) | 随程序(启动→结束) |
| 初始值 | 垃圾值 | 默认0 |
| 关键字 | 无 | 无(加static变为静态全局变量) |
3. 关键字作用(重点)
| 关键字 | 作用1(修饰局部变量) | 作用2(修饰全局变量/函数) | 作用3(其他) |
|---|---|---|---|
static | 延长生命周期(静态全局区存储) | 私有化(仅当前文件可见) | - |
extern | - | 声明外部变量/函数(跨文件访问) | 避免重复定义 |
typedef | - | - | 重命名数据类型(简化复杂类型) |
4. 实参与形参对比
| 维度 | 实参 | 形参 |
|---|---|---|
| 定义时机 | 函数调用时 | 函数定义时 |
| 本质 | 具体值/变量(有实际内存) | 临时变量(仅函数内部有效) |
| 数据传递 | 传递值/地址给形参 | 接收实参数据 |
| 影响范围 | 地址传参时可能被形参修改,值传参不会 | 仅影响自身,不影响实参(值传参) |
4.2 字符串函数重写示例(实践巩固)
通过重写标准字符串函数,加深“指针操作”“函数传参”的理解。
1. 重写strlen(计算字符串长度)
功能:计算字符串有效长度(不含结束符\0)。
#include <stdio.h>
int strlen_t(char *p); // 自定义strlen函数
int main() {
char a[500] = {0};
gets(a); // 输入字符串
int len = strlen_t(a); // 调用自定义函数
printf("%d\n", len); // 输出长度
return 0;
}
int strlen_t(char *p) {
int i = 0;
while (*p) { // 循环至'\0'结束(*p为'\0'时循环终止)
p++; // 指针后移(访问下一个字符)
i++; // 计数+1
}
return i; // 返回长度
}
2. 重写strcmp(比较两个字符串)
功能:比较两个字符串,返回差值(相等返回0,s1>s2返回正数,s1<s2返回负数)。
#include <stdio.h>
int strcmp_t(char *s1, char *s2); // 自定义strcmp函数
int main() {
char a[500] = {0}, b[500] = {0};
int ret = 0;
gets(a);
gets(b);
ret = strcmp_t(a, b); // 比较两个字符串
if (ret == 0) {
printf("相同\n");
} else {
printf("不同\n");
}
return 0;
}
int strcmp_t(char *s1, char *s2) {
// 循环条件:两字符相等且未到字符串结束(*s1!='\0')
while (!(*s1 - *s2) && (*s1)) {
s1++; // 指针后移
s2++;
}
return *s1 - *s2; // 返回最后一个字符的差值
}
3. 重写strcpy(字符串复制)
功能:将s2指向的字符串复制到s1指向的空间(含\0),返回s1首地址。
#include <stdio.h>
char *strcpy_t(char *s1, char *s2); // 指针函数(返回s1地址)
int main() {
char a[500] = {0}, b[500] = {0};
gets(b); // 输入源字符串
char *p = strcpy_t(a, b); // 复制s2到s1,接收返回地址
printf("%s\n", p); // 输出复制结果
return 0;
}
char *strcpy_t(char *s1, char *s2) {
char *tmp = s1; // 保存s1首地址(避免后续指针后移丢失)
// 循环条件:*s2!='\0'(复制至结束符),赋值后指针后移
while (*s1 = *s2) {
s1++;
s2++;
}
return tmp; // 返回s1首地址
}
4. 重写strcat(字符串拼接)
功能:将s2指向的字符串追加到s1指向的字符串末尾(覆盖s1的\0,再添加s2的\0),返回s1首地址。
#include <stdio.h>
char *strcat_t(char *s1, char *s2); // 指针函数
int main() {
char a[500] = {0}, b[500] = {0};
gets(a); // 输入目标字符串
gets(b); // 输入源字符串
char *p = strcat_t(a, b); // 拼接字符串
printf("%s\n", p); // 输出结果
return 0;
}
char *strcat_t(char *s1, char *s2) {
char *tmp = s1; // 保存s1首地址
// 第一步:将s1指针移至末尾(*s1!='\0')
while (*s1) {
s1++;
}
// 第二步:将s2复制到s1末尾(含'\0')
while (*s1 = *s2) {
s1++;
s2++;
}
return tmp; // 返回s1首地址
}
265

被折叠的 条评论
为什么被折叠?



