IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言核心知识梳理 -> 正文阅读

[C++知识库]C语言核心知识梳理

一、基本数据类型

基本数据类型

数据类型字符串模板格式化输出sizeof()字节
int%d4
short%d2
long%ld8
float%f4
double%lf8
char%c1

进制格式化输出

// %x 	十六进制
// %o 	八进制
// %s 	字符串
// %#x	打印地址

sizeof判断字节长度

//基本数据类型所占的字节数
printf("int 占%d字节\n", sizeof(int));
printf("char 占%d字节\n", sizeof(char));
printf("float 占%d字节\n", sizeof(float));

打印函数printf,

int i;
scanf("%d", &i); //控制台输入,&取地址符

输入函数scanf

printf("i的值为:%d\n", i);

系统暂停

system("pause");

二、指针

指针存储的是变量的内存地址。

2.1、变量指针

*:指针的数据类型与变量一致,即变量int i对应int *p

&:取地址操作符,获取变量地址

int i = 90;
//指针变量,创建一个int类型的指针
//p的值就是i这个变量的内存地址
int *p = &i;
printf("%#x\n", p);

指针赋值,也叫变量间接赋值。

i = 89;		// 直接赋值
*p = 89;	// 间接赋值

空指针NULL。

空指针的默认值为0

int *p = NULL;
printf("%#x\n", p); //输出0

2.2、多级指针

指针保存的是变量的地址

int a = 50;
//p1上保存的a的地址
int* p1 = &a;
//p2上保存的p1的地址
int** p2 = &p1;

printf("p1:%#x\n", p1);			//p1:0xbfeff2ac
printf("p2:%#x\n", p2);			//p2:0xbfeff2a0
printf("*p2:%#x\n", *p2);		//*p2:0xbfeff2ac

多级指针赋值

**p2 = 90;

2.3、数组

ids:数组名

&ids[0]:数组第一个元素地址

&ids:数组地址

都相等

//数组在内存中连续存储
int ids[] = { 78, 90, 23, 65, 19 };
//数组变量名:ids就是数组的首地址
printf("%#x\n", ids);       //0xbfeff2a0
printf("%#x\n", &ids[0]);   //0xbfeff2a0
printf("%#x\n", &ids);      //0xbfeff2a0  

指针运算p++、p–:移动sizeof(数据类型)个字节

int* p = ids;
//指针的加法
p++; //p++向前移动sizeof(数据类型)个字节
printf("p的值:%#x\n", p);   //p的值:0xbfeff2a4
printf("p指向:%d\n", *p);   //p指向:90

通过指针给数组赋值

int uids[5];
//早些版本的写法
int* p = uids;
for (int i=0; p < uids + 5; p++,i++)
{
    *p = i;
}

等价于(在最古老的C语言里是没有中括号array[i]的)

int uids[5];
for (int i = 0; i < 5; i++)
{
    uids[i] = i;
}

2.4、函数指针

函数名即函数地址

int msg(char* msg, char* title)
{
    printf("title:%s,msg:%s\n",title,msg);
    return 0;
}

int main(int argc, const char * argv[]) {
    printf("%#x\n", msg);           //0x3ec0
    printf("%#x\n", &msg);          //0x3ec0
    return 0;
}

函数指针

//函数返回值类型,函数指针的名称,函数的参数列表
int (*fun_p)(char* msg, char* title) = msg;
fun_p("content", "title");      //title:title,msg:content
//等价于
msg("content","title");         //title:title,msg:content

函数指针作为入参。

下图msg函数入参是minus函数、10、20

// 候选方法1
int add(int a, int b)
{
    return a + b;
}

// 候选方法2
int minus(int a, int b)
{
    return a - b;
}

//函数入参、m值、n值
void msg(int(*func_p)(int a, int b), int m, int n)
{
    printf("执行回调函数...\n");
    int r = func_p(m, n);
    printf("执行结果:%d\n", r);
}

void main()
{
    msg(minus, 10, 20);
}

2.5、小节

案例:用随机数生成一个数组,写一个函数查找最小的值,并返回最小数的地址,在主函数中打印出来

