C语言复习

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;    // 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值