****************************************************************************************************************************************************************************************************************
操作符解释:
    1,算术操作符:
        ‘+’、‘-’、‘*’、‘/’、‘%’
        %取模操作符只能用于整数,
        /除法操作符,两个操作数均是整数时为整数除法,有一个是浮点数则执行浮点数除法。
        如:int a = 6/5;//->a==1
            double b = 6/5;//->b==1.000000
            double c = 6.0/5//->c==1.200000,即实际运算与定义的储存类型无关
    2,移位操作符
        左移‘<<’、右移‘>>’
        左移操作符:
            如:   int a=2;
                int b = a << 1;//a向左移动一位,结果存入变量b中,结果b为4
            正整数2在内存中存放的补码为:                                00000000000000000000000000000010
            向左移动一位,左边的首位0去掉,右边的缺一位补0:00000000000000000000000000000100 ->4(十进制)
        右移操作符:
            右移时分为算数右移和逻辑右移。
            算数右移:右边丢弃,左边补符号位。
            逻辑右移:右边丢弃,左边补0。
            如:int a = -10;
                   int b = a >> 1;//a向右移动一位,结果存入变量b中
            正整数10在内存中的补码为11111111111111111111111111110110//若为负数则原码与补码不同。
            算术右移:             11111111111111111111111111111011 ->-5
            逻辑右移:             01111111111111111111111111111011 ->2,147,483,643
        对移位操作符移动负数位,这时C语言未定义的,不要使用。
            
    3,位操作符
        按位与‘&’、按位或‘|’、按位异或‘^’
        位解释为二进制的一位。
        &按位与,全为1时结果才为1,其他情况结果均为0
            如:int a = 3;//        00000000000000000000000000000011
                   int b = 5;//        00000000000000000000000000000101
                   int c = a & b;//    00000000000000000000000000000001->1(十进制)
            eg:找出一个整数的二进制的1的个数。
                #include <stdio.h>
                int main(){
                int a=1;
                int cnt=0;
                for(int i=0; i<31; i++){
                    if((a&1) == 1){//整数的二进制数的1的个数 
                        cnt++;
                    }
                    a=a >> 1;
                }
                printf("%d\n",cnt);
                
                return 0;
            } 
        |按位或,全为0时结果才为0,其他情况结果均为1
            如:int a = 3;//    00000000000000000000000000000011
                   int b = 5;//    00000000000000000000000000000101
                   int c = a | b;//    00000000000000000000000000000111->7(十进制)
        ^按位异或,不同为1,相同为0。一个数a与另一个数b异或两次结果仍是a本身,数a与0异或结果仍为数a。
            如:int a = 3;//    00000000000000000000000000000011
                   int b = 5;//    00000000000000000000000000000101
                   int c = a ^ b//    00000000000000000000000000000110->6(十进制)
        eg:交换两个数a=3,b=5,要求不使用第三个变量。
            #include <stdio.h>//此法适用于a,b相加的结果不超过类型范围的情况,有缺点
            int main(){
                int a=3;
                int b=5;
                a = a + b;//变量a储存a与b的和
                b = a -b;//变量b储存原来a的值
                a = a - b;//变量a储存原来b的值
            return 0;
            }
            #include <stdio.h>//此法适用于a,b相加的结果不超过类型范围的情况,有缺点
            int main(){
                int a=3;
                int b=5;
                a = a ^ b;
                b = a ^ b;//3 ^ 5 ^ 5的结果3存入到b中
                a = a ^ b;//3 ^ 5 ^ 3的结果5存入到a中
            return 0;
            };
    4,赋值操作符
        =    +=    -=    *=    /=    %=    <<=     >>=
        ‘=’是赋值,‘==’是等号。
        int  a = a + 4;//等价于a+=4;
        连续赋值:
        如:int b = a = a+1;//等价于a = a + 1; b = a;
        连续赋值不好进行调试,阅读不便。    
    
    5,单目操作符
        !(逻辑反)    -(负值)    +(正值)    ~(按位取反)         sizeof(操作数的类型长度,按字节计算)    --     ++     &(取地址)    *(间接访问操作符或解引用操作符)    (类型名)强制类型转化
        ‘i’一般用于条件判断,0的反是1,非0的反是0;
            如:
            #include <stdio.h>
            int main(){
                int flag =0;
                if(flag){
                    printf("Yes\n");//条件为假,语句不执行
                }
                if(!flag){
                    printf("YesYes\n");//条件为真,语句执行
                }
            return 0;
            };
        ‘~’对二进制位的操作,类比按位与、按位或、按位异或。最高位也参与取反。
            如:int a = 13;//00000000000000000000000000001101
                   int b = ~a; //11111111111111111111111111110010 -> b==-14;
        ‘sizeof’是单目操作符,可求内存大小。
            如:    int a =13;
                int b = sizeof(a);//结果为4,是一个int类型变量所占字节个数,也可写为int b = sizeof a; 不需要加括号
                int arr[10]={0};
                int c = sizeof(arr);//结果为40,是十个int类型变量所占字节个数
                int g = sizeof(int [10]);//结果为40,数组的类型实际上是去掉数组名(arr)之后留下的(int [10])。
            如:
            #include <stdio.h>
            int main(){
                short s = 5;
                int a = 10;
                printf("%d\n",sizeof(s = a + 2));//输出为2,short占2字节,等价于printf("%d\n", sizeof(s));。
                printf("%d\n",s);//输出为5,上面表达式没有计算
            return 0;
            };
        ‘++’
            int a = 13;
            ++a;//前置++,先++在使用
            a++;//后置++,先使用,在++
        ‘&’取地址操作符,单目操作符,与按位与操作符区分
        ‘*’解引用操作符或间接引用操作符
           如:#include <stdio.h>
            int main(){
                int a =13;
                int *p = NULL;//定义int类型的指针变量,此时 * 是定义
                p = &a;//整型指针变量p中存放整型变量a的地址
                printf("%d\n",*p);//此句输出为13,*p与a等价。此时 * 是解引用
                int a =1;
                char b=1;
                double c=1;
                float d=1;
                int *pa =&a;
                char *pb =&b;
                double *pc=&c;
                float *pd=&d;
                printf("%d\n",sizeof(pa));
                printf("%d\n",sizeof(pb));
                printf("%d\n",sizeof(pc));
                printf("%d\n",sizeof(pd));//输出相同,为4或8(与具体系统有关),求的是指针变量的大小,不论是那种类型的指针变量,储存的地址形式都相同,内存的地址(指针常量)形式是确定的
            }
            
        ‘(类型名)’,
           如:#include <stdio.h>
            int main(){
                int a = 3.14;//浮点型转换为int型会损失精度,产生警告。
                int b =(int)3.14;//强制类型转换,没有警告。
            return 0;
            }
    6,关系操作符
        =(赋值)    ==(相等)    <、>、<=、>=、!=(不等于)
        字符串比较不能用==,要用字符串函数
    7,逻辑操作符
        &&(逻辑与)        ||(逻辑或)
        首先和按位与(&)、按位或(|)区分开。
        逻辑与:全真为1,其它为0。
        逻辑或:全0为0,其它为1。
        短路现象:对于逻辑与:
        如:    #include <stdio.h>
            int main(){
                int a=0,b=3,c=2,d=4;
                if(a && b && c && d)//先看a && b,a==0,则最终表达式结果一定为0,后面表达式不再计算
                    printf("111\n");
                else{
                    printf("000\n");//最后输出为000
                }
            }
        对于逻辑或:
        如:    #include <stdio.h>
            int main(){
                int a=5,b=3,c=2,d=4;
                if(a || b || c || d)//先看a || b,a == 5,则最终表达式结果一定为1,即为真,后面表达式不再计算,输出为111。
                    printf("111\n");
                else{
                    printf("000\n");
                }
            }
        
    8,条件操作符
        ? : (三目操作符)
        如:#include <stdio.h>
            int main(){
                int a =2;
                int b = 4;
                printf("%d\n", a>b?a:b);//如果a>b表达式成立,则表达式结果为a,否则为b,输出为4。
            return 0;
            }
    9,逗号表达式
        逗号表达式,从左向右依次计算,整个表达式的值是最后一个子表达式的值。
        #include <stdio.h>
            int main(){
                int a =2;
                int b = 4;
                printf("%d\n", (a=a+3,b=b-2));//表达式a = a + 3计算,但整个表达式(a=a+3,b=b-2)的值是表达式(b=b-2)的值,结果为2    
                b = 4;
                printf("%d\n", b=b-2);//结果为2
            }
    10,下标引用、函数调用和结构成员访问
        [ ]下标引用操作符(运算符)
            操作数:一个数组名+索引值,类比二元操作符,1 + 2。
            如:    #include <stdio.h>
                int main(){
                    int a[10];//创建数组
                    a[0] = 10;//使用下标引用操作符为数组第一个单元赋值
                }
        ( )函数调用操作符,接受一个或多个操作数,第一个操作数是函数名,剩余的操作数是函数的参数。
        如:    #include <stdio.h>
            int max(int a, int b){//函数定义
                return a > b ? a : b;
            }
            int main(){
                int a = 3;
                int b = 4;
                max(a, b);//函数调用,输出二者中较大的那个数。
                printf("%d\n",t);
                return 0;
            }
        结构成员访问操作符:对于非指针变量使用圆点操作符,指针变量还可以使用箭头操作符
        如:    
            #include <stdio.h>
            struct student{
                char name[20];
                int age;
                double mathscore;
            };
            int main(){
                struct student a = { "weihe", 20, 90};
                printf("%s\n",a.name);//使用圆点操作符访问结构体成员name数组
            
                struct student *p = NULL;
                p = &a;
                printf("%d\n", (*p).age);//使用指针解引用访问结构体成员age
                printf("%d\n", p->age);//使用箭头操作符访问结构体成员age
            return 0;
            }
    11,表达式求值
        求值顺序由操作符的优先级和结合性决定。有些表达式的操作数再求值的时候可能要转换为其他类型。
    12,隐式类型转换
        C的整型算术运算是以缺省(sheng)整形类型的精度来进行的。
        为了达到这个缺省(sheng)整形类型的精度,表达式中的字符和短整型操作数(2字节)在使用之前被转换为普通整型(int或unsigned int),这种转换称为整形提升。
        整形提升的意义:
            表达式的整型运算在CPU相应的运算器件内执行,CPU内整型运算器的操作数的字节长度一般是int的字节长度,同时也是CPU的通用寄存器的长度。
            即使是两个字符型变量相加,CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
            通用CPU难以直接实现两个8bit字节的直接相加运算(虽然机器指令中可能有这种字节相加指令)。
        整形提升过程:
            按照变量数据类型的符号位提升。
            正数:    char a = 1;//00000001(补码) ->(整形提升) 00000000000000000000000000000001
            负数:    char b = -1//11111111 (补码)-> (整形提升)11111111111111111111111111111111
            如:    #include <stdio.h>
                int main(){
                char a = 3;        
                char b = 127;
                char c = a + b;//    a: 00000011 -> 00000000000000000000000000000011
                           //    b: 01111111 -> 00000000000000000000000001111111
                            c:              00000000000000000000000010000010 ->(截断) 10000010
                printf("%d\n", c);/    c: 10000010 -> 11111111111111111111111110000010(反码)->(求原码)10000000000000000000000001111110 即为-126
                return 0;//以int型输出是也会发生整型提升,对于unsigned char,整型提升时高位均补0
                }
                
                #include <stdio.h>
                int main(){
                    char c =0;//sizeof返回类型为size _t,是unsigned int,打印时要用格式符%u
                printf("%u\n",sizeof(c));结果为1
                printf("%u\n",sizeof(-c));结果为4,sizeof运算符括号里的表达式(-c)虽然不参与运算,但有类型属性,为整形提升后的int
                printf("%u\n",sizeof(+c));结果为4
                printf("%u\n",sizeof(!c));结果为4
                return 0;
                }
            