随机数生成

//初始化随机数发生器,设置种子,种子不一样,随机数才不一样
//当前时间作为种子 有符号 int -xx - > +xx
srand((unsigned)time(NULL));
rand() % 100;//100范围内

整个算法

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 最小值
int* getMinPointer(int ids[], int len)
{
    int i = 0;
    int* p = ids;
    for (; i < len; i++)
    {
        if (ids[i] < *p)
        {
            p = &ids[i];
        }
    }
    return p;
}

//生成随机数
int* generateArray(){
    int ids[10];
    //初始化随机数发生器,设置种子,种子不一样,随机数才不一样
    //当前时间作为种子 有符号 int -xx - > +xx
    srand((unsigned)time(NULL));
    for (int i = 0; i < 10; i++) {
        ids[i] = rand() % 100;//100范围内
        printf("%d\n", ids[i]);
    }
    return ids;
}

void main()
{
    int* ids=generateArray();
    
    int* p = getMinPointer(ids, sizeof(ids) / sizeof(int));
    printf("%#x,%d\n",p, *p);
}

2.6、指针与数据的几种写法

对于数组:

a+i			// 等价于&a[i]
*(a+i)	// 等价于a[i]

推导如下

int a[] = { 78, 34, 73, 25, 80, 90 };
//a首地址
//a 等价于 &a[0],a+1 等价于 &a[1]
//结论: a+i 等价于 &a[i]
//*a等价于 a[0],*(a+1) 等价于 a[1]
//结论: *(a+i)等价于a[i]
// 元素的值和元素的地址

验证

int a[] = { 78, 34, 73, 25, 80, 90 };
int* p = a;
for (int i = 0; i < 6; i++)
{
    printf("%d,%#x\n", a[i], &a[i]);				//78,0xbfeff2a0
    printf("%d,%#x\n", *(a + i), a + i);		//78,0xbfeff2a0
    printf("%d,%#x\n", p[i], &p[i]);  			//78,0xbfeff2a0
    printf("%d,%#x\n", *(p + i), p + i);		//78,0xbfeff2a0
}

2.7、二维数组

遍历输出

void main()
{
    int a[2][3] = { 95,82,56,17,29,30 };
    for(int i=0;i<2;i++){
        for (int j=0; j<3; j++) {
            printf("%d,%#x ",a[i][j],&a[i][j]);
        }
        printf("\n");
    }
}
// 95,0xbfeff2a0 82,0xbfeff2a4 56,0xbfeff2a8 
// 17,0xbfeff2ac 29,0xbfeff2b0 30,0xbfeff2b4 

二维数组a、*a、&a的区别:

代表含义内存地址Sizeof大小
a行指针,是数组第一行的指针0xbfeff2a012
*a数组的第一行第一个元素的指针0xbfeff2a04
&a一个指向二维数组的指针0xbfeff2a024

验证

printf("%#x,%#x,%#x\n", a, &a, *a);//0xbfeff2a0,0xbfeff2a0,0xbfeff2a0
//这里sizeof测量所指向的值的长度
printf("%d,%d,%d\n",sizeof(*a),sizeof(*&a),sizeof(**a));//12,24,4

指针移动

//a是一个行指针,是数组第一行的指针,a+1第二行的指针,以此类推
printf("%#x,%#x\n", a, a + 1);//0xbfeff2a0,0xbfeff2ac
//*a是数组的第一行第一个元素的指针,*a+1是数组第一行第二个元素的指针
printf("%#x,%#x\n", *a, *a + 1);//0xbfeff2a0,0xbfeff2a4
//*(a+1)是数组的第二(1)行第一(0)个元素的指针
printf("%#x,%#x\n", *(a + 1), *(a + 1) + 1);//0xbfeff2ac,0xbfeff2b0

取数组的第2行,第3个元素

printf("%d\n", a[1][2]);        //30
printf("%d\n", *(*(a + 1)+2));  //30

三、动态内存分配

3.1、C语言内存分配:

1.栈区(stack)
自动分配,释放
2.堆区(heap)
程序员手动分配释放,操作系统80%内存
3.全局区或静态区
4.字符常量区
5.程序代码区

3.2、栈区(stack)内存分配

使用基本数据类型关键字创建即可。

//分配40M内存,Stack Overflow
//windows下,栈内存分配2M(确定的常数),超出了限制,提示stack overflow错误
int a[1024 * 1024 * 10];//一个int为4字节

3.3、堆区(heap)内存分配

使用malloc、realloc关键字开辟内存

int* p = malloc(len * sizeof(int));										//开辟
int* p2 = realloc(p, sizeof(int) * (len + addLen));		//扩容

静态内存分配,分配内存大小的是固定,问题:1.很容易超出栈内存的最大值 2.为了防止内存不够用会开辟更多的内存,容易浪费内存
动态内存分配,在程序运行过程中,动态指定需要使用的内存大小,手动释放,释放之后这些内存还可以被重新使用(类似活水)

3.3.1、malloc开辟

循环开辟释放40M内存

#include <unistd.h>
void main()
{
    while (1) {
        sleep(3);
        //40M内存
        int* p = malloc(1024 * 1024 * 10 * sizeof(int));
        printf("%#x\n", p);
        sleep(3);
        //释放
        free(p);
        sleep(3);
    }
}

练习:创建一个数组,动态指定数组的大小

void main()
{
    //静态内存分配创建数组,数组的大小是固定的
    int a[10];
    
    int len;
    printf("输入数组的长度:");
    scanf("%d", &len);
    
    //开辟内存,大小len*4字节
    int* p = malloc(len * sizeof(int));
    srand((unsigned)time(NULL));
    for (int i = 0; i < len; i++)
    {
        p[i] = rand() % 100;
        printf("%d,%#x\n", p[i], &p[i]);
    }
    
    //手动释放内存
    free(p);
}

3.3.2、realloc扩容

重新分配内存的两种情况:

缩小,缩小的那一部分数据会丢失

扩大,(连续的)

1.如果当前内存段后面有需要的内存空间,直接扩展这段内存空间,realloc返回原指针

2.如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据库释放掉,返回新的内存地址

3.如果申请失败,返回NULL,原来的指针仍然有效

// 代码在上面malloc开辟的基础上接着写
int addLen;
printf("输入数组增加的长度:");
scanf("%d", &addLen);
//内存不够用,扩大刚刚分配的内存空间
int* p2 = realloc(p, sizeof(int) * (len + addLen));
if (p2 == NULL)
{
    printf("重新分配失败,世界那么大,容不下我。。。");
}

//重新赋值
i = 0;
for (int i = 0; i < len + addLen; i++)
{
    p2[i] = rand() % 100;
    printf("%d,%#x\n", p2[i], &p2[i]);
}

//手动释放内存
if (p2 != NULL) {
    free(p2);
    //释放完之后(指针仍然有值),给指针置NULL,标志释放完成
    p2 = NULL;
}

四、字符串

4.1、字符串存储方式

在java中字符串String是不可修改的,只能重新被复制。

在c中字符串有两种表达式:

字符数组:可修改

字符指针:不可修改

4.1.1、字符数组

字符数组表示字符串主要有这三种方式

char str[] = {'c','h','i','n','a','\0'};    //shina,0xbfeff2ba
char str[6] = { 'c','h','i','n','a' };      //shina,0xbfeff2ba
char str[10] = "china";                     //shina,0xbfeff2ae

//可以修改
str[0] = 's';
printf("%s,%#x\n", str,str);

对于不指定长度的字符串数组初始化,会读取错误

//错误 shina\320\363\357\277\367,0xbfeff2bb
char str[] = {'c','h','i','n','a'};

4.1.2、字符指针

//内存连续排列
char* str = "how are you?";
*str = 'y';//对字符串指针重新赋值会报错

使用指针加法,截取字符串

str += 3;
while (*str) {
    printf("%c",*str);
    str++;
}
// are you?

不能使用%s输出

printf("%s,%#x\n", str,str);//,0x3f9a

