C语言内存管理
1. 静态开辟
-
定义:编译时分配固定大小的内存,生命周期与程序一致。
-
特点: 在栈或全局区分配(如全局变量、局部数组)。 大小不可变,自动释放(栈变量在函数结束时释放)。
-
示例:
int arr[100]; // 全局区(静态内存) void func() { char str[50]; // 栈区(函数结束时自动释放) }
2. 动态开辟
-
定义:运行时通过函数手动分配堆内存,需显式释放。
-
核心函数:
malloc(size)
:分配未初始化的内存。calloc(num, size)
:分配并初始化为0。free(ptr)
:释放内存。 -
示例:
int *p = (int*)malloc(10 * sizeof(int)); // 分配10个int的空间 if (p == NULL) { /* 处理失败 */ } free(p); // 释放
3. 动态开辟的使用场景
- 适用情况: 未知数据大小:如运行时输入的数组长度。 大内存需求:栈空间不足时(如大型矩阵)。 灵活生命周期:需跨函数长期存在的数据。 数据结构:链表、树等动态结构。
- 反面案例: 小规模、生命周期短的变量应优先用栈内存。
4. 动态开辟之realloc
-
作用:调整已分配内存块的大小(扩大或缩小)。
-
原型:
void *realloc(void *ptr, size_t new_size);
-
行为: 成功:返回新指针(可能原地扩展或重新分配)。 失败:返回
NULL
,原指针仍有效。 -
示例:
int *p = (int*)malloc(5 * sizeof(int)); p = (int*)realloc(p, 10 * sizeof(int)); // 扩容到10个int if (p == NULL) { /* 处理失败 */ } free(p);
-
注意事项: 若
realloc
失败,需保留原指针避免内存泄漏。 缩小时可能原地调整,无需移动数据。
小结
特性 | 静态开辟 | 动态开辟 |
---|---|---|
分配时机 | 编译时 | 运行时 |
内存区域 | 栈/全局区 | 堆 |
大小 | 固定 | 可调整(realloc ) |
释放方式 | 自动(栈)/程序结束(全局) | 手动(free ) |
适用场景 | 已知大小、短生命周期 | 未知大小、长生命周期 |
C语言字符串
以下是C语言中关于字符串操作的知识点总结:
2. 字符串的两种形式
-
字符数组(可修改):
char str1[] = "Hello"; // 栈区,可修改 str1[0] = 'h'; // 合法
-
字符指针(常量字符串,不可修改):
char *str2 = "World"; // 常量区,不可修改 // str2[0] = 'w'; // 非法操作,会导致段错误
3. 指针挪动获取字符串信息(手写API)
通过指针遍历字符串:
// 示例:手写strlen函数(求字符串长度)
int my_strlen(const char *str) {
int len = 0;
while (*str != '\0') { // 指针遍历到'\0'结束
len++;
str++; // 指针后移
}
return len;
}
// 调用
char *s = "Hello";
printf("Length: %d\n", my_strlen(s)); // 输出5
4. 字符串的比较
-
标准库函数:
strcmp(str1, str2)
返回值:0
:字符串相等。>0
:str1
大于str2
(按ASCII码)。<0
:str1
小于str2
。
-
手写实现:
int my_strcmp(const char *s1, const char *s2) { while (*s1 == *s2) { if (*s1 == '\0') return 0; // 同时结束 s1++; s2++; } return *s1 - *s2; // 返回差值 }
5. 字符串查找、包含与拼接
- 查找字符:
strchr(str, ch)
返回首次出现字符ch
的指针,未找到返回NULL
。 - 查找子串:
strstr(str, substr)
返回首次出现子串的位置。 - 字符串拼接:
strcat(dest, src)
将src
拼接到dest
末尾(需确保dest
空间足够)。
-
手写拼接函数:
void my_strcat(char *dest, const char *src) { while (*dest != '\0') dest++; // 找到dest结尾 while (*src != '\0') { *dest = *src; // 逐个复制 dest++; src++; } *dest = '\0'; // 添加结束符 }
6. 大小写转换(手写API)
-
库函数:
toupper(ch)
和tolower(ch)
(需包含<ctype.h>
)。 -
手写实现:
// 转大写 void to_upper(char *str) { while (*str != '\0') { if (*str >= 'a' && *str <= 'z') { *str -= 32; // ASCII码差值 } str++; } } // 转小写 void to_lower(char *str) { while (*str != '\0') { if (*str >= 'A' && *str <= 'Z') { *str += 32; } str++; } } // 调用 char s[] = "Hello"; to_upper(s); // "HELLO"
小结
- 字符串形式:字符数组(可修改) vs 字符指针(常量)。
- 指针操作:通过指针挪动遍历字符串(如手写
strlen
)。 - 比较:
strcmp
或手写实现,注意返回值含义。 - 查找与拼接:
strchr
、strstr
、strcat
的用法及手写逻辑。 - 大小写转换:基于ASCII码的差值计算(
A=65
,a=97
)。
C语言中关于结构体、枚举等
2. 结构体定义与使用
-
定义:结构体是用户自定义的复合数据类型,用于将不同类型的数据组合在一起。
struct Student { int id; char name[20]; float score; };
-
声明变量:
struct Student stu1; // 直接声明 struct Student stu2 = {1001, "Alice", 90.5}; // 初始化
-
访问成员:使用
.
运算符。stu1.id = 1002; printf("Name: %s\n", stu2.name);
3. 结构体指针与动态内存开辟
-
结构体指针:指向结构体变量的指针。
struct Student *pStu = &stu1; pStu->id = 1003; // 通过指针访问成员(等价于 (*pStu).id)
-
动态内存开辟:使用
malloc
或calloc
动态分配内存。struct Student *p = (struct Student*)malloc(sizeof(struct Student)); p->score = 85.0; free(p); // 释放内存
4. 结构体数组
-
定义和初始化:
struct Student class[3] = { {1001, "Bob", 78.5}, {1002, "Tom", 92.0}, {1003, "Lily", 88.5} };
-
访问元素:
printf("%s's score: %.1f\n", class[1].name, class[1].score);
5. 结构体与结构体指针取别名
-
使用
typedef
简化类型名:typedef struct Student { int id; char name[20]; } Student; // 结构体别名 Student stu; // 直接使用别名声明变量
-
结构体指针别名:
typedef struct Student* PStudent; // 指针别名 PStudent p = &stu;
6. 枚举(enum)
-
定义:枚举是一种用于定义命名常量的数据类型。
enum Weekday {Mon=1, Tue, Wed, Thu, Fri, Sat, Sun};
-
使用:
enum Weekday today = Wed; if (today == Wed) printf("Today is Wednesday!\n");
-
特点: 枚举常量默认从
0
开始递增,可显式赋值(如Mon=1
)。 常用于提高代码可读性(如状态码、选项等)。
小结
- 结构体:组合异构数据,通过
.
或->
访问成员。 - 动态内存:需手动管理(
malloc
/free
),指针操作高效但需防内存泄漏。 - 结构体数组:连续存储,适合批量处理数据。
- 别名:
typedef
简化复杂类型声明。 - 枚举:增强可读性,限制变量取值范围。
C语言文件操作及加密解密
1. 文件操作基础
- 文件指针:
FILE *
类型,用于操作文件。 - 打开模式:
"r"
:只读(文件必须存在)。"w"
:只写(覆盖创建)。"a"
:追加(文件不存在则创建)。"rb"
/"wb"
:二进制模式。
2. 文件的读
-
核心函数:
fopen()
:打开文件。fgetc()
:逐字符读取。fgets()
:逐行读取。fread()
:二进制读取。fclose()
:关闭文件。 -
示例:
FILE *fp = fopen("test.txt", "r"); if (fp == NULL) { perror("Error"); return; } char buffer[100]; while (fgets(buffer, 100, fp) != NULL) { // 逐行读取 printf("%s", buffer); } fclose(fp);
3. 文件的写
-
核心函数:
fputc()
:逐字符写入。fputs()
:写入字符串。fwrite()
:二进制写入。 -
示例:
FILE *fp = fopen("output.txt", "w"); if (fp == NULL) { perror("Error"); return; } fputs("Hello, World!\n", fp); // 写入字符串 fclose(fp);
4. 文件复制
-
逐字节复制:
FILE *src = fopen("source.txt", "rb"); FILE *dest = fopen("dest.txt", "wb"); if (!src || !dest) { perror("Error"); return; } char ch; while ((ch = fgetc(src)) != EOF) { // 逐字节复制 fputc(ch, dest); } fclose(src); fclose(dest);
5. 获取文件大小
-
方法1:
fseek
+ftell
:FILE *fp = fopen("test.txt", "rb"); if (fp == NULL) { perror("Error"); return; } fseek(fp, 0, SEEK_END); // 移动到文件末尾 long size = ftell(fp); // 获取当前位置(即大小) fclose(fp); printf("Size: %ld bytes\n", size);
6. 文件加密与解密
-
简单异或加密:
void encrypt_file(const char *filename, const char *key) { FILE *fp = fopen(filename, "rb+"); if (!fp) { perror("Error"); return; } int key_len = strlen(key); int i = 0; char ch; while ((ch = fgetc(fp)) != EOF) { ch ^= key[i % key_len]; // 异或加密 fseek(fp, -1, SEEK_CUR); fputc(ch, fp); i++; } fclose(fp); } // 调用:encrypt_file("data.txt", "secret");
-
解密:相同操作(异或的自反性)。
7. 字符串密码加密与解密
-
基于ASCII的加密:
void encrypt_str(char *str, const char *key) { int key_len = strlen(key); for (int i = 0; str[i] != '\0'; i++) { str[i] += key[i % key_len]; // 简单位移加密 } } void decrypt_str(char *str, const char *key) { int key_len = strlen(key); for (int i = 0; str[i] != '\0'; i++) { str[i] -= key[i % key_len]; // 反向解密 } } // 调用示例 char password[] = "mypassword123"; encrypt_str(password, "key"); printf("Encrypted: %s\n", password); decrypt_str(password, "key"); printf("Decrypted: %s\n", password);
小结
操作 | 关键函数/方法 | 注意事项 |
---|---|---|
文件读 | fgets , fread |
检查文件是否打开成功 |
文件写 | fputs , fwrite |
模式"w" 会覆盖原内容 |
文件复制 | 逐字节或fread /fwrite 批量操作 |
二进制模式避免文本换行符问题 |
文件大小 | fseek(fp, 0, SEEK_END) + ftell |
适用于二进制文件 |
文件加密 | 异或、位移等算法 | 密钥管理是关键 |
字符串加密 | ASCII码操作或复杂算法(如AES) | 避免简单加密(易被破解) |