****************************************************************************************************************************************************************************************************************
字符串
相关函数
头文件#include <string.h>
求长度
    char s[10] = "hello world";
    strlen(s),返回字符的个数
    sizeof(s),返回字符串大小,包括'\0'
复制函数
    char s1[10];//字符数组
    char s2[10] = "hello";
    strcpy(s1,s2),把字符串s2整体复制到s1中,但s1中要有足够的内存
    strncpy(s1,s2,n),对s2最多n个字符成立
比较函数
    strcmp(s1,s2),相等返回0,s1>s2返回正整数,s1<s2返回负整数
    strncmp(s1,s2,n),字符串s1中最多n个字符与字符串s2比较,同上
连接函数
    char s1[10];//字符数组
    char s2[10] = "hello";
    strcat(s1,s2),把s2中字符添加到s1中有效字符之后,s1要有足够的内存
    strncat(s1,s2,n),对s2最多n个字符成立
逆置函数(倒序)
    char s[10] = "hello";
    strrev(s),
输出函数
    sprintf(s,格式字符串,输出项列表),输出到字符串s中,不在屏幕上显示
输入函数
    sscanf(s,格式字符串,输出项列表),从字符数组s中读取,而不是从键盘读取,

字符串数组与函数
    

****************************************************************************************************************************************************************************************************************
数组
    &arr//指数组的地址,(&arr+1)加了整个数组的长度
    arr//指数组首元素的地址,(arr+1)加了一个数组元素的长度
    arr[0]//指数组首元素的地址