4.2、字符串常用函数

c函数速查与mac chm文档阅读器

4.2.1、strcpy拷贝

char *a = "china";
char dest[50];
// 把a字符串拷贝到dest数组中
strcpy(dest, a);

4.2.2、strcat拼接

// 把b字符串拼接到a后面
strcat(dest, b);
printf("%s\n", dest);//china is powerful!

4.2.3、匹配查找

strchr查单个字符

在一个串中查找给定字符的第一个匹配之处

只能查找一个字符

char* str = "I want go to USA!";
char *p = strchr(str, 'w');
if (p) {
    printf("索引位置:%d\n", p - str);//索引位置:2
}
strstr查多个字符

可以查多个字符

char *haystack = "I want go to USA!";
char *needle = "to";

char *p = strstr(haystack, needle);
if (p) {
    printf("索引位置:%d\n", p - haystack);//索引位置:10
}

五、结构体

数据类型:结构体是一种构造数据类型,把不同的数据类型整合起来成为一个自定义的数据类型,类似java中的bean。

操作方式:结构体是栈内存,不需要malloc,也不需要手动释放内存

struct Man
{
    //成员
    char name[20];
    //char* name;
    int age;
    //int (*func)();
};

5.1、初始化结构体

type1:构建时便赋值

struct Man m1 = {"Jack", 21};//Jack,21
printf("%s,%d\n",m1.name,m1.age);

type2:初始化对象的字面量

注意:这里对于name的赋值不能写作m1.name = “Jack”;指针字符串修改会报错

struct Man m1;
strcpy(m1.name, "rose");
//sprintf(m1.name, "Jason");//用于将格式化的数据写入字符串
m1.age = 23;
printf("%s,%d\n",m1.name,m1.age);//rose,23

type3:从另外一个结构体赋值

结构体是栈内存,这里m1与m2相互隔离。

struct Man m2 = m1;
m1.age = 25;

printf("%s,%d\n",m2.name,m2.age);//rose,23

5.2、结构体的几种写法

type1:具名结构体

struct Man{
    char name[20];
    int age;
} m1,m2={"张三",19};

void main(){
    strcpy(m1.name, "李四");
    m1.age=18;
    printf("%s,%d\n",m1.name,m1.age);
    printf("%s,%d\n",m2.name,m2.age);
}

type2:匿名结构体

控制结构体变量的个数(限量版),相当于单例

struct
{
    char name[20];
    int age;
}m1;

type3:结构体嵌套

struct Teacher{
    char name[20];
};

struct Student{
    char name[20];
    int age;
    struct Teacher teacher;
};

void main(){
    struct Student student1;
    strcpy(student1.name,"张三");
    student1.age=19;
    strcpy(student1.teacher.name,"李老师");
    printf("Student %s,%d的老师是%s\n",student1.name,student1.age,student1.teacher.name);
}
//Student 张三,19的老师是李老师

type4:结构体嵌套匿名结构体

struct Student{
    char name[20];
    int age;
    struct Teacher{
        char name[20];
    } teacher;
};

5.3、指针与结构体数组

struct Man
{
    char name[20];
    int age;
};

5.3.1、结构体数组的遍历

struct Man
{
    char name[20];
    int age;
};

void main()
{
    struct Man mans[] = { {"Jack", 20}, {"Rose",19} };
    struct Man* p = mans;
    for (; p < mans + 2; p++)
    {
        printf("%s, %d\n", p->name, p->age);
    }
}
//Jack, 20
//Rose, 19

5.3.2、计算结构体数组长度

sizeof(mans) / sizeof(struct Man)			//2
struct Man mans[] = { {"Jack", 20}, {"Rose",19} };
for (int i = 0; i < sizeof(mans) / sizeof(struct Man); i++)
{
    printf("%s, %d\n", mans[i].name, mans[i].age);
}

5.4、结构体的大小(字节对齐)

结构体大小讲究《字节对齐》:结构体变量的大小,必须是最宽基本数据类型的整数倍。

作用:可以提升读取的效率

基本情况

