前言

看到这篇博客的同学们,到明天为止,咱们的c语言高级局部解说就完结了(可能有的同学好奇我的题目不是写的15天么,这才七天,哈哈,因为咱们接下来就要开始进入c++的世界了,算是c语言的进阶,我明天整顿公布的已经自学的笔记绝对有些简单,波及指针高级运算,明天的内容不求把握,只求简略了解就好,即便没懂,也没关系啦,楼主纯手动码字不易,还望珍惜。欢送关注,多和我交换。

0. 温习

0.1 构造体

是一种复合数据类型,能够将多个不同类型得变量给捏在一起。个别用于代表某一个整体得信息。
比方:学生信息有学生姓名,年龄,学号.... 贪吃蛇的 速度 血量 长度.....
语法:

struct 类型名{  字段1类型   字段1名字; //字段也叫做成员  字段2类型   字段2名字;    .....;};struct  类型名  变量名 = {初始值};C语言中定义构造体变量的时候,须要加上struct.c++不须要C语言的程序员为了不写这个struct,有了一种类型定义的写法typedef struct _类型名{  字段1类型   字段1名字; //字段也叫做成员  字段2类型   字段2名字;    .....;}类型名,*P类型名;typedef struct _STUDENT{  //}STUDENT,*PSTUDENT;

应用构造体的时候,依照成员自身的类型去应用。

0.2 联合体

和构造体语法是类型的,区别在于联结的所有成员是共享内存的。
比拟适宜用在 成员互斥的状况下(一个无效,其余的就都是有效的)

0.3 类型定义

typedef int INT; //INT 就是int的别名

0.4 堆空间

申请:malloc
开释:free
设置内存中的值:memset
拷贝内存:memcpy
一些概念:
悬空指针:开释之后,没有被置为nullptr的指针
野指针:没有初始化的指针
悬空指针和野指针都指向有效区域。
正在运行的程序,有5个内存区域:
静态数据区:全局变量,static局部变量所在的区域
常量区:字符串常量所在的区域
代码区:代码所在的区域
栈区:局部变量和函数的参数都在栈区
堆区:malloc 申请的空间,是堆区的。

作用域:变量起作用的一个范畴
生存期:变量存在的一个期间
局部变量,参数:进入函数,无效,此时在栈区创立进去。来到函数,生效,此时主动销毁。
动态局部变量:进入函数之前就被创立。然而在函数里面是不能应用的。来到函数,也不会被销毁。
堆区:申请就存在,开释就销毁

1. 指针进阶

1.1 指针的算术运算

1.1.1 指针+(-) 整数

#include <stdio.h>int main(){    int a = 100;    int* p = NULL;    p = (int*)100;    //1. 一个地址实质来说也是一个数字    //然而地址个别都是由&失去的,或者malloc函数返回的    //如果咱们轻易写个地址,这个地址通常都是不能拜访    //2. 做加法运算    //a+n 一个整型做加法运算,为了失去一个算术后果    //p+n 指针做加法运算,是为了失去偏移为n的元素的地位。    //有了这个地位,就能够通过*失去偏移为n的地位的数据    printf("%d\n", a );    printf("%d\n", a+2);    printf("%d\n", p);    printf("%d\n", p + 2);    //3. 这个个性有什么用呢???    int arr[10] = { 4,5,20,40,10,6,7,8,9,10 };    p = arr;//能赋值,阐明类型类似    for (int i = 0; i < 10; i++)    {        printf("%x ", p + i);        printf("%d \n", *(p + i));    }    //4. double* char*  short*  构造体*    typedef struct _TEST    {        int a;        double b;        int c;    }TEST,*PTEST;    double* p2 = (double*)100;    char* p3 = (char*)100;    short* p4 = (short*)100;    //上面两种写法等价的    PTEST p5 = (PTEST)100;    TEST* p6 = (TEST * )100;    printf("%d\n", p2);    printf("%d\n", p3);    printf("%d\n", p4);    printf("%d\n", p5);    printf("%d\n", p6);    printf("%d\n", p2+1);//108    printf("%d\n", p3+1);//101    printf("%d\n", p4+1);//102    printf("%d\n", p5+1);//124    printf("%d\n", p6+1);//124    return 0;}

1.1.2 指针- 指针(大略理解即可)

要求:两个指针的类型必须是统一的。失去的后果是两个地址之间可能存储下多少个此类型

1.2 指针和一维数组

指针和一维数组有十分多的相同点:

#include <stdio.h>int main(){    //1. 对于不类似的类型,C语言是不让间接赋值的    int nNum1 = 100;    short sNum1 = 0;    sNum1 = nNum1;    int* p1 = NULL;    short* p2 = NULL;    p1 = &nNum1;    p2 = &sNum1;    //类型十分不同,不能间接赋值的    //p = nNum1;不行    //short*和int*也是不能间接赋值的    //运算规定不同    //p1 = p2;    //指针和数组能够间接赋值    int arr[10] = { 4,5,20,40,10,6,7,8,9,10 };    int* p = arr;//能赋值,阐明类型类似    for (int i = 0; i < 10; i++)    {        printf("%d ", *(p + i));        printf("%d ", *(arr + i));        printf("%d ", arr[i]);        printf("%d \n", p[i]);    }    return 0;}

是否就能够说 指针就是数组呢???
1.指针是一个变量,能够指向其余地位
2.数组名是数组的起始地址,是一个常量,不能扭转的
从sizeof的角度说,他俩也不同。
在32位,任何一个指针,都是四个字节的变量。

1.3 一级指针的应用场景

1.3.1 通过函数去批改内部的变量

#include <stdio.h>void Test(int* a){    *a = 500;}int main(){    int nNum = 100;    Test(&nNum);    printf("%d", nNum);    return 0;}

1.3.2 将数组作为一个参数进行传递的时候

#include <stdio.h>//         int* Test//         int Test[]//         int Test[100]int GetAdd(int Test[10]){    int s = 0;    printf("%d\n",sizeof(Test));    for (int i = 0; i < 10; i++)    {        s += Test[i];    }    return s;}int main(){    //如果有一个数组    int arrTest[10] = { 4,3,5,7,8,1,2,9,0,10 };    //通过函数求所有元素的和    //数组名是一个地址,接管地址的只能是指针    printf("%d\n", sizeof(arrTest));    int n = GetAdd(arrTest);    printf("%d", n);    return 0;}

1.3.3 应用堆空间的时候

#include <stdio.h>#include <stdlib.h>//         int* Test//         int Test[]//         int Test[100]int GetAdd(int Test[10], int nCount){    int s = 0;    printf("%d\n", sizeof(Test));    for (int i = 0; i < nCount; i++)    {        s += Test[i];    }    return s;}int main(){    int* p = (int*)malloc(5 * sizeof(int));    for (int i = 0; i < 5; i++)    {        printf("请输出一个数据:");        scanf_s("%d", &p[i]);        //scanf_s("%d", p+i);    }    int n = GetAdd(p, 5);    printf("和为%d", n);    return 0;}

1.4 指针和二维数组(对初学者来说难度很大,尽量了解就好,没有了解也没有关系,不用泄气)

#include <stdio.h>int main(){    int arrTest1[3][4] =    {   1,2,3,4,        5,6,7,8,        9,10,11,12 };    int arrTest2[10][4] =    { 1,2,3,4,5,6,7,8,9,10,11,12 };    int arrTest3[3][5] =    { 1,2,3,4,5,6,7,8,9,10,11,12 };    //printf("%p\n", arrTest1);    //二维数组名也是地址    //然而类型并非是一般的指针    //int* p1 = arrTest1;    //类型和  【数组指针】  类似    int(*p1)[4] = NULL;    p1 = arrTest1;//赋值胜利,类型的确类似    p1 = arrTest2;//这个也能胜利,就是列数对上了就行    //p = arrTest3; 不能胜利    int(*p2)[5] = arrTest3;    //数组指针有什么特点:    //数组指针+1是加了一排    int(*p3)[4] = ( int(*)[4] )100;    //printf("%d ", p3);    //printf("%d", p3 + 1);    //数组指针和二维数组加法规定统一    p1 = arrTest1;    printf("%p\n", p1);    printf("%p\n", p1+1);    printf("%p\n", p1 + 2);    printf("%p\n", arrTest1);    printf("%p\n", arrTest1 + 1);    printf("%p\n", arrTest1 + 2);    //解一次援用    //对于数组指针和二维数组而言    //解一次援用,还是那个地址值不变    //然而 类型产生了变动    printf("------------------\n");    printf("%p\n", p1);      //数组指针类型    printf("%p\n", *p1);     //一级指针类型,指向的是下标为0的那一排    printf("%p\n", p1 + 1);  //数组指针类型    printf("%p\n", *(p1 + 1)); //一级指针类型,指向的是下标为1的那一排    printf("------------------\n");    printf("%p\n", arrTest1);    printf("%p\n", *arrTest1);    printf("%p\n", arrTest1 + 1);    printf("%p\n", *(arrTest1 + 1));    //区别    printf("------------------\n");    printf("%p\n", p1+1);    printf("%p\n", p1 + 1+1);//再往下找一排+16    printf("%p\n", *(p1 + 1) + 1);//在本排中找下一个元素+4    printf("%p\n", *(*(p1 + 1) + 1));    return 0;}

代码有点简短和绕,一张图了解总结下:

    //遍历二维数组    for (int i = 0; i < 3; i++)    {        for (int j = 0; j < 4; j++)        {            printf("%d", p1[i][j]);            printf("%d", *(p1[i]+j));            printf("%d", *(*(p1+i)+j));            printf("%d", (*(p1 + i))[j]);            printf("%d", arrTest1[i][j]);            printf("%d", *(arrTest1[i] + j));            printf("%d", *(*(arrTest1 + i) + j));            printf("%d", (*(arrTest1 + i))[j]);        }    }

1.5 应用场景

1.5.1 传参

1.5.2 应用堆空间

#include <stdio.h>#include <stdlib.h>#include <memory.h>//         int (*test)[4]//         int test[][4]//         int test[8][4]int GetAdd(int(*test)[4],int nRowCount){    printf("%d", sizeof(test));    int s = 0;    for (int i = 0; i < nRowCount; i++)    {        for (int j = 0; j < 4; j++)        {            s += test[i][j];        }    }    return s;}int main(){    //1. 数组传参    int arr[3][4] = { 1,2,3,4,        5,6,7,8,        9,10,11,12 };    printf("%d", sizeof(arr));    GetAdd(arr,3);    //2. 应用堆空间,能够把堆空间当数组来用    int(*p)[4] = (int(*)[4])malloc(5 * 4 * sizeof(int));    memset(p, 0, 5 * 4 * sizeof(int));    p[1][1] = 10;    GetAdd(p, 5);    return 0;}

1.6 数组指针和指针数组

// 数组指针 是指针
// 指针数组 是数组

#include<stdio.h>#include <stdlib.h>int main(){    //数组指针是一个指针    //能够把一块内存区域,依照二维数组的拜访去应用    int(*p1)[4] = nullptr;    int arrTest[5][4] = { 1,2,3 };    p1 = arrTest;    //指针数组    int nNum = 0;    int arr[3] = { 1,2,3 };    int* p2[4] = { NULL,NULL,NULL,NULL };    p2[0] = (int*)malloc(5 * sizeof(int));    p2[1] = &nNum;    p2[2] = arr;    p2[3] = (int*)malloc(6 * sizeof(int));    //尽管两个语法是不同的概念    //然而在应用的时候有相似性    arrTest[1][1] = 100;    //这个代表 2号指针 指向的地位 外面下标为1的中央    p2[2][1] = 20;    return 0;}

字符类型

//字符串    char arr1[3][20] = { "xiaoming","xiaobai","xiahui" };    printf("%s", arr1[0]);    printf("%s", arr1[1]);    arr1[0][1] = 'a';    const char* arr2[3] = { "xiaoming","xiaobai","xiahui" };    printf("%s", arr2[0]);    printf("%s", arr2[1]);    //arr2[0][1] = 'a';

1.7 构造体指针

int main(){    TEST stc = {10,2.5,20};    PTEST pstc = NULL;    pstc = &stc;    printf("%d %d\n", pstc->a, stc.a);    printf("%lf %lf\n", pstc->b, stc.b);    printf("%d %d\n", pstc->c, stc.c);    pstc = (PTEST)malloc(sizeof(TEST) * 3);    memset(pstc, 0, sizeof(TEST) * 3);    for (int i = 0; i < 3; i++)    {        (pstc + i)->a = i * 10+1;        pstc[i].b = 3.3;    }    return 0;}

1.8 二级指针

#include <stdio.h>#include <stdlib.h>#include <memory.h>void Fun(int** p, int n){    *p = (int*)malloc(n * sizeof(int));    memset(*p, 0, sizeof(int) * n);}int main(){    int a = 100;    int* p1 = &a;    //二级指针    int** p2 = &p1;    //利用,如果说我心愿在一个函数中申请堆空间,出了函数    //这个堆空间还能应用    int* p3 = NULL;    Fun(&p3, 5);}

2. 文件操作

2.1 文件的简略分类

1.文本文件(应用记事本可能间接关上并查看显示的问题),实质上存储的是文本的编码
2.二进制文件(电影,音乐,图片,通常是给特定的软件去应用的)

2.2 对于门路的问题

咱们在windows中,去定位一个文件,通常都是应用门路的,去找到某一个文件
门路有两种模式:
1.绝对路径
D:\Test\abc.txt
2.相对路径
是从当前目录登程,去寻找某一个文件
. 代表当前目录
.. 代表上一级目录

例子:
.\abc.txt
..\test\abc.txt
什么是当前目录:
1.当咱们F5 调试运行的时候,代码所在的目录就是当前目录
2.当咱们间接运行程序的时候,exe文件所在的目录就是当前目录

2.3 文件操作的根本步骤

1.关上文件 fopen_s

2.读写文件

  从文件中读取数据              往文件中写入数据       fgetc                                  fputc       fgets                                  fputs       fscanf_s                             fprintf       fread                                  fwrite

文件读取写入函数-都是字符

#include <stdio.h>int main(){    FILE* pFile = nullptr;    ////1. 关上一个文件,以w的形式,文件不存在就会创立一个    ////这里的文件指针,就是代表一个关上的文件    //pFile = nullptr;    //fopen_s(&pFile, "..\\debug\\abc.txt", "w");    ////2. 写入内容    //// 2.1 fputc    ////fputc('A', pFile);    ////fputc('B', pFile);    ////fputc('C', pFile);    //// 2.2 fputs    //// fputs("hello world", pFile);    //// 2.3 fprintf    //fprintf(pFile, "%d %d %lf", 10, 20, 8.55);    ////3. 敞开文件    //fclose(pFile);    //读取    //1. 关上文件    pFile = nullptr;    fopen_s(&pFile, "..\\debug\\abc.txt", "r");    //2. 读取文件    //2.1 fgetc    //char cCh = 0;    //while (cCh!=EOF)    //{    //    cCh = fgetc(pFile);    //    printf("%c", cCh);    //}    //2.2 fgets    //char buf[20] = {};    //fgets(buf,20,pFile);    //2.3 fscanf_s    int a = 0;    int b = 0;    double c = 0;    fscanf_s(pFile, "%d %d %lf", &a, &b, &c);    //3. 敞开文件    fclose(pFile);}

文件读取写入函数- 内存读写

#include <stdio.h>int main(){    FILE* pFile = nullptr;    ////1. 关上一个文件,以w的形式,文件不存在就会创立一个    ////这里的文件指针,就是代表一个关上的文件    //pFile = nullptr;    //fopen_s(&pFile, "..\\debug\\abc.txt", "w");    ////2. 写入内容    //int arr[7] = { 10,20,30,40,50,60,70 };    ////这个函数,是将内存中数据写入到文件中    ////写入了4*7  也就是28个字节    //fwrite(arr, 4, 7, pFile);    ////3. 敞开文件    //fclose(pFile);    ////读取    ////1. 关上文件    pFile = nullptr;    fopen_s(&pFile, "..\\debug\\abc.txt", "r");    ////2. 读取文件    int arr[7] = { };    fread(arr, 4, 7, pFile);    ////3. 敞开文件    //fclose(pFile);}

3.敞开文件-fclose

2.4 关上文件的模式 加不加 b的问题


所有的关上的模式,都能够增加一个b,比方:wb rb ab w+b r+b a+b。
有了这个b的模式,叫做二进制模式,没有b的就是文本模式。
文本模式,写入的时候,遇到了0A 就会转为0D 0A 。读取的时候遇到了 0D 0A 就会转换为0A读取进来。
文本模式,遇到了0A转换为0D 0A 如下:

因为在windows平台,\n的asc码 是 0A ,然而只有\n不能换行,要显示换行 须要是 \r \n 就是0D 0A。
如果是二进制模式,那么就不会转换

惟一的区别就在于会进行渺小的转换。
肯定要成对应用,写入的时候 加了b ,读取的时候也应该加

4.补充函数:

                  fseek                  ftell

有一个文件读写地位。在读取或者写入时会主动设置这个地位。
比方一个文件有100个字节。刚刚关上的时候,地位是在0这个中央,读取了3个字节,地位就会往后调整3个字节。
fseek 的作用:设置读写地位

#include <stdio.h>int main(){    FILE* pFile = nullptr;    ////1. 关上一个文件,以w的形式,文件不存在就会创立一个    ////这里的文件指针,就是代表一个关上的文件    //pFile = nullptr;    //fopen_s(&pFile, "..\\debug\\abc.txt", "wb");    ////2. 写入内容    //fputs("Hello world", pFile);    ////3. 敞开文件    //fclose(pFile);    fopen_s(&pFile, "..\\debug\\abc.txt", "rb");    char cCh = fgetc(pFile);    cCh = fgetc(pFile);    cCh = fgetc(pFile);    cCh = fgetc(pFile);    //上面这个函数,可能调整读写的地位    //SEEK_END 以结尾为一个锚点    //SEEK_SET 以开始为锚点    //SEEK_CUR 以当初的地位为锚点    fseek(pFile, 0, SEEK_SET);    cCh = fgetc(pFile);    cCh = fgetc(pFile);}

ftell的作用:是查看以后的读写的地位在哪??

#include <stdio.h>int main(){    FILE* pFile = nullptr;    ////1. 关上一个文件,以w的形式,文件不存在就会创立一个    ////这里的文件指针,就是代表一个关上的文件    //pFile = nullptr;    //fopen_s(&pFile, "..\\debug\\abc.txt", "wb");    ////2. 写入内容    //fputs("Hello world", pFile);    ////3. 敞开文件    //fclose(pFile);    fopen_s(&pFile, "..\\debug\\abc.txt", "rb");    char cCh = fgetc(pFile);    cCh = fgetc(pFile);    cCh = fgetc(pFile);    cCh = fgetc(pFile);    //fseek这个函数,可能调整读写的地位    //SEEK_END 以结尾为一个锚点    //SEEK_SET 以开始为锚点    //SEEK_CUR 以当初的地位为锚点    fseek(pFile, 0, SEEK_SET);    cCh = fgetc(pFile);    cCh = fgetc(pFile);    //ftell 通知以后的文件读写地位在哪    int nSize = ftell(pFile);    cCh = fgetc(pFile);    nSize = ftell(pFile);    //fseek和ftell配合能够获取文件大小    fseek(pFile, 0, SEEK_END);    nSize = ftell(pFile);    printf("以后文件大小就是%d字节", nSize);}

留神:
应用这些函数的时候,最好成对应用。
写入的时候如果加了 b,读取的时候也肯定要加上。
在一次的关上和敞开之间,只进行一次读写操作