二维数组的函数调用

void pow(int a[][10])//一个调用二维数组的函数
int arr[10][10];
pow(arr);//只写数组名

****************************************************************************************************************************************************************************************************************
刷新函数
memset(数组名,0,sieof(数组名));

****************************************************************************************************************************************************************************************************************

指针(间接引用)(地址)
指针常量:计算机内存中的事先实现好的编号即地址,不可改变。
指针变量:指针类型,储存地址的变量,可以改变,指针变量自身也占有一定内存。不与指针常量混淆时简称为指针。

指针的大小:
    指针在32位系统中占4字节,在64位系统中占8字节,与地址占内存的大小有关。
    而在同一系统中地址由多个相同的bit构成
变量(int,float,double,char等)占用内存中的字节,首个字节的地址是该变量的地址

初始化
    声明指针时必须对其进行初始化(为0,NULL,已定义变量地址)。
    如:
    void *p = NULL;//可以指向任何类型的指针,未知指向变量的数据类型,不能进行间接寻址运算
    int *p = NULL;//不指向任何对象
    int *p = 0;
    int a = 1;
    char *p = &a;//指向变量a的地址

间接寻址运算(运算符*,&)
    *:解引用操作符,访问指针p所指的变量a。
    &:取(返回)变量a的地址。
    int a = 1;
    int *p = &a;//此时*为指针声明符
    有*p与a等价,p与&a等价