4+8+补上的整数=16

// 长度16
struct Man
{
    int age;
    double weight;
};

void main()
{
    struct Man m1 = {20, 89.0};
    printf("%#x, %d\n", &m1, sizeof(m1));//0xbfeff2b0, 16 
}

4+4+4+4+4=20,不需要对齐

//长度20
struct Man
{
    int a;
    struct Woman{
        int b;
        int c;
        int d;
        int e;
    } f;
};

烧脑情况

4+4+8=16,不需要对齐

//长度16
struct Man
{
    int age;
    int height;
    double weight;
};

4+8:第一次对齐,需要补上4

4+8+4:第二次对齐,再补上一4

最后为24

//长度24
struct Man
{
    int age;
    double weight;
    int height;
};

5.5、结构体与动态内存分配

p->name="Jack";
//这里箭头函数等价于
(*p).name="Jack";
struct Man
{
    char *name;
    int age;
};

void main()
{
    struct Man* m_p = (struct Man*)malloc(sizeof(struct Man) * 10);
    struct Man* p = m_p;
    //赋值
    
    p->name = "Jack";
    (*p).name="afd";
    p->age = 20;
    p++;
    p->name = "Rose";
    p->age = 20;

    struct Man* loop_p = m_p;
    for (; loop_p < m_p + 2; loop_p++)
    {
        printf("%s,%d\n", loop_p->name, loop_p->age);
    }

    free(m_p);
}
//afd,20
//Rose,20

六、typedef 类型取别名

6.1、基本数据类型取别名

//Age int类型的别名
typedef int Age;
//Age int类型指针的别名
typedef int* Ap;

6.2、结构体取别名

struct Man
{
    char* name;
    int age;
};
typedef struct Man JavaMan;
typedef struct Man* JM;

简写

typedef struct Woman
{
    char* name;
    int age;
} W, *WP; //W 是woman结构体的别名, WP 是woman结构体指针的别名

测试结构体别名、结构体指针别名

void main()
{
    JavaMan m1 = {"Jack",20};
    JM p = &m1;

    //结构体变量
    W w1 = { "Rose", 20 };
    //结构体指针'
    WP wp1 = &w1;
    printf("%s,%d\n", w1.name, w1.age);//Rose,20
    printf("%s,%d\n", wp1->name, wp1->age);//Rose,20
}

6.3、没有名称是否可以取别名

结构体可以

typedef struct {
    char* name;
    int age;
} Student;

void main(){
    Student student;
    student.name="zhangsan";
    student.age=18;
    printf("%s,%d\n",student.name,student.age);//zhangsan,18
}

6.4、结构体函数指针成员

Girl结构体类似于Java中的类,name和age类似于属性,sayHi类似于方法

struct Girl
{
    char* name;
    int age;
    //函数指针
    void(*sayHi)(char*);
};

void sayHi(char* text)
{
    printf("%s\n",text);
}

void main()
{
    struct Girl g1;
    g1.name = "Lucy";
    g1.age = 18;
    g1.sayHi = sayHi;
    g1.sayHi("hello");
}

使用typedef取别名

typedef struct Girl
{
    char* name;
    int age;
    //函数指针
    void(*sayHi)(char*);
} Girl;

typedef Girl* GirlP;

void sayHi(char* text)
{
    printf("%s\n",text);
}

void main()
{
    Girl g1 = {"Lucy", 18, sayHi};
    GirlP gp1 = &g1;
    gp1->sayHi("Byebye");//Byebye
}

七、联合体

不同类型的变量共同占用一段内存(相互覆盖),联合变量任何时刻只有一个成员存在,节省内存

联合体变量的大小=最大的成员所占的字节数

union MyValue
{
    int x;
    int y;
    double z;
};

void main(){
    union MyValue d1;
    d1.x=90;
    d1.y=100;
    d1.z=11.1;
    //最后一次赋值有效
    printf("%d,%d,%lf\n",d1.x,d1.y,d1.z);
}

八、枚举

默认会赋值123。因此

