C 语言基础快速入门
认识C源文件的代码结构
#include <stdio.h>
//声明文件 .h .hpp
// <> 表示寻找系统的资源
// "" 表示寻找开发者定义的声明文件
// .c 源文件
// main 函数只能存在一个, 程序的入口
int main() {
printf("你好,Hello, World!\n");
// getchar();// 阻塞 用户输入 防止程序一闪而过
return 0;
}
基本数据类型
#include <stdio.h>
// 解决long double类型超长度无法输出的问题
// #define printf __mingw_printf // 或者在函数名前 + __mingw_
int main() { // 6种基本数据类型
short a1 = 10;
int a2 = 10;
long a3 = 10L;
float a4 = 10.0F;
double a5 = 10.0;
char ch = 'a';
// 字符串
char *str = "hello"; // 底层是不可变字符数组的形式构成
short int b1 = 10; // 等价 short 类型 最原始的形式
long int b3 = 10L; // 等价 long 类型
long long b4 = 10L; // 扩展类型
long double b5 = 10; // 扩展类型
//打印需要占位,格式化填充输出
printf("short类型 a1的值是%hd\n", a1);
printf("int类型 a2的值是%d\n", a2);
printf("long类型 a3的值是%ld\n", a3);
printf("float类型 a4的值是%f\n", a4);
printf("double类型 a5的值是%lf\n", a5);
__mingw_printf("long double类型 b5的值是%Lf\n", b5);
printf("字符类型 ch的值是%c\n", ch);
printf("字符串 str的值是:%s\n", str);
return 0;
}
类型占用字节数
#include <stdio.h>
//sizeof 判断占用的字节数 sizeof 变量名 / sizeof(变量名) / sizeof 类型 / sizeof(类型)
int main() {
// 基本类型占用的字节数
printf("short 占用的字节数:%llu\n", sizeof(short)); // 2
printf("int 占用的字节数:%llu\n", sizeof(int)); // 4
printf("long 占用的字节数:%llu\n", sizeof(long)); // 4 Linux上根据系统位数 4/8字节
printf("double 占用的字节数:%llu\n", sizeof(double)); // 8
printf("char 占用的字节数:%llu\n", sizeof(char)); // 1
// 指针类型 不管几级的,什么类型,32位平台4字节,64位平台8字节
printf("void* 占用的字节数:%llu\n", sizeof(void *)); // 8
return 0;
}
初识 指针/指针变量
指针就是内存空间的地址,指针变量存储的永远都是内存地址 ,通常我们叙述时会把指针变量简称为指针,实际他们含义并不一样
#include <stdio.h>
// C 万物皆指针 // Linux 一切皆文件 // java 万物皆对象
int main() {
//指针 == 地址
//例如 基本类型变量存储在栈中,栈也会由编译器分配一块空间来存储变量的值
int number = 10000;
// %p 输出地址的占位 & == 取地址符
printf("number在内存中的地址:%p\n", &number); //000000000061FE14
// 任何变量都有地址 【寄存器变量除外,它没有地址】 * == 取出地址存储的值,也叫解引用
printf("获取number地址的值:%d\n", *(&number)); //10000
// 指针变量永远存放的是内存地址
int *p = &number;
printf("获取指针变量:%p\n", p); //指针变量 存储的永远是内存地址 000000000061FE14
printf("获取指针变量内存地址的值:%d\n", *p); //获取地址对应存储空间的值 10000
// int * 表示int类型的指针
// 内存地址就是指针,指针就是内存地址
// p 指针变量名
*p = 300; // 修改了&number地址对应空间存储的值,那么number的值也会修改
printf("number:%d\n", number); //300
return 0;
}
常量指针和指针常量
常量指针:指针变量指向的空间的内容(数据),不能通过指针变量自己改变,即不允许通过指针修改空间内容。
指针常量:指针变量本身不可变,即指向不可变,理解为指针变量本身是常量。
函数声明和函数定义
声明与定义:声明变量不需要建立存储空间,定义变量需要建立存储空间
#include <stdio.h>
// C语言不允许函数重载的 ,即不允许同名函数 Java和C++是可以的
// 函数声明 : 如果函数定义在main函数后,需要事先在main函数前声明告诉编译器,确实存在这么一个函数,有的书上叫函数原型
// 格式 : 返回值类型 函数名 (形参列表)
void swap2(int *a, int *b);
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 10;
int b = 20;
swap(&a, &b);
printf("交换后的结果:a = %d, b = %d\n", a, b);
return 0;
}
// 函数定义 : 包含了完整的语句体实现
void swap2(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
多级指针
#include <stdio.h>
int main() {
int num = 100; // 指针 英文point ,通常指针变量名以p开头 (通常说的指针是指针变量,混为一谈了,但概念还是要记得)
int *p = # // num的内存地址 一级指针
int **pp = &p; // 存储的是p的内存地址,即指向指针(变量)p的指针 二级指针
int ***ppp = &pp; // 三级指针
printf("num的内存地址: %p\n", &num); // 000000000061FE14
printf("num的内存地址: %p\n", p); // 000000000061FE14
printf("指针变量p的内存地址: %p\n", &p); // 000000000061FE08
printf("指针变量p的内存地址: %p\n", pp); // 000000000061FE08
printf("指针变量pp的内存地址: %p\n", &pp); // 000000000061FE00
printf("指针变量pp的内存地址: %p\n", ppp); // 000000000061FE00
// 解引用
printf("num的值是: %d\n", *p); // 100
printf("num的值是: %d\n", **pp); // 100
printf("num的值是: %d\n", ***ppp); // 100
return 0;
}
数组与指针
数组名是地址常量,指针通常说的是 指针变量
,数组作为实参时,数组名会退化为指针
#include <stdio.h>
int main() {
// 定义一个int类型的数组,不指定元素个数,由编译器计算
int arr[] = {1, 2, 3, 4, 5};
// 如下遍历数组在其他平台上可能会报错,因为c99标准前是不支持在循环内定义局部变量的
// for (int i = 0; i < 4; ++i) {...}
// 比较通用的写法
int i = 0; // 定义变量时,建议初始化
for (i = 0; i < 5; i++) {
printf("%d\n", arr[i]);
}
// 数组的第一个元素的地址 和数组名本身 以及数组的地址 是一样的,但是并不等价,如果用指针进行操作就会发现级别不一样。
printf("arr == %p\n", arr); // 000000000061FE00 // 地址常量 指向第一个元素 指针操作时跨度为一个元素
printf("&arr == %p\n", &arr); // 000000000061FE00 // 跨度大小为整个数组
printf("&arr[0] == %p\n", &arr[0]); // 000000000061FE00
printf("%d\n", *(arr)); // 打印第一个元素
// 将数组的内存地址赋值给指针变量
int *p = arr;
//使用指针遍历数组
for (i = 0; i < 5; i++) {
// 两种取数组值的方式
printf("%d ", *(p + i));
printf("%d\n", p[i]);
}
printf("*p: %d\n", *p); //由于arr的内存地址指向了第一个元素,所以取值就是第一个元素 1
// 指针位移,由于数组内存地址是连续的所以指针+1就是指向第二个元素
p++; // 如果是arr++ 会报错,数组名是地址常量
printf("*p: %d\n", *p); //取值就是第二个元素 2
// 求数组元素个数
int size = sizeof arr / sizeof(int);
for (int j = 0; j < size; ++j) {
*(p + j) = j + 1000; // 通过指针给数组重新赋值
}
// 遍历打印省略...
return 0;
}
函数指针和指针函数
函数指针就是相当于把函数的函数名用指针替代了 void(*p)(int ,int ); void swap(int, int )
#include <stdio.h>
void add(int num1, int num2) {
printf("num1 + num2 = %d\n", (num1 + num2));
}
void sub(int num1, int num2) {
printf("num1 - num2 = %d\n", (num1 - num2));
}
// 函数指针,是一个指针(变量),指向一个函数的入口地址,等价函数名
// 指针函数,说明函数的返回值是指针类型
// 操作回调/函数回调
// void(*method)(int, int) -> 声明的函数指针
// void -> 返回值
// (*method) -> 函数名
// (int,int) -> 两个参数
void operate(void(*method)(int, int), int num1, int num2) {
method(num1, num2);
}
void callBackMethod(char *fileName, int current, int total) {
printf("压缩的文件:%s,进度:%d/%d", fileName, current, total);
}
void compress(char *fileName, void(*callback)(char *, int, int)) {
//开始压缩图片 压缩进度回调
callback(fileName, 5, 100);
}
//函数指针 - 相当于java接口的回调
int main() {
//函数add就是在内存中的地址 add和&add是一样的
printf("add的地址: %p\n", add);//add的地址:0x1011a0be0
printf("&add的地址: %p\n", &add);//&add的地址:0x1011a0be0
// 直接回调函数
operate(add, 1, 2); //num1 + num2 = 3
operate(sub, 2, 1); //num1 - num2 = 1
// void (*call)(char *, int, int) = callBackMethod;
//兼容性问题,先声明在使用
void (*call)(char *, int, int); // 定义函数指针(变量) 注意变量是call
call = callBackMethod; // 给函数指针赋值,call就代表callBackMethod方法
// 函数调用,函数回调其他函数,灵活性更强
compress("1.png", call);
return 0;
}
C 库函数
strcpy()、srand()、rand()
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
// C函数的使用
int main() {
// 初始化随机数发生器函数 随机数种子
srand((unsigned) time(NULL));
int i = 0;
for (i = 0; i < 10; ++i) {
printf("随机数: %d\n", rand() % 100); // rand()获取随机数 0 -99
}
// 字符串拷贝函数 注意拷贝的目标位置空间要足够大,要是可修改的左值,不能是常量
char s[10];
char *str = "abcdefg";
strcpy(s, str); // 将str拷贝到s中
printf("%s\n", s); // abcdefg
return (0);
}
静态/动态开辟内存
#include <stdio.h>
#include <stdlib.h>
// 动态开辟: malloc calloc 在堆区开辟 内存空间 动态范畴
// C语言在开发中不能出现野指针和悬空指针 free之后一定要将指针置空(NULL)
// 必须从动态开辟内存的起点释放
// 不要对同一块内存空间多次释放
/**
* 什么时候才会使用动态开辟呢?使用场景?
--> 当程序中有比较大的数据块需要使用内存的时候使用。静态分配内存,整个程序运行完才能释放,可能造成内存不足。
* 静态开辟的内存空间大小是不能改变的 常见的是直接实例化,定义数组或变量
*/
int main() {
//静态开辟 空间大小是不能改变的,静态开辟会自动回收安全可靠,但程序执行完才会回收 【有时程序一直处于循环运行中】
int array[6];
// 开辟的空间想要变化 需要使用动态开辟
// 动态开辟
int *p; // 野指针 没有具体的指向
// 申请在堆中开辟一段内存区域 返回void *: 表示可以转换成任意类型int * double *....
int *arr = (int *) malloc(1 * 1024 * 1024); // 申请堆区大小是1M
printf("arr自己的内存地址: %p ,arr指向的堆区的内存地址:%p\n", &arr, arr);
// arr自己的内存地址:0x7ffee57faee8, arr指向的堆区的内存地址:0x7fe219500000
free(arr); // 释放申请的堆区
//释放堆区后,arr还是指向了堆区的内存地址,这就会有 悬空指针 的问题
printf("arr指向的堆区的内存地址: %p\n", arr);
// 不设置NULL会悬空指针 ,需要重新指向一块内存地址,因为即使free,arr还是指向了堆区的地址
arr = NULL;
printf("arr指向的堆区的内存地址: %p\n", arr); // arr自己的内存地址:0x0, 空指针
// malloc 申请堆区内存 free释放堆区
return 0;
}
进制转换
常见有十进制、二进制、8进制、16进制
十进制转二进制 除2倒数取余
十进制转八进制 除8倒数取余 ...
二进制转8进制,3位一组,按421算
二进制转16进制,4位一组,按8421算
字符串常见操作
size_t strlen(const char *_Str) 字符串长度计算,不计算 '\0'
char *strcpy(char *dest, const char *src); 字符串复制,'\0' 也会复制,但注意dest要是可修改的左值,注意容量要足够
char *strncpy(char *dest, const char *src, size_t n); 将前n个字符复制到dest所指向的空间中,容量够会复制'\0'
char *strcat(char *dest, const char *src); 字符串拼接,将src全部拼接到dest, 注意dest的空间要足够
char *strncat(char *dest, const char *src, size_t n); 将src字符串前n个字符连接到dest的尾部,末尾追加'\0'
int strcmp(const char *s1, const char *s2); 比较是s1和s2的大小,逐个字符按ASCII码比较,返回值为两字符的差值
int strncmp(const char *s1, const char *s2, size_t n); 比较是s1和s2前n个字符的大小,逐个字符按ASCII码比较
int sprintf(char *str, const char *format, ...); 将字符格式化输出到 字符缓存区
int sscanf(const char *str, const char *format, ...); 从字符串按照 格式化串 抽取值传递给变量
char *strchr(const char *s, int c); 在字符串s中查找字母c出现的位置
const char *strstr(const char *str, const char * subStr); 在字符串str,查找子串subStr,返回第一次出现子串的位置
char *strtok(char *str, const char *delim); 字符串切割,将delim出现的位置替换成 '\0',第一次必须传分隔符delim
int atoi(const char *nptr); 将字符转成int类型整数 类似的 atof() 、atol()
char * str2 = "Derry";
str2[2] = 'z'; // 报错, 因为str2指向的字符串是常量
内存操作函数
void *memset(void *dest, int value, size_t n); 内存初始化:将dest空间的前n个字节初始化为value值
void *memcpy(void *dest, const void *src, size_t n); 内存数据拷贝:将src的前n个字节拷贝到dest,如果内存空间重叠会报错。
void *memmove(void *dest, const void *src, size_t n); 内存数据拷贝:同memcpy(), 区别就是安全可靠。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *p = (char *) malloc(sizeof(char) * 16);
memset(p, 0, sizeof(char) * 16); // 注释掉这行会输出乱码
puts(p);
}
char str[16] = {0};
char *s = "hello world";
// memcpy(str, s, strlen(s));
memmove(str,s, strlen(s));
puts(str);
内存四区
程序未加载到内存前,已经分为代码区(text)、数据区(data)和未初始化数据区(bss) 【data区和bss区有时合称全局区/静态区】
加载到内存后,又有了栈区(stack)和堆区(heap)
结构体
- 声明结构体,及结构体赋值
#include <stdio.h>
#include <string.h>
#include <malloc.h>
// 结构体类似于Java中的类,但有区别,没有继承的概念,仅仅是自定义数据结构
struct User {
//定义成员
char name[10];
int age;
char sex;
char *desc;
}; // 末尾可以定义结构体变量,但一定要以分号结尾
int main() {
// 类型就是 struct User 可以使用typedef定义别名
struct User user;
// 初始化成员
// 字符数组赋值 不能使用 = ,否则会报错,不能只将一个地址赋值给一个数组
strcpy(user.name, "张三");
user.age = 18;
user.sex = 'n';
user.desc = "hello"; // 指针类型可以直接赋值
// name: 张三, age: 18, sex: n, desc: hello
printf("name: %s, age: %d, sex: %c, desc: %s\n", user.name, user.age, user.sex, user.desc);
// 定义该类型指针变量,需要动态开辟空间
struct User *user2;
user2 = (struct User *) malloc(sizeof(struct User) * 1); // malloc返回void* 指针,建议强制转换
// memset(user2, 0, sizeof(struct User) * 1); // 初始化空间为0, 第三个参数不能直接丢指针,指针大小只有4/8
// 指针变量赋值 使用 -> 成员运算符
strcpy(user2->name, "李四");
user2->age = 20;
user2->sex = 'n';
user2->desc = "happy"; // 指针类型可以直接赋值
printf("name: %s, age: %d, sex: %c, desc: %s\n", user2->name, user2->age, user2->sex, user2->desc);
// name: 张三, age: 18, sex: n, desc: hello
// name: 李四, age: 20, sex: n, desc: happy
// 释放空间
free(user2);
user2 = NULL; // 防止悬空指针
return 0;
}
- 结构体嵌套
#include <stdio.h>
struct Study {
char *studyContent; // 学习内容
};
//结构体嵌套
struct Student {
char name[10];
int age;
char sex;
struct Study study;
// 不推荐的写法,这里相当于定义了一个结构体,有一个实例wan 嵌套定义,阅读性差
struct Wan {
char *wanContent;
} wan; //定义结构体别名
};
int main() {
// 使用结构体 声明时就初始化
struct Student stu = {"赵云", 18, 'N',
{"学习C语言"},
{"游戏"}
};
// 获取嵌套的结构体
stu.study.studyContent;
stu.wan.wanContent;
printf("姓名: %s\n年龄: %d\n性别: %c\n爱好: %s\n喜欢: %s\n",
stu.name, stu.age, stu.sex, stu.study.studyContent, stu.wan.wanContent);
return 0;
}
- 结构体数组
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Cat_ {
char name[10];
int age;
} Cat; // typedef 定义类型别名
// typedef struct Cat_ Cat;
int main() {
// 结构体数组 静态分配内存
Cat cat[5] = {
{"xs", 1},
{"xs2", 2},
{"xs3", 3},
};
// 初始化结构体数组第四个元素, 注意索引下标是3,索引从0开始
strcpy(cat[3].name, "xs4");
cat[3].age = 4;
// 以指针的形式根据数组名 + 偏移量 初始化数组元素
strcpy((*(cat + 4)).name, "xs5");
(*(cat + 4)).age = 5;
for (int i = 0; i < 5; ++i) {
printf("name: %s, age: %d\n", cat[i].name, cat[i].age);
}
return 0;
}
- typedef 定义结构体类型 抹平不同工具平台的差异化
#include <stdio.h>
#include <stdlib.h>
struct Cat_ {
int age;
};
// typedef 定义类型别名
typedef struct Cat_ Cat;
typedef struct Cat_ *pCat; // 注意这里是为struct Cat_ * 类型定义了一个别名 pCat
int main() {
Cat a = {2};
pCat b; // 实际是指针类型变量
b = (pCat) malloc(sizeof(Cat) * 1);
b->age = 3;
free(b);
b = NULL;
return 0;
}
联合体/共用体
联合体也叫共用体,定义方法类似结构体,不同的是使用的是union关键字,并且整体占用的内存大小为成员变量的最大的那个。
#include <stdio.h>
// 定义联合体
typedef union Ua_ {
char ch;
short a;
int b; // 4字节
double c; // 8字节
} ua;
int main() {
printf("联合体类型 union Ua_占用内存大小 : %llu 字节", sizeof(union Ua_)); // 8
return 0;
}
枚举
#include <stdio.h>
//枚举 Day_类型
enum Day_ { // 默认值从 0 开始递增
Mon, Tues, Wed, Thur, Fri, Sat, Sun
};
//枚举 Day2_类型
enum Day2_ { // 从 2 开始递增
// Mon, Tues, Wed, Thur, Fri, Sat, Sun // 报错,与上一个枚举类的定义冲突了
Mon1 = 2, Tues1, Wed1, Thur1, Fri1, Sat1, Sun1
};
// 定义类型别名
typedef enum Day_ Day;
int main() {
// 枚举类型变量,就是一个变量,一个int类型的变量
// enum Day_ day1 = 1;
// 通常搭配 switch 使用
enum Day_ day1 = Thur;
Day day2 = Wed;
printf("day1: %d, day2: %d\n", day1, day2);
return 0;
}
存储类型总结
类型 | 作用域 | 生命周期 | 存储位置 |
---|---|---|---|
auto变量 (普通局部变量) | 一对{}内 | 当前函数 | 栈区 |
static局部变量 | 一对{}内 | 整个程序运行期 | 初始化在data段,未初始化在BSS段 |
extern变量 | 整个程序 | 整个程序运行期 | 初始化在data段,未初始化在BSS段 |
static全局变量 | 当前文件 | 整个程序运行期 | 初始化在data段,未初始化在BSS段 |
extern函数 | 整个程序 | 整个程序运行期 | 代码区 |
static函数 | 当前文件 | 整个程序运行期 | 代码区 |
register变量 | 一对{}内 | 当前函数 | 运行时存储在CPU寄存器 |
字符串常量 | 当前文件 | 整个程序运行期 | data段 |
文件操作
文件指针 FILE * , FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型
C语言中有三个特殊的文件指针由系统默认打开,用户无需定义即可直接使用:
stdin: 标准输入,默认为当前终端(键盘),我们使用的scanf、getchar函数默认从此终端获得数据。
stdout:标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端。
stderr:标准出错,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端。
文件打开/关闭
FILE * fopen(const char * filename, const char * mode); 文件打开:以mode模式打开文文件
int fclose(FILE * stream); 文件关闭:关闭成功返回0
**打开模式 ** | 含义 |
---|---|
r或rb | 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错) |
w或wb | 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a或ab | 以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件 |
r+或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+或 wb+ | 以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a+或ab+ | 以添加方式打开可读、可写的文件。若文件不存在则创建文件;如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留。 |
文件读写 API
// 按字符读,从流中读取一个字符,返回字符的int类型的ASCII码值
int fgetc(FILE * stream);
// 将ch转换为unsigned char后写入stream指定的文件中
int fputc(int ch, FILE * stream);
// 按行读, 从流中读取一行,当读到n-1字节或结尾时停止,成功返回地址
char *fgets(char *_Buf, int _MaxCount, _iobuf *_File);
// 将str所指定的字符串 写入到stream指定的文件中
int fputs(const char * str, FILE * stream);
// 格式化输出到文件
int fprintf(FILE * stream, const char * format, ...);
// 从文件读数据,格式化解析
int fscanf(FILE * stream, const char * format, ...);
// 按照块读写文件fread、fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
// 按块读,返回值为第三个参数的实际读到的字节数, 使用时第二个参数建议使用1
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// 判断文件是否读到的文件结尾, 返回值 0:没有到文件结尾
int feof(FILE * stream);
#include <stdio.h>
#include <string.h>
int main() {
char *fileName = "D:/Test/test.txt"; // 文件的路径
FILE *fp = fopen(fileName, "r");
if (fp == NULL) {
// 如果fp为 NULL就表示文件打开失败了
perror("文件打开失败");
return -1;
}
// 定义缓冲区
char buffer[1024] = {0};
while (fgets(buffer, 10, fp)) {
printf("%s", buffer);
memset(buffer, 0, sizeof buffer); // 重置缓冲区
}
//关闭文件
fclose(fp);
fp = NULL;
return 0;
}
文件复制
#include <stdio.h>
#include <string.h>
int copy_f(const char *dest, const char *src);
int main() {
// char *fileName = "D:/Test/test.txt"; // 文件的路径
// char *destName = "D:/Test/test_copy.txt";
char *fileName = "D:/Test/video.mkv"; // 文件的路径
char *destName = "D:/Test/video_copy.mkv"; // 文件的路径
copy_f(destName, fileName);
return 0;
}
// 任意文件复制
int copy_f(const char *dest, const char *src) {
if (dest == NULL || src == NULL) {
perror("路径不存在");
return -1;
}
FILE *src_f = fopen(src, "rb");
if (src_f == NULL) {
perror("文件打开失败");
return -1;
}
FILE *dest_f = fopen(dest, "wb+");
rewind(src_f); // 重置文件读写指针位置到开头
char buf[1024] = {0}; // 文件缓存区
int len; // 实际读到的字节数
// fread(缓存区,块大小,块数,文件流) 返回实际读到的块数 也就是第三个参数的实际值
while ((len = fread(buf, 1, sizeof buf, src_f))) { // fread() 第一个参数给1就行
fwrite(buf, len, 1, dest_f);
memset(buf, 0, sizeof buf);
}
// 关闭文件
fclose(dest_f);
fclose(src_f);
dest_f = NULL;
src_f = NULL;
puts("文件复制完成");
return 0;
}
文件加密及解密
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <malloc.h>
int code_f(const char *src_file, char *dest_file, const char *secret_key, int flag);
int main(int argc, char *argv[]) {
char *src = "D:\\Test\\video.mkv"; //要加密的文件
char *dest = "D:\\Test\\video_code.mkv"; //解密后的文件
code_f(src, dest, "242563534", 2);
return 0;
}
// 加密文件:把每一个字节拿出来,对每一个字节进行处理(全部加密)。把某一部分内容拿出来处理(部分加密)
// 异或处理 10 ^ 5 = 11111
// 解密:还原 1111 ^ 5 = 10
// flag: 1 表示加密 其他表示解密
int code_f(const char *src_file, char *dest_file, const char *secret_key, int flag) {
time_t t1 = time(NULL);
if (src_file == NULL || dest_file == NULL) {
perror("路径错误");
return -1;
}
FILE *src_fp = NULL;
FILE *dest_fp = NULL;
if (flag == 1) {
printf("开始文件加密 ->\n");
src_fp = fopen(src_file, "rb");
dest_fp = fopen(dest_file, "wb"); // w模式,文件不存在自动创建
} else {
printf("开始文件解密 <-\n");
src_fp = fopen(dest_file, "rb");
dest_fp = fopen(src_file, "wb");
}
if (src_fp == NULL) {
perror("文件打开失败");
return -1;
}
// 密钥
int len = secret_key == NULL ? 64 : strlen(secret_key) + 1;
char sk[len];
if (secret_key != NULL) {
strcpy(sk, secret_key);
} else {
strcpy(sk, "1234567890123456");
}
int ch;
int index = 0;
int pass_len = strlen(sk);
while ((ch = fgetc(src_fp)) != EOF) {
// 按散列算法,获取秘钥中的加密字符
char item = sk[index % pass_len];
// printf("item=%c\n", item);
// 每一个字节异或,再写入文件
fputc(ch ^ item, dest_fp);
index++;
}
fclose(src_fp);
fclose(dest_fp);
time_t t2 = time(NULL);
printf("完成, 耗时%llds\n", t2 - t1);
src_fp = NULL;
dest_fp = NULL;
return 0;
}