指针的算术运算
加法运算:(得到另一个地址)
    int a[4]={4,5,3,1};
    int *p = a;//或p = &a[0];
    有(p + 1) == &a[1];.......(p + i) == &a[i];
减法运算: (得到地址之间的距离,用元素的个数表示)
    int a[4]={4,5,3,1};
    int *p = &a[0];
    int *q = &a[3];
    则p - q == 3.//结果可以为负整数,即地址相减后除以类型所占的字节
    地址之间的距离==sizeof(int)*3.
指针比较:(指向相同数组时才有意义)
    int a[4] = {5, 4, 3, 2, 1};
    int *p = &a[0];
    int *q = &a[4];
    for(; p<=q; p++){
        printf("%d\n", *p);//循环输出数组各元素
    }
指针与数组:
    int a[4];
    由数组名a可知数组a的内存大小并得到数组a的首元素的第一个字节的地址(即数组名a是指向a[0]的一个指针常量,,指针常量,不可更改)。
    int *p = a;
    int i;
    一个指针p与数组a关联之后数组a中的元素表示方法可以为:(以指针p指向数组首元素为例)
        数组下标表示: a[i];
        数组偏移量表示: *(a + i);//偏移量,即i
        指针下标表示: p[i];
        指针偏移量表示: *(p+i);
    数组作为函数的参数: 
        形参数组(定义的函数中,非main函数)本质上是一个相应类型的指针变量,不是指针常量.
        例如
            int a[4];//实参
            函数头void sum(int a[], int n)//形参a[]获得了数组的首元素的指针(地址),可以访问和修改数组a[4]中的所有元素
            与void sum(int *a, int n)等价
指针与const限定符: (减小函数的在主函数的权限等)
    const修饰后,为常量,不可修改。
    定义函数时
    声明为从const的指针要在声明时就初始化,若为定义的函数参数则由相应主函数的实参初始化。
    可变指针指向可变(可修改的)数据:
        void sum(char *p,int n)//权限最大,无const约束指针或类型数据(char).
    可变指针指向常量(不可修改的)数据:
        void sum(const char * p, int n)//应从右往左理解const char * p,p是一个指针,指向字符常量,可进行指针运算。
    常量指针指向可变数据: 
        void sum (char const * p, int n)//从右往左char const * p,p是一个指针常量,指向字符型数据,可改变指针指向数据的值。
    常量指针指向常量数据: 
        void sum(const char const * p,int n)//权限最小,指针(地址)的值不能改变,其指向的数据也不能改变

指针数组:是一个数组,数组元素时一个个指针变量
    char* arr[4];//一级字符指针数组
    char** arr[4];//二级字符指针数组
    如:    #include <stdio.h>
        int main() {
            int a = 10;
            int b = 20;
            int c = 30;
            int *arr[3] = { &a,&b,&c };//注意[ ]优先级比*高
            int i = 0;
            for(i=0; i<3; i++){
                printf("%d\n", *arr[i]);
            }
    
            return 0;
            }

        #include <stdio.h>
        int main() {
        int a[5] = { 1,2,3,4,5 };
        int b[] = { 1,3,5,7,89 };
        int c[] = { 2,3,4,5,6 };
        int* arr[3] = { a,b,c };//先是一个数组,数组中的元素为int*,即整型指针。
        
        int i = 0;
        for (i = 0; i < 3; i++) {
            int j = 0;
            for (j = 0; j < 5; j++) {
                //printf("%d ", *(arr[i] + j));//arr[i]是指针数组第一个元素储存的地址即a数组首元素的地址,(arr[i]+j)是a数组中第j个元素的地址
                printf("%d ", arr[i][j]);//等价于二维数组
            }
            printf("\n");
        }
        return 0;
        }
数组指针:
如:    #include <stdio.h>
    int main() {
        int a = 10;
        int* pa = &a;//整型指针
        char ch = 'w';
        char* pch = &ch;//字符指针
    
        int arr[10] = { 0 };
        int (*parr)[10] = &arr;//取出的是数组arr的地址,不是数组首元素的地址。先是是一个指针,再指向具有10个元素的数组,数组中的元素为int。
    
        double* b[5];//指针数组
        double* (*p)[5] = &b;//数组指针,即指向指针数组的指针。先是一个指针,再指向具有5个元素的数组,数组中的元素为double*。
    
        return 0;
    }
    (&数组名)与数组名(首元素的地址)区别:
        #include <stdio.h>
        int main() {
            int arr[10] = { 0 };
            
            printf("%p\n", arr);
            printf("%p\n", &arr);//输出arr与&arr地址的值相同,意义不同(类型不同)。
            //
            int* pa = arr;//类型为int
            int(*pb)[10] = &arr;//类型为int [10]或int []。
            printf("%p\n", pa);//指针加一跳过指向对象的大小。
            printf("%p\n", pa+1);//指针加一跳过一个int大小,为4字节。
            printf("%p\n", pb);
            printf("%p\n", pb+1);//指针加一跳过一个int [10]大小,为40字节。
            return 0;
        }
        数组名是首元素的地址,但有两个例外:
            1:sizeof(数组名)    -    数组名表示整个数组,计算的是整个数组大小,单位是字节。
            2:&数组名 - 数组名表示整个数组,取出的是整个数组的地址。