enum Day
{
  Monday = 0,
  Tuesday = 1,
	Wednesday = 2,
	Thursday = 3,
	Friday = 4,
	Saturday = 5,
	Sunday = 6
};
// 等价于
enum Day
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
};
void main()
{
    //枚举的值,必须是括号中的值
    enum Day d = Saturday;
    printf("%#x,%d\n",&d,d);
}

九、IO操作

9.1、读取文本文件

fopen:打开文件

fgetc:读文件(注意:windows中的fgetc与macos中的fgetc不一样)

char *path="/Users/zhanglei/Desktop/NewFile.txt";
// 打开
FILE *fp=fopen(path, "r");
if (fp==NULL) {
    printf("文件打开失败...");
    return;
}
/*windows使用这个*/
//    // 读取
//    char buff[50];//缓冲
//    while (fgetc(buff,50,fp)) {
//        printf("%s",buff);
//    }

/*macos使用这个*/
char ch;
while ((ch=fgetc(fp))!=EOF) {
    printf("%c",ch);//或者putchar(ch);
}

fclose(fp);

9.2、写入文本文件

fputs:写入文件

char *path="/Users/zhanglei/Desktop/NewFile2.txt";
// 打开
FILE *fp=fopen(path, "w");
if (fp==NULL) {
    printf("文件打开失败...");
    return;
}
char *text="hchmily@sina.com,程华才,学清路 8\n号科技财富中心 A";
fputs(text, fp);

// 关闭流
fclose(fp);

9.3、文件复制

fread:读字节

fwrite:写字节

char *read_path="/Users/zhanglei/Desktop/NewFile.txt";
char *write_path="/Users/zhanglei/Desktop/NewFile2.txt";
//读的文件 b字符表示操作二进制文件binary
FILE *read_fp = fopen(read_path,"rb");
//写的文件
FILE* write_fp = fopen(write_path, "wb");
// 缓冲区域
char buff[50];
//每次读到的数据长度
int len=0;
while ((len=fread(buff, sizeof(char), 50, read_fp))!=0) {
    //将读到的内容写入新的文件
    fwrite(buff, sizeof(char), len, write_fp);
}
//关闭流
fclose(read_fp);
fclose(write_fp);

9.4、获取文件的大小

fseek(fp, 0, SEEK_END):移动指针到文件末尾

ftell(fp):获取指针偏移量,即文件大小

char *read_path="/Users/zhanglei/Desktop/NewFile.txt";
FILE* fp=fopen(read_path, "r");
//重新定位文件指针
//SEEK_END文件末尾,0偏移量
fseek(fp, 0, SEEK_END);
//返回当前的文件指针,相对于文件开头的位移量
long fileSize=ftell(fp);
printf("%d\n",fileSize);//162

9.5、文本文件加解密

异或运算(可逆)

规则:1^1=0, 0^0=0, 1^0=1, 0^1=1 同为0,不同为1

fputc写入,这里不可写错成fputs

void cryptFun(char norma_path[],char crypt_path[]){
    //打开文件
    FILE* normal_fp=fopen(norma_path, "r");
    FILE* crypt_fp=fopen(crypt_path, "w");
    //一次读一个字符
    int ch;
    while ((ch=fgetc(normal_fp))!=EOF) {
        //写入(异或运算)
        fputc(ch^9, crypt_fp);
    }
    //关闭流
    fclose(normal_fp);
    fclose(crypt_fp);
}


void main(){
    char *norma_path="/Users/zhanglei/Desktop/NewFile.txt";
    char *crypt_path="/Users/zhanglei/Desktop/NewFile_crypt.txt";
    char *decrypt_path="/Users/zhanglei/Desktop/NewFile_decrypt.txt";
    //加密
    cryptFun(norma_path,crypt_path);
    //解密
    cryptFun(crypt_path,decrypt_path);
}

9.6、进制文件加解密

读取二进制文件中的数据时,一个一个字符读取

strlen:测量字符串长度,需要导#include <string.h>库

rb:读字节

wb:写字节

