int strcmp(const char *str1, const char *str2);
将函数调用看成“str1 - str2”,返回值决定两字符串的大小及是否相等关系:
str1 < str2
str1 = str2
,即两个字符串内容一致str1 > str2
注意:返回值只关注符号性和是否为 0,不关注具体取值。strcmp 函数的返回值实际只有 0、-1、1,用于表示两字符串的大小关系,只要知道大于 0、小于 0 以及等于 0 即可,具体值不重要。
int my_strcmp(const char *s1, const char *s2) {
while (*s1 && *s2 && *s1 == *s2) { // 当两个字符都不为空且相等时,移动指针继续比较下一对元素
s1++;
s2++;
}
// 不管是哪种情况(找到不相等字符或其中一个指针指向空字符),返回它们的编码值差就是结果
return *s1 - *s2;
}
char类型的二维数组
,本质是连续内存存储的字符矩阵。char week_days[][10] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
'\0'
结尾。week_days[0][0] = 'm'
),但数组名不可整体赋值。优点 | 缺点 |
---|---|
1. 实现简单直接,逻辑易懂 2. 连续内存访问效率高 |
1. 列长需固定为最长字符串长度,空间浪费 2. 排序、删除等操作繁琐,适合只读场景 |
char*类型的一维数组
,每个元素是字符串首元素指针,指向独立的字符数组(可位于栈、堆或只读数据段)。char *week_days2[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
week_days2[2][0] = 'w'
会报错),但指针指向可修改(如week_days2[0] = "星期一"
)。优点 | 缺点 |
---|---|
1. 无空间浪费,按需分配 2. 操作灵活,排序、删除等通过指针操作实现 |
1. 内存不连续,访问性能略低 2. 需理解指针指向规则,操作复杂度较高 |
维度 | 二维字符数组 | 字符指针数组 |
---|---|---|
内存连续性 | 字符串连续存储 | 字符串存储不连续,指针数组连续 |
空间效率 | 固定列长,可能浪费空间 | 按需分配,无浪费 |
操作灵活性 | 仅支持简单访问,修改繁琐 | 支持指针操作,适合频繁修改场景 |
适用场景 | 固定只读字符串集合(如星期列表) | 动态修改场景(如命令行参数处理) |
典型示例 | char days[][10] = {...} |
char *args[] = {"a", "b", "c"} |
main函数
的参数,以空格分割的字符串数组。int main(int argc, char *argv[]); // 常用声明
// argc:参数个数(至少为1),argv:字符串数组(首个元素为程序路径)
int main(int argc, char *argv[]) {
printf("参数个数:%d\n", argc);
for (int i = 0; i < argc; i++) {
printf("参数%d:%s\n", i, argv[i]);
}
// 将第二个参数转为整数
int num;
sscanf(argv[1], "%d", &num); // 或使用atoi(argv[1])
printf("转换后的整数:%d\n", num);
return 0;
}
argc ≥ 1
,第一个参数argv[0]
为程序路径(如./a.out
)。atoi
、sscanf
)。cp source.txt dest.txt
中source.txt
和dest.txt
为参数)。// 栈上可修改的字符串
char name1[] = "Faker"; // 栈上数组,内容可修改
char name2[] = "Uzi";
char *names[] = {name1, name2}; // 指针指向栈上数组
names[1][0] = 'u'; // 合法,修改栈上字符串内容
// names[0] = "Bin"; // 合法,但"Bin"是字面值,指向后内容不可修改
names[0] = "Bin"
),内容不可修改;指向栈/堆区时,内容可修改。malloc
分配后释放)。argc
和argv
获取,首个参数为程序路径,需手动转换数据类型。typedef struct 结构体本名 {
成员类型1 成员名1;
成员类型2 成员名2;
// ...
} 结构体别名;
示例:
typedef struct student {
int stu_id; // 学号
char name[25]; // 姓名(字符数组)
char gender; // 性别
int score; // 成绩
} Student; // 别名Student
struct
:struct student stu;
。struct
:Student stu;
(推荐使用别名)。typedef struct node {
int data;
struct node *next; // 必须用本名struct node
} Node;
struct A { struct A a; };
(非法)。场景 | 语法 | 示例 |
---|---|---|
仅用本名声明 | struct 结构体本名 对象名; |
struct student stu; |
用别名声明 | 结构体别名 对象名; |
Student stu; |
定义时初始化 | 结构体别名 对象名 = {值列表}; |
Student s = {1, "Faker", 'm', 100}; |
{}
按顺序初始化成员,未赋值成员默认零值。"Faker"
是{'F','a'...'\0'}
的简写)。Student s2 = {0};
(所有成员初始化为0或'\0'
)。对象类型 | 运算符 | 语法 | 示例 |
---|---|---|---|
结构体对象 | . |
对象名.成员名 |
s.name = "Uzi"; |
结构体指针 | -> |
指针名->成员名 |
p->score = 90; |
指针->成员
等价于 (*指针).成员
,因.
优先级高于*
,需显式加括号:
(*p).score = 90; // 与 p->score = 90; 等价
int
值传递)。void swap_struct(Student s1, Student s2) { // 值传递
Student temp = s1;
s1 = s2;
s2 = temp; // 仅修改副本,原对象不变
}
void swap_struct_ptr(Student *p1, Student *p2) { // 指针传递
Student temp = *p1;
*p1 = *p2;
*p2 = temp; // 修改原始对象
}
int
返回,不涉及原内存)。Student create_stu() {
return (Student){2, "Clearlove", 'm', 85}; // 返回副本
}
Student* create_stu_static() { // 合法(静态区)
static Student s = {3, "Bin", 'm', 95};
return &s;
}
特性 | 结构体 | 数组 |
---|---|---|
初始化方式 | 用{} 按成员赋值 |
用{} 按元素赋值 |
对象赋值 | 支持= (成员逐一拷贝) |
禁止= (需逐个元素复制) |
作为函数参数 | 值传递(副本)或指针传递 | 隐式转换为指针(传递首地址) |
返回值支持 | 允许(返回副本或指针) | 禁止(只能返回指针) |
内存连续性 | 成员在内存中连续存储 | 元素在内存中连续存储 |
typedef enum 枚举本名 {
枚举值1, // 默认为0
枚举值2, // 默认为1
枚举值3 = 5 // 显式赋值为5,后续值依次+1
} 枚举别名;
示例:
typedef enum color {
RED, // 0
GREEN, // 1
BLUE = 3 // 3,下一个枚举值YELLOW为4
} Color;
int
类型,可直接使用整数赋值或传参。Color c = GREEN; // 等价于 c = 1
func(2); // 合法,传入枚举值BLUE的底层值
int
可隐式转换,缺乏严格类型检查。RED=0
, GREEN=1
)。BLUE=3
,则YELLOW=4
)。RED
代替0
),提高代码可读性。switch
分支条件等场景:int arr[RED + 1]; // 合法,RED是编译时常量
switch (c) {
case RED: ... // 合法
}
维度 | 结构体 | 枚举 |
---|---|---|
本质 | 自定义复合数据类型(含多成员) | 给整数取别名的聚合类型 |
内存占用 | 各成员内存之和 | 等于底层整数类型大小(通常4字节) |
类型安全 | 强类型(需通过成员访问) | 弱类型(与int可隐式转换) |
典型用途 | 存储复杂数据(如学生信息) | 定义状态常量(如颜色、错误码) |
typedef
定义别名,简化对象声明。switch
分支或数组维度定义。static
修饰的局部变量、字符串字面值。malloc/calloc/realloc
申请的内存。free
释放,否则程序结束时由系统回收。特性 | 栈内存 | 堆内存 |
---|---|---|
管理方式 | 自动(栈指针移动) | 手动(调用分配/释放函数) |
空间大小 | 小(通常几KB到MB级) | 大(受限于系统内存) |
动态性 | 编译时确定大小 | 运行时动态分配 |
访问速度 | 快(寄存器直接操作) | 慢(涉及系统调用和碎片化管理) |
典型用途 | 局部变量、函数调用栈帧 | 大数据结构、动态数组、跨函数数据 |
void*
int*
、char*
),无需显式转换(C语言允许隐式转换,C++需强转)。int num = 100;
void *p = #
int val = *(int*)p; // 强转后解引用
malloc
返回值)。NULL
本质是(void*)0
,表示无效地址。malloc
函数void* malloc(size_t size); // 分配连续的`size`字节内存块
size
为所需字节数,需手动计算(如malloc(sizeof(int)*n)
)。void*
),失败返回NULL
。0xCD
)。NULL
指针:int *p = (int*)malloc(100);
if (p == NULL) {
perror("malloc failed");
exit(1);
}
calloc
函数void* calloc(size_t num, size_t size); // 分配`num`个元素,每个`size`字节的内存块
malloc
的随机值)。num*size
,适合初始化数组:int *arr = (int*)calloc(5, sizeof(int)); // 等价于malloc(20)并清零
realloc
函数void* realloc(void* ptr, size_t new_size); // 调整已分配内存块的大小
NULL
且原内存块保持不变。int *arr = (int*)malloc(5*sizeof(int));
arr = (int*)realloc(arr, 10*sizeof(int)); // 扩展至10个元素
free
函数void free(void *ptr); // 释放动态分配的内存块
free
中途移动的指针)。NULL
,避免二次释放或访问悬空指针:int *p = (int*)malloc(4);
free(p);
p = NULL; // 防止野指针
free
同一指针会导致程序崩溃(double free
错误)。arr[10] = 100
,但arr
仅分配5个元素)。malloc(n)
实际需要n+1
字节)。finally
风格的代码结构)。free
后立即将指针置为NULL
。NULL
(如if (p != NULL) { ... }
)。realloc
扩展数组时,先保存原指针,避免分配失败导致数据丢失:int *old_ptr = arr;
arr = (int*)realloc(arr, new_size);
if (arr == NULL) { // 分配失败,恢复原指针
arr = old_ptr;
perror("realloc failed");
}
0xCD/0xDD
标记)或工具(如Valgrind)检测内存问题。malloc
分配的内存填充为0xCD
(表示“未初始化”)。free
后的内存填充为0xDD
(表示“已释放”)。0xCC
(防止使用随机值)。malloc/calloc/realloc
)。void*
需强转后解引用。NULL
。if (ptr == NULL)
)。malloc
对应一个free
)。calloc
(自动清零),小块内存使用malloc
(轻量高效)。