数组指针的使用:数组指针中存放数组的地址
   如:用于一维数组,一般不使用数组指针,使用一级指针就可以了,使用数组指针反而麻烦。
    #include <stdio.h>
    int main() {
        int arr[5] = { 1,2,3,4,5 };
        int(*pa)[5] = &arr;//指向含有五个整型元素数组的指针,储存的是整个数组的地址
        for (int i = 0; i < 5; i++) {
            printf("%d " ,*((*pa) + i));
            //首先*pa是数组arr,而数组arr是数组首元素的地址,故*pa是数组首元素的地址
            //接着*pa+j是数组中下标为j的元素的地址,*((*pa)+j)是下标为j的元素
        }
        return 0;
    }
    用于二维数组
    #include <stdio.h>
    //void print(int arr[3][4], int m, int n) {//常规写法
    //    for (int i = 0; i < m; i++) {
    //        for (int j = 0; j < n; j++) {
    //            printf("%d ", arr[i][j]);
    //        }
    //        printf("\n");
    //    }
    
    
    void print2(int(*pa)[4],int m, int n) {//定义了指向一维数组的指针,
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            printf("%d ", *(*(pa + i) + j));
            //*(pa+i)首先是二维数组第i-1行的地址,*(pa+i)+j是第i-1行的第j-1个元素的地址
            //*(*(pa+i)+j)是第i-1行的第j-1个元素
        }
        printf("\n");
     }
    }
    int main() {
        int arr[3][4] = { {1,2,3,0},{4,5,6,0},{7,8,9,0} };
        //print1(arr, 3, 4);
        print2(arr, 3, 4);
    
        return 0;
    }
    
    int arr[5];//一维整型数组
    int a[3][4];//二维整型数组
    int* parr1[10];//指针数组,该指针指向存放10个整型指针的数组
    int(*parr2)[10];//数组指针,指向存放10个整型元素的数组
    int(*parr3[10])[5];//存放10个数组指针的数组,每个数组指针指向存放5个整型变量的数组
    
    
    
函数参数传递方式:按值传递和按引用传递
C中均为按值传递:被调函数的形参为主调函数的副本(拷贝),一般不影响主调函数变量的值。当传入指针时(类似按引用传递),主调函数
向被调函数传入了地址,可以改变主调函数中的相应变量的值。
如:void swap(int *p, int *q)//函数定义
      int a=5, b=6;
      swap(&a, &b);//函数调用

    数组参数与指针参数
        一维数组传参:
        #include <stdio.h>
        void test1(int arr[10]) {//1一维数组接收,可以
        }
        void test1(int arr[]) {//1一维数组接收,可以
        }
        void test1(int* arr) {//1一级指针接收可以
        }
        void test2(int* pa[10]) {//2一维指针数组接收,可以
        }
        void test2(int* pa[]) {//2一维指针数组接收,可以
        }
        void test2(int** pa) {//2可以
        }
        
        int main() {
            int arr1[10] = {0};
            int* arr2[20] = {0};
            test1(arr1);//传的是首元素的地址,此指第一个元素int的地址
            test2(arr2);//此指第一个元素int*的地址,即是二级指针
            return 0;
        }
        
        二维数组传参:
        #include <stdio.h>
        void test3(int arr[3][4]) {//3二维数组接收,可以
        }
        void test3(int arr[][4]) {//3二维数组接收,可以
        }
        void test3(int arr[][]) {//3二维数组接收,列元素必须写,不可以
        }
        
        void test3(int* arr) {//一级指针接收,类型不匹配,不可以
        }
        void test3(int* arr[4]) {//一维指针数组接收,类型不匹配,不可以
        }
        void test3(int(*pa)[4]) {//一个数组指针接收,可以
        }
        void test3(int** arr) {//二级指针接收,与一维数组的地址不匹配,不可以
        }
        int main() {
            int arr[3][4] = { 0 };
            test3(arr);//传的是二维数组的首元素的地址,此指第一个元素一维数组的地址
            return 0;
        }

函数指针
指针函数
****************************************************************************************************************************************************************************************************************