void cryptFun(char norma_path[],char crypt_path[], char password[]){
    //打开文件
    FILE* normal_fp=fopen(norma_path, "rb");
    FILE* crypt_fp=fopen(crypt_path, "wb");
    //一次读一个字符
    int ch;
    int i=0;
    //密码的长度
    int pwd_len=strlen(password);
    while ((ch=fgetc(normal_fp))!=EOF) {
        //写入(异或运算)
        fputc(ch^password[i++ % pwd_len], crypt_fp);
    }
    //关闭流
    fclose(normal_fp);
    fclose(crypt_fp);
}


void main(){
    char *norma_path="/Users/zhanglei/Desktop/liuyan.png";
    char *crypt_path="/Users/zhanglei/Desktop/liuyan_crypt.png";
    char *decrypt_path="/Users/zhanglei/Desktop/liuyan_decrypt.png";
    //加密
    cryptFun(norma_path,crypt_path,"iloveyou");
    //解密
    cryptFun(crypt_path,decrypt_path,"iloveyou");
}

十、预编译

C语言执行的流程

1、预编译(预处理):为编译做准备工作,完成代码文本的替换工作(宏定义、宏替换、预编译指令)

2、编译:形成目标代码(.obj)

3、连接:将目标代码与C函数库连接合并,形成最终的可执行文件

4、执行

10.1、define指令作用

定义标示

#ifdef __cplusplus //标识支持C++语法

定义常数(便于修改与阅读)

#define MAX 100
void main(){
  printf("%d\n", MAX);//100
}

定义“宏函数”

可以动态替换函数名称

void dn_com_jni_read() {
    printf("read\n");
}
void dn_com_jni_write() {
    printf("write\n");
}
#define jni(NAME) dn_com_jni_##NAME();

void main()
{
    //替换:dn_com_jni_write();
    jni(write);//write
}

10.2、日志输出示例

定义LOG、LOG_I、LOG_E三个宏函数

FORMAT//第一个入参
...//表示多个参数
__VA_ARGS__//可变参数(在windows下变量可能需要加个##,即##__VA_ARGS__)
//日志输出
//__VA_ARGS__可变参数
#define LOG(FORMAT,...) printf(FORMAT, __VA_ARGS__);
//日志会有级别
#define LOG_I(FORMAT,...) printf("INFO:"); printf(FORMAT, __VA_ARGS__);
#define LOG_E(FORMAT,...) printf("ERRO:"); printf(FORMAT, __VA_ARGS__);

void main()
{
    LOG("%s,%d\n","Jason",18);//Jason,18
    LOG_I("%s\n","message");//INFO:message
    LOG_E("%s\n", "fire");//ERRO:fire
}

升级版本

宏函数LOG_I、LOG_E复用了LOG宏函数

#define LOG(LEVEL, FORMAT, ...) printf(LEVEL); printf(FORMAT, __VA_ARGS__);
#define LOG_I(FORMAT, ...) LOG("INFO:", FORMAT, __VA_ARGS__)
#define LOG_E(FORMAT, ...) LOG("ERRO:", FORMAT, __VA_ARGS__)
void main()
{
    LOG("错误级别:","%s,%d\n","Jason",18);//错误级别:Jason,18
    LOG_I("%s\n","message");//INFO:message
    LOG_E("%s\n", "fire");//ERRO:fire
}

10.3、定义标示,解决重复引用

通过#define AH来解决重复循环include的问题(在c语言中循环引入会编译报错)。

#ifndef AH
#define AH
#include "B.h"
#endif

或者简洁语法

#pragma once

代码示例。MyCLanguage.c部分

#include "A.h"

void printfA()
{
    printf("print A");
}

void printfB()
{
    printf("print B");
}

void main()
{
    printfA();
}

A.h部分

//如果没有定义AH,定义AH
//#ifndef AH
//#define AH
//#include "B.h"
//#endif

#pragma once
#include "B.h"

void printfA();

B.h部分

//#ifndef BH
//#define BH
//#include "A.h"
//#endif

#pragma once
#include "A.h"

void printfB();
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-26 15:13:27  更:2022-05-26 15:13:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 0:31:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码