结构体(构造类型)
    struct关键字用于结构体定义。
    结构是同一名字下的一组相关变量的集合。可以包含不同的数据类型。(当然包括结构类型)
    结构体定义(声明)形式:
        struct 结构标识符{                如:struct student{
            数据类型 成员1;                       char name[20];
            数据类型 成员2;                int age;
            ......                        double mathscore;
        };
                                       };
        定义之后,struct student便成为了自己定义的新的一种数据类型,与char、int、float、double使用基本相同。编译器不对数据类型分配内存,
        当定义了相应类型的变量时编译器会为该类型的变量分配内存。自定义结构类型也是这样。
        typedef关键字:为任意数据类型起一个别名,便于使用和理解。
            关于结构体使用:
            如:typedef struct student STU,即STU成为struct student 数据类型的另一个名字。
            或如:typedef struct student{
            char name[20];
            int age;
            double mathscore;
            }STU;此STU不是struct student类型的结构变量,而是struct student 数据类型的另一个名字。
    结构变量的声明(定义):
        a.先声明结构类型,在定义结构变量。
            如:struct student a;
        b.定义结构类型的同时,定义结构变量。(此时结构标识符可以省略,且只能在此处定义结构变量,少使用)
            如:struct student{
            char name[20];
            int age;
            double mathscore;
            }a;
        结构变量的内存用sizeof运算符计算出,一般大于结构成员所占字节的和。
        结构类型可以作为另一个结构的成员:
            struct date{
                int year;
                int month;
                int day;
            }
            struct student{
            char name[20];
            int age;
            double mathscore;
            struct date born;
            };
    结构变量的初始化:
        与普通变量一样,不同成员用逗号隔开,字符串用双引号,单个字符用单引号。
        如:struct student a = { "weihe", 20, 90};
    结构成员的访问方法:C规定结构变量不能整体进行输入输出比较等操作,只能对具体的成员进行输入输出比较等操作。
                   相同类型的结构变量可以直接进行赋值操作,等价于结构变量成员依次进行赋值操作。
        使用成员选择运算符(圆点运算符):
            struct date{
                int year;
                int month;
                int day;
            }
            struct student{
            char name[20];
            int age;
            double mathscore;
            struct date born;
            };
            struct student a = { "weihe", 20, 90, {2000,4,24}};
            a.name == "weihe";//成员引用
            a.age == 20;
            a.date.year == 2000;//嵌套结构体的级联引用
    结构数组:数组中元素均为结构类型。(定义和初始化)与普通数组相同。
        结构指针:指向结构的指针。
        成员访问运算符对于指针:圆点运算符和箭头运算符
        如:struct student{
            char name[20];
            int age;
            double mathscore;
            };
            struct student a[20]={ {"weihe",20,90},
                             {"齐天大圣",1000,95}};//初始化,未初始化的赋值为0。
            struct student b;
            a[4].age = 20;//对结构数组第5个元素中的一个变量赋值(引用)。
            struct student *p = NULL;//定义结构指针。
            p = &b;//得到结构类型b的地址用用取地址运算符。
            p = a;//数组名就是数组的地址故不需要取地址运算符,等价于 p = &a[0];.
            (*p).mathscore = 89;//同时使用解引用运算符与圆点运算符,等价于a[0].mathscore = 89;
            *(p+1).mathscore = 79;//同时使用解引用运算符与圆点运算符,等价于a[1].mathscore = 79;,指针+1则跳过相应类型的一个元素,再此为一个结构元素。
            p->mathscore = 99;//箭头运算符,等价于 a[0].mathscore = 99;
            当p定义为指向结构类型的指针时就只能指向定义的类型了,不能指向结构类型中的成员,
            但可以通过成员访问运算符(圆点或箭头)访问成员。对于结构指针有两种(圆点和箭头),对于结构名只有圆点运算符。
    结构与函数:
            struct student{
            char name[20];
            int age;
            double mathscore;
            };
            void max(int n, struct student a);//结构作为函数的参数,是按值传递,在max函数新建一个结构体(形参),不影响main函数里传参的的结构体(实参)。不常用。
            void max(int n,struct student *a);//结构指针作函数参数,传入的是main函数里实参(再此为结构体)的地址,影响实参数值。常用。
            void max(int n, struct student a[]);//结构数组作函数参数,与结构指针作用相同,传入的是数组的首元素字节的首地址。
            struct student max(int n);//结构作函数返回值,max函数最终返回一个结构(其中一般包含多个结构成员,可一次性返回),类似返回其他类型数据。
    联合:C语言中多个不同变量共享同一内存区的功能。
        关键字:union
        定义:(类比结构)
            union student {//该联合的三个成员共享一个声明联合的内存单元,须有,同一时间内有且只有一个成员有意义。不能为联合的所有成员都初始化,只能用相应类型的常量为第一个成员初始化。
                int n;//4字节
                double a;//8字节
                char ch;//1字节
            }u;//定义的同时可以声明一个联合u。
            union student u1;//也可在定义之后再声明一个联合u1。
            在为声明的一个联合u分配内存大小时,编译器按成员中最长的类型double分配储存单元。
        联合指针:
            union student {
                int n;//4字节
                double a;//8字节
                char ch;//1字节
            }u,u1;
            union student *p = NULL;
            p = &u;
            u1.n = p->n;//p->n等价于*p.n。 类比结构。圆点运算符与箭头运算符。
        联合与结构:结构可以是联合的成员,联合也可以是结构的成员,能互相嵌套。
            如:    struct yes{//有对象问名字与年龄
                    name[20];
                    int age;
                };
                struct no {//无对象问希望什么时候找与喜欢类型
                    int hopeseektime;
                    char liketype[50];
                };
                union otherhalf{定义一个联合是否有对象,成员为两个结构分别表示有或没有
                    struct yes a;
                    struct no b;
                };
                
                struct student {//定义学生的结构,包括姓名、年龄与有无对象。
                    char name[20];
                    int age;
                    union otherhalf;
                };
    枚举:一种值由程序员列出的类型,而且程序员必须为每个值命名(枚举常量)。
        关键字:enum(类比结构)
        在枚举类型声明语句中,花括号内的标识符都是整型常量,称为枚举常量。一般第一个枚举常量是0,依次增加1。
        如:    enum weekday{
                Sun;//0
                Mon;//1
                Tue;
                Wed;
                Thu;
                Fri;
                Sat;//6......
            }someday0;//定义枚举类型同时声明枚举变量someday0。
            enum weekday someday;//先定义枚举类型,再声明枚举变量someday
            someday = Mon;//枚举变量someday被定义的枚举常量赋值。
time()和localtime()
****************************************************************************************************************************************************************************************************************

int a[10];//数组定义
void array(int a[]) 等价于void array(int *a)//函数定义

动态内存分配
    void *malloc(unsigned size)//函数原型

    int *p = NULL;
    p = (int*)malloc(sizeof(int));//使用,按字节分配内存,用于数组个数不定时

动态内存分配之后的释放函数
    void free(void *p)//函数原型

    int *p = NULL;
    p = (int*)malloc(sizeof(int));
    free(p);//使用

****************************************************************************************************************************************************************************************************************

void *calloc(unsigned n,unsigned size)//函数原型
为n个数组分配size字节大小的内存,并均初始化为0

****************************************************************************************************************************************************************************************************************

void realloc(void* p,unsigned size);//函数原型
重新为指针p所指的内存块分配适合的内存
若指针p指向NULL,则作用与malloc()相同

****************************************************************************************************************************************************************************************************************
EOF(End  OF File)
表示文件结束符,用于while循环中,如while(scanf("%d",&n) != EOF),当在控制台输入ctrl+z时结束程序进行


****************************************************************************************************************************************************************************************************************

数据的存储
    程序的版本:Debug版本和Release版本
        Debug版本即调试版本,包含调试信息,利于去调试程序,无优化。
        Release版本即发布版本,对程序进行了多种优化如代码大小和运行速度,便于使用,无调试信息。
    数据类型:
        类型的意义:确定开辟空间的大小与看待内存空间的视角。
        基本类型:
        char            字符型//char C语言没有规定是signed char 还是unsigned char 类型,由具体的编译器决定,多数编译器默认为 signed char类型
        short            短整型
        int            整型//int C语言规定为signed int类型
        long            长整型
        long long        更长的整型
        float            单精度浮点型
        double        双精度浮点型
        整形家族:
            char
                unsigned char    0~255(2^8-1)
                signed char        -128~127
            short
                unsigned short int        
                signed short int
            int
                unsigned int    0~4,294,967,295(2^32-1)
                signed int(默认)    -2,147,483,648~2,147,483,647
            long
                unsigned long int
                signed long int
        浮点家族:
            float
            double
        构造类型(自定义类型):
            数组类型        a[]
            结构体类型        struct
            枚举类型        enum
            联合类型        union
        指针类型:
            int *p
            float *p
            double *p
            char *p
            void *p
        空类型:函数返回,函数参数,指针
    数据的截断与整型提升(指不同的数据类型之间    ):
        如:int a = 10;
              char ch = 10;//10的二进制:00000000 00000000 00000000 00001010 最高位为0,原码反码补码相同
        10为整型,占内存的四个字节,由字符类型(一个字节)ch储存时发生截断,只保留最后一个字节(00001010)储存。
        ch转化为整型时从前补24个最高位即0,得补码00000000 00000000 00000000 00001010,最高位为0原码反码补码相同,即得原码00000000 00000000 00000000 00001010即10。
    堆栈
    局部变量储存在栈区,栈区地址从下到上逐渐增大,即上为高地址,下为低地址。栈区先使用高地址,在使用低地址
    整型在内存中的储存:
        已知数据在内存中以二进制储存,
        有符号数的二进制表示方式有三种:原码,反码,补码。,每一种均分为符号位和数值位。
        对于正整数:原码,反码,补码三者相同。
        对于负整数:
            数据储存时的二进制序列最高位为1,而正整数二进制序列最高位为0。
            对于signed前缀类型,最高位为符号位无数值表示,而unsigned前缀类型最高位为数值表示。
            原码:由数据的数值直接写出的二进制序列,补码取反再加一又得到原码或补码减去1再取反得到原码。
            反码:原码符号位(即1)不变,其它位按位取反。
            补码:先取反码再+1得到。
        如    int a = -10;
            内存中存放(16进制): F6 FF FF FF(此储存为小端字节序)
            原码:10000000 00000000 00000000 00001010(四个字节)
            反码:11111111 11111111 11111111 11110101
            补码:11111111 11111111 11111111 11110110
            16进制:FF FF FF F6
            
            int b = 10
            内存中存放(16进制):0a 00 00 00(此储存为小端字节序)
            原码:00000000 00000000 00000000 00001010
            反码:00000000 00000000 00000000 00001010
            补码:00000000 00000000 00000000 00001010
            16进制:00 00 00 0a
        整型在内存中以补码的形式储存,表示和计算,原因是可以将符号位和数值域统一处理,加法和减法可以统一处理(cpu中只有加法器),原码与补码相互转换运算过程相同,不需要额外硬件电路。    
        数据数值的二进制的储存形式:大端字节序和小端字节序
            如    int a = 0x11223344;//(16进制表示)其中11为高位,44为低位,类比十进制数字
            大端字节序:把数据的低位字节序的内容存放在高地址处,高位字节序的内容存放在低地址处。
                低地址    11 22 33 44        高地址
            小端字节序:把数据的地位字节序的内容存放在低地址处,高位字节序的内容存放在高地址处。
                低地址    44 33 22 11        高地址
            想知道自己计算机数据的储存形式,可以采用如下代码简单判断:
                用字符指针(只看一个字节)指向整型a的第一个字节,若为小端则为(16进制)01 00 00 00,若为大端则为 00 00 00 01。
                #include <stdio.h>

                int main(){
                int a=1;
                char *p=(char*)&a;
    
                if(*p==1)
                    printf("小端\n");
                else
                    printf("大端\n");
                return 0;
                } 
        浮点型在内存中的存储:float(四字节),double(八字节),long double
            浮点数在内存中以补码的形式储存。
            C语言用二进制储存浮点数的标准:IEEE 754。
                任意一个二进制浮点数V均可以化为一个形式:
                (-1)^S*M*2^E    其中 ^ 表示指数运算。
                S表示符号位,S为0时V为正数,S为1时V为负数。
                M表示有效数字,范围大于等于1,小于2。
                2^E表示指数位。
                        例如:9.0(十进制)->1001.0(二进制)->1.001*2^3->0 10000010 00100000000000000000000
                            5.5(十进制)->101.1(二进制)->1.011*2^2->  0 10000001 01100000000000000000000
                对于double类型:0 00000000000 0000000000000000000000000000000000000000000000000000 首位为符号位,接下来11位存放指数,余下52位存放小数部分。
                对于float而言共四个字节32位:0 00000000 00000000000000000000000 首位1或0表示符号位,接着8位存放E即指数(二进制形式),余下的23位储存M(又称小数部分),M不够就补0.
                    对于1.XXXXXXXXX的二进制有效数字,实际储存时舍去1和小数点,只保留小数点后的0和1,等到取出已经储存好的浮点数时,M部分取出时在开头添加上1和小数点。
                    对于E指数,看做无符号整型(unsigned int),float中占8位,范围为0~255,考虑到指数E可能为负数,实际储存时要存入正数就需要修正,为指数E加上一个中间值,
                    127(float)或1023(double)。实际储存的是(E+127)的二进制形式,double占11位范围为0~2047,实际储存的是(E+1023)的二进制形式。
                取出时:
                    E全为0时:
                        这时浮点数的指数E等于(1-127)或(1-1023)作为真实值,有效数字M不在加上第一位的1,而是还原为0.xxxxxx的小数,以此表示正负0或接近于0很小的数。
                        00000000八个位为0 ->实际的E的值为0 - 127 == -127,2^(-127)是很小的一个数,正负接近于0。
                    E全为1时:
                        这时,如果有效数字M全为0(还原之后为1.000.....),表示无穷大,符号确定正负。
                        11111111八个位为1 -> 实际的值为255 - 127 == 128,2^(128)是很大的数字 ,表示无穷大。
                    E不为全0或全1时:(最多) 
                        指数E二进制的计算值减去127(或1023)得到指数位真实值,有效数字M前加上第一位的1,
                        5.5(十进制)->101.1(二进制)->1.011*2^2->  0 10000001 01100000000000000000000
                    
***************************************************************************************************************************************************************************************************************