美文网首页
C语言笔记

C语言笔记

作者: yaoyao_IOS | 来源:发表于2020-06-28 09:09 被阅读0次
     C语言
     - 编译:
     将.c源文件中的代码编译为对应的二进制指令。
     `cc -c .c文件名`
     如果一切正常的情况下,会生成一个.o文件,叫做目标文件
     这个目标文件存储的是.c文件对应的二进制代码
     
     >注意:编译器在编译的时候,先检查拟定的源文件中的代码是否符合C语言的语法规范,如果符合才能生成目标文件,如果不符合就会报错,不会生成目标文件
     
     - 链接:
     目标文件无法交给CPU直接执行,因为.o文件中只是存储的.c的二进制代码,一个可以被执行的文件中必须还要有执行代码才可以。链接的事情之一:为.o目标文件添加启动代码
     
     `cc .o文件名`
     如果一切正常的情况下,会生成一个a.out文件,是我们最终编写的程序,可以直接执行
     如果程序中使用到了框架中的函数或者类.那么在链接的时候,就必须要告诉编译器 去那1个框架中找这个函数或者类.
     `cc xx.o -framework 框架名称.`
     
     - 执行
     `./a.out`
     
     
     补充: 
    1. 返回值代表程序的结束状态.
         0 代表正常结束 非0代表非正常结束.
     
    2. 参数 
         argv数组的第0个元素的值 是这个正在运行的程序的路径.
         argc代表的是数组的长度.
        
    3. 你要清楚一件事情.
         我们写的程序可以在终端中运行. 运行的时候可以为程序传递一些数据.
         传递的方式:
     
         程序名 数据1 数据2 数据3 .....
     
         在程序的内部如何拿到.
     
         argc代表用户传递的数据的个数.
         argv数组中每1个元素存储的就是用户传递过来的数据.
     
         第0个元素是程序的路径.
     
     
     
     - 数据类型
     printf高级用法:
     %md    位数不足m位,空格补足
     %0md   位数不足m位,0补足
     %.nf   四舍五入,保留n位小数,0补足(float)
     %.nlf  四舍五入,保留n位小数,0补足(double)
     
     
     赋值数据类型与变量声明类型不同,则会自动类型转换:
     
     当变量的类型为int的时候:
     1).赋值超出了int的范围,C系统会将数据转换为一个随机的int数据
     2).赋值超出了int的范围太多,此时自动类型转换无能为力,编译器会直接报语法错误
     3).赋值的数据类型是一个实型的小数,C系统会直接截取整数部分
     
     当变量的类型为float的时候:
     float a = 12.f;
     1).赋值的数据是一个double类型的小数,C系统会将这个double类型的小数转换为float
     1).赋值的数据是一个int类型的整数,C系统会将这个int类型的整数转换为float小数,直接加一个.0就可以
     
     
     当变量的类型为double的时候:
     double a = 12.0;
     1).赋值的数据是一个float类型的小数,C系统会将这个float类型的小数转换为double,占据8个字节
     1).赋值的数据是一个int类型的整数,C系统会将这个int类型的整数转换为double小数,直接加一个.0就可以
     
     
     当变量的类型为char的时候:
     char a = 'a';
     ASCII码: 每一个字符数据都有一个与之对应的整数, A-65 a-97 0-48
     
     为char变量赋值的时候,可以直接赋值ASCII码
     char a = 97;
     当我们为char变量赋值一个整数的时候,其实赋值的是以这个整数位ASCII码所对应的字符数据
     
     
     - scanf函数的使用:
     scanf("格式控制符", 变量地址列表);
     步骤:
     a.在格式控制符中使用占位符来要求用户输入一个指定类型的数据
     b.在后面写上要将用户输入的数据存储在哪个变量的地址
       使用&就可以获取到变量的地址
     
     int num = 0;
     scanf("%d", &num);
     
     scanf函数的执行原理:
     scanf函数是一个阻塞式的函数,当CPU执行到这个scanf函数的时候,CPU的执行就会暂停,不会继续往下执行了,并等待用户输入数据,当用户s输入完毕数据,按下回车键表示输入完毕,此时,就回将用户输入的数据赋值给后面指定的变量,然后再继续往下执行
     
     // 注意:
     1).scanf函数是输入不是输出,所以不要在后面加\n
     2).scanf函数后面的参数是写变量的地址 而不是变量,使用&取变量的地址
     3).如果程序正在运行,想要重新运行程序 要先将这个正在运行的程序停止
     4).存储用户输入的数据的变量的类型要和scanf函数的占位符对应
     
     
     用户在输入多个数据的时候,默认的分隔符号是空格或者回车
     自定义分隔符:
     在格式控制字符串中可以自定义多个输入数据的分隔符.
     scanf("%d-%d-%d", &num, &boyNum, &avg);
     代表输入3个数据,这三个数据用-分隔开
     
     注意: 一旦指定了分隔符,那就必须使用指定的分隔符,空格回车则无效;一次输入的多个数据,只能是数(整型和浮点型),如果有char混合输入,就会出问题
     
     数据输入完成后,并不是将这个数据直接赋值给变量,而是存入缓冲区
     在执行scanf函数的时候,会先检查缓冲区中是否有数据,如果缓冲区中没有数据,那么就会让用户输入数据
     
     当从缓冲区中拿数据的时候,如果要拿到的数据类型是整型或者实型,如果拿到的是空格 回车 Tab键 就会被自动忽略,继续往下拿
     
     如果拿到的数据类型是字符型,不会忽略任何数据.
     所以,当数字和字符混合输入的时候,字符的接收就有可能会出问题
     解决方案: 在输入字符之前,将缓冲区中的数据全部清空
    
     char color = "a";
     rewind(stdin); // 将缓冲区中的数据全部清空
     scanf("%c", &color);
     
     
     
     - 算术运算符
     1).算术表达式都有一个结果,一般处理方式是声明一个变量将这个表达式的结果存储起来
     我们必须知道算术表达式的结果的类型,只有知道类型才可以声明一个对应的类型的变量来保存这个结果.
     2).如果参与算术表达式的操作数的类型都是一致的,那么这个算术表达式的结果的类型就是这个l类型
        10/4 记住,这个算术表达式的操作数都是int的,所以这个表达式的结果是2,不是2.5 ,所以正确的方式是使用int变量保存结果
        如果为了得到结果是2.5, 可以将任意一个操作数的类型改为double(或乘以1.0)
     
        10%3(求模运算)
        可以用来判断1个数是否为另外一个数的倍数,或1个数能否被另一个数整除
        实型数据无法参与求模运算,因为没有意义
        m%n的结果一定在0-(n-1)之间
     
     3).如果参与算术表达式的操作数的类型不一致,那么这个算术表达式的结果的类型就是范围最大的那个类型
     int < float < double
     
     4).当表达式中的操作数是一个char数据的时候,会先将这个char数据的ASCII码找出来代替,然后参与算术运算
     
     
     - 自增自减运算
     ++i 先运算再赋值   i++ 先赋值再运算
     --i              I--
     
     
     - 逗号表达式
     int num = (i++,j++,++k,i+j+k);
     从头到尾的去执行每一个子表达式,最后一个子表达式的结果就是整个表达式的结果
     
     
     - 比较表达式
     使用int类型的数据来表示真假
     0代表假 非0代表真
     
     - 逻辑运算符
     
     断路问题:
     int i = 0;
     int res = i++ > 0 && ++i < 3
     printf("%d", i);
     打印结果 i=2 ????
     逻辑表达式在执行的时候,是先计算左边的条件表达式的结果,再计算右边的表达式的结果
     
     *****当是&&的时候,如果左边的不成立,则可以确定整个逻辑表达式的结果为0,而这个时候,右边的条件根本就不会去判断了,所以右边的代码也不会再执行
     *****当是||的时候,如果左边的成立,则可以确定整个逻辑表达式的结果为1,而这个时候,右边的条件根本就不会去判断了,所以右边的代码也不会再执行
    
     
     优先级:
     ! > && > ||
     */
    
    
    - 函数
     
     - goto函数; 不建议使用,因为它不安全,容易造成死循环,除非在特别确定的情况下不会造成d死循环,就可以使用
       且只能在当前函数中跳转
       取标签名下面的代码不能是声明代码,如果非要声明的话,可以先写个printf函数
     
     loop:(标签名:)
        printf("sss");
     
     goto loop;
     
     
     - 全局变量:
     如果全局变量的类型是char类型,并且也没有初始化,那么系统就会自动给这个char变量赋值'\0'
     \0 代表一个不可见的字符,这个字符的ASCII码就是0
     
     
     全局变量与局部变量的异同点:
     同: 都是变量,都是在内存中开辟一块空间来存储数据
     异:1.声明的位置不同(函数内部与外部) 2.作用域不同
     3.默认值不同
     局部:默认值是一个垃圾数,是个随机数
     全局:默认值为0
     4.创建和回收的时间不同
     
     
     1. 预处理指令/预处理代码
        C语言的代码主要分为两类,
        1).C代码 之前学习的代码都叫C代码
        2).预处理代码   在编译之前执行的  以#开头的代码叫预处理代码
     2. 手写一个C程序的步骤
        1).在.c的源文件中写上符合C语言规范的源代码
        2).编译  使用cc -c 指令将C语言的源代码编译为.o的目标文件
           a.先检查源文件中的代码是否符合语法规范
            YES 生成目标文件   NO 报错
        3).链接  使用cc 指令 将目标文件链接生成一个可执行文件
           a.为目标文件添加启动代码
        4).执行可执行文件
     
      .c源文件 ->执行.c文件中的预处理代码 ->检查语法 -> 编译成.o目标文件 ->链接生成可执行文件 ->执行
    
     3.预处理指令
     1).分类:
     a.文件包含指令 #include
     b.宏定义      #define
     c.条件编译指令 #if
     
     2).特点:
     a.都是以#开头
     b.预处理指令的后面没有分号
     c.在编译的时候,在检查语法之间执行
     
     
     4. 文件包含指令 #include
     1).作用:将指定的文件的内容拷贝到指令的地方
     2).语法:
     #include "文件路径"  直接目录查找
     #include <文件路径>  从编译器目录查找
    
     
     - int num = 11;
     if ((num & 1) == 0) {
     num是偶数
     }
     
     
     - 数组:
     1).特点:
     a.可以存储多个数组
     b.存储的多个数据的类型相同
     c.长度固定
     d.存储在数据中的数据方便管理
     
     2).语法:
     元素类型 数组名[长度];
     int arr[2];
     arr的类型是int数组
     
     3).存储/取出 数据,使用下标
     4).遍历
     5).注意点:
     a.数组的长度
     b.数组元素的默认值 0
     c.初始化
     int arr[] = {10,293,10};
     int arr[] = {0};
     当声明数组的同时使用大括弧初始化数组的时候 数组的长度不能是一个变量
     
     3.数组在内存中的存储
     
     数组的地址
     数组名 == 数组地址 == 数组的低字节的地址 == 数组的第0个元素的地址 == 数组的第0个元素的低字节地址
     
     数组的长度
     sizeof(数组名) / 每一个元素占用的字节数
     sizeof(arr) / sizeof(arr[0])
     
     4.数组与函数
     当数组作为函数的参数的时候
     传递实参数组的时候会丢失数组的长度,所以在函数的内部使用sizeof计算数组的长度的时候是计算不出来的
     再多传一个参数 让调用者将数组的长度一并传递过来
     
     5.数组的算法
     a.求最大,最小值,累积和.平均值
     b.判断指定的数据是否包含在数组中
     c.找出指定的数据在数组中的下标
     d.产生不重复的随机数
     int balls[] = {0};
     
     for (int i = 0; i < 6; i++) {
     loop:
        printf("");
        int num = arc4random_uniform(33)+1;
     
        int rtes = containsObject(balls, 6, num);
        if(rtes == 1) {
            goto loop;
        }else {
            balls[i] = num;
        }
     }
     
     int containsObject(int arr[], int length, int num) {
     
        for (int i = 0; i < length; i++) {
            if (arr[i] == num) {
                return 1;
            }
        }
        return 0;
     }
     e.
     // 选择排序
     for (int i = 0; i < len - 1; i++) {
        for (int j = i + 1; j < len; j++) {
     
            if (arr[i] < arr[j]) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
     }
     
     
     // 冒泡排序
     for (int i = 0; i < len - 1; i++) {
        for (int j = 0; j < len - 1 - i; j++) {
            if (arr[j] < arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
     }
     
     二分查找:
     // 前提:arr是一个有序数组
     int min = 0;
     int max = len - 1;
     int num = 11;
     
     while(min<max) {
     int mid = min+max/2;
     if (arr[mid] == num) {
     return num;
     
     } else if(arr[mid] > num) {
     max = mid-1;
     } else {
     min = mid+1;
     }
     }
    
    
    - 二维数组与函数
     1).当二维数组作为函数的参数的时候,丢失行数和列数, 应同时传入行数和列数
     2).形参二维数组: 行数可以省略 列数不可省略
        实现二维数组: 行数任意 但是列数需保持一致
     
     void test(int rows, int cols, int arr[][cols]);
     
     - 字符串
     1).字符串数据的每一个字符存储在字符数组中,会自动追加一个'\0'结束
     char name[] = "jack"; ======= char name[5]
     
     2).用printf("%s", name); 输出字符串
     
     3).输入字符串
     scanf("%s", name);
      a.不安全,当数组的长度不够的时候就会崩溃
      b.空格问题, 输入空格会有\0问题
     
     4).长度计算
      a.不能用sizeof字符数组计算,因为这样得到的是字符数组的长度
      b.从第一个字节开始计数,直到遇见'\0'为止
     
     sizeof  返回定义arr数组时,编译器为其分配的数组空间大小,不关心里面存了多少数据。
     strlen   只关心存储的数据内容,不关心空间的大小和类型。
     
     5).字符串的常用函数
     puts(); // 输入 不安全
     gets(); // 输出 不安全
     strlen(); // 长度
     strcmp(); // 比较两个字符串设这两个字符串为str1,str2,若str1=str2,则返回零
     strcpy(); // 把str2的字符串复制到str1中,这两个都是地址
     strcat(); // 拼接字符串
     
     
     - 指针变量:专门用来存储地址的变量, 专门用来存储另外一个变量的地址的,可以说指针变量指向了另外一个变量
     变量的地址就叫做指针, 指针就是地址, 地址就是指针
     int num = 20;
     int *p1 = &num;
     
     p1操作的是p1这个指针变量,可以取p1的值, 也可以为p1赋值
     &p1拿到的是p1的地址
     
     - 野指针: 没有初始化的指针变量,值为垃圾值,随机数
     访问野指针指向的变量会报错,BAD_ACCESS错误
     
     - NULL  代表指针变量不指向内存中的任何地址,NULL完全等价于0,所以也可以赋值0
     int *p = NULL;
     int *p = 0;
     此时依然不可以访问,否则会报错
     
     
     函数的参数:
     1.当函数的参数类型是int 等等普通类型的时候,
     参数传递是值传递
     在函数的内部改变形参变量的值,对实参变量没有丝毫影响
     
     2.当函数的参数类型是数组的时候,
     参数传递是地址传递
     在函数的内部改变参数数组的元素的时候,其实改变的就是实参数组的元素
     
     3.当函数的参数类型是指针的时候,
     在函数的内部访问参数指针指向的变量的时候,其实改变的就是实参
    
     
     无论指针是什么类型,在内存中都占据8个字节
     
     
     - 多级指针
     声明二级指针: **p
     三级指针: ***p
     
     // 指针遍历数组
     int arr[7] = {10,20,30,40,50,60,70};
     int *p = arr;
     for (int i = 0; i < 7; i++) {
     printf("%d\n", *(p+i));
     printf("%d\n", *(arr+i));
     }
     
     
     - 当数组作为函数的参数的时候
     在声明这个参数数组的时候,它不是去创建1个数组,而是去创建一个用来存储地址的指针变量
     如果我们为函数写了一个数组作为参数
     其实编译器在编译的时候已经把这个数组换成了指针
     
     所以,在声明参数的时候不是创建数组,而是创建一个存储数组地址的指针变量
     这 也是为什么我们通过sizeof计算参数数组得到的永远都是8
     
    
     void test(int arr[], int len);
     替换为 void test(int *arr);
     
     
     - 索引的本质
     1).指针变量可以使用中括弧,在中括弧中写上下标来访问数据
     2).p1[n]; 前提是p1是一个指针变量
     完全等价于 *(p1+n)
     3).只要是指针都可以使用中括弧下标,就相当于是访问指针指向的变量
     
     arr[0] = 100;// *(arr+0)
     arr[1] = 200;// *(arr+1)
     
     
     两个指针变量相减 就是用在数组中,判断两个元素之间相差多少个元素
     
     
     
     - 字符串的两种存储方式
     1).使用字符数组来存储
     char name[] = "jack";
     
     2).使用字符指针存储
     char *name = "jack";
     
     区别:
     1).存储的结构不同
     2).可变与不可变
     
     - 字符串的恒定性
     
     - fputs();  f-->File
     作用: 将字符串数组 输出到 指定的流中
     流: 标准输出流->控制台
         文件流->磁盘上的文件
     使用格式:
     fputs(要输出的字符串, 指定的流);
     
     1.标准输出流(控制台) : stdout
     char *name = "sds";
     fputs(name, stdout);
     
     2. 将字符串存储到文件中
     a.要先声明一个文件指针,指向磁盘上的文件
     fopen函数可以创建一个指向文件的指针
     fopen(文件的路径(代表创建的指针指向这个文件), 操作文件的模式(对文件做什么操作));
     
     操作文件的模式: "w"-->write
                   "r"-->read
                    "a"-->append 追加数据
     
     当操作模式是w的时候,如果文件不存在则创建这个文件
     如果文件存在则会将原来的文件替换掉
     
     当操作模式是a的时候,如果文件不存在则创建这个文件
     如果文件存在则会追加
     
     b.使用fputs函数将字符串写入到指定的文件流中
     fputs(字符串, 文件指针);
     
     c.写完之后,一定要使用fclose函数结束这个文件
     
     FILE* pFile = fopen("/User/Desktop/abc.txt", "w");
     char *name = "ss";
     fputs(name, pFile);
     fclose(pFile);
     printf("写入成功");
     
     
     
     - fgets函数
     作用: 从指定的流中读取字符串
     fgets(要将字符串存储到哪一个数组中, 最多接收多少个长度的字符串(n , 最多n-1个,最后一个自动\0), 文件的路径);
     
    
     1.标准输出流(控制台) : stdin
     char input[5];
     fgets(input, 5, stdin);
     
     size_t len = strlen(input);
     if (input[len - 1] == '\n') {
     input[len - 1] = '\0';
     }
     
     2. 将字符串存储到文件中
     FILE* pFile = fopen("/User/Desktop/abc.txt", "r");
     char name[50];
     fgets(name, 50, pFile);
     fclose(pFile);
     printf("写入成功");
     
     
     - 字符串数组
     1).字符的二维数组  char name[][4]
     2).字符指针数组    char *name[4]
     
     
     - const关键字
     用来修饰变量,被const修饰的变量具备一定程度上的不可变性,叫做"只读变量"
     
     const int num = 10;
     const int arr[4] = {10,20,30,40}; 数组的元素不可更改
     
     const int *p1 = &num; 无法通过指针p1去修改指针指向的变量的值,但是如果直接去操作变量是可以的,
     但是指针变量的值可以改,可以把另一个变量的地址赋值给指针
     *p1 = 100; ❎
     int age = 20;
     p1 = &age; ✅
     
     
     int const *p1 = &num; 效果同上
     
     
     int * const p1 = &num;
     p1的值不能修改,但是可以通过p1去修改p1指向的变量的值
     
     int const * const p1 = &num;
     p1的值不能修改,也不可以用p1去修改p1指向的变量的值
    
     
     使用场景:
     1).const的特点:
     被const修饰的变量,是只读变量,只能取值,不能改值
     所以,const变量的值至始至终都不会发生变化
     2).当某些数据是固定的,在整个程序运行期间都不会变化,而且也不允许他人去更改,此时就用const
     3).当函数的参数是一个指针的时候,函数的内部有可能会改变实参变量的值
     void test(const int arr[]);
     
     
     
     
     - 内存管理
     1.内存的五大区域
     栈: 局部变量
     堆: 堆区中的字节空间允许程序员是手动的申请
     BSS段: 未初始化的全局变量和静态变量
     数据段: 已初始化的全部变量,静态变量,常量数据
     代码段: 存储代码段落
     
     2.如何向堆区申请字节空间来使用
     1).我们在堆区中申请的字节空间,如果我们不主动释放,那么系统是不会释放的,除非程序结束了
     2).在堆中申请字节空间的步骤: 申请->使用->释放
    
     #include <stdlib.h>
     
     - 向堆内存中申请连续的参数个数字节空间:
     malloc(n);
     
     (size_t == unsigned long)
     返回值: void *   代表没有类型的指针
     返回的是创建的空间中第一个字节的地址,地址是没有类型的
     * 在堆区申请的字节空间是从低地址向高地址分配
       每次申请的字节地址都是从0开始,每一次申请的字节空间不一定挨在一起
       但是,每一次申请的指定个字节一定是连续的
     * 在堆区申请的字节,里面是有值的,值是垃圾值,不会自动清零
     * 在堆区申请字节空间的时候有可能会申请失败,失败时返回NULL值,最好判断一下
    
     int *p1 = malloc(4);
     if(p1) {
      // 申请成功
     }
     * 申请的空间使用完毕后,一定要记得释放
     释放申请的堆空间: free(指针);
     
     
     - 向堆内存中申请指定个数字节空间:
     calloc(多少个单位, 每一个单位多少字节);
     int *p1 = calloc(3, sizeof(int));
     if (p1) {}
     相较malloc,申请完之后,系统会将字节中的数据清零
     
     
     - realloc 扩容
     如果原来的剩余空间不够扩容,则拷贝数据后自动释放,重新找一块足够的空间申请
     
     
     
     - 指向函数的指针
     1).可以定义一个指针指向一个函数,来使用这个指针间接调用这个函数
     2).指向函数的指针的声明
     拷贝函数头,去掉函数名 用小括弧代替,里面写上 *指针名
     3).初始化
     函数名就代表函数的地址,直接将函数名赋值给指针
     
     函数声明:
     void text () {
     }
     
     改为:
     void (*pFunc) () = test;
     
     4).使用
     a. pFunc();
     b. (*pFunc)();
     
     
     
     - 结构体
     作用:创建一个数据类型,这个数据类型的变量是由多个其他普通类型的小变量联合而成的
     
     struct 新类型名称(首字母x大写)
     {
        成员;
     };
     
     struct Student
     {
        char *name;
        int age;
     };
     
     声明结构体类型的变量:
     struct 新类型名称 变量名
    
     结构体变量的初始化:
     struct Student std1;
     std1.name = "jack";
     std1.age  = 18;
     
     也可以: 按顺序初始化成员变量
     struct Student std1 = {"jack", 18};
     
     也可以:
     struct Student std1 = {.age = 18, .name = "jack"};
    
     结构体成员变量的默认值:
     声明一个结构体变量,如果没有为这个结构体变量的成员赋值,那么成员有值,是垃圾值
     只要在声明结构体变量的同时只要初始化一个成员,其他的成员就会被自动初始化为0
     
     作用域:一般情况下,都是定义在函数外面
     
     结构体变量之间的相互赋值:
     相同结构体绝对可以: 将源结构体中的每一个变量拷贝一份赋值给目标结构体
     struct Student std1 = {"jack", 18};
     struct Student std2 = std1;
     
     
     
     - 结构体数组
     struct 结构体类型名称 数组名称[数组长度];
     struct Student arr[5];
     
     arr[0] = (struct Student){"jack", 18};
     或:
     struct Student arr[5] = {{"jack", 18},
                              {"jack", 18}
                              };
    
     结构体数组长度计算:
     sizeod(arr) / sizeof(struct Student);
     
     
     结构体指针的声明:
     struct 结构体类型名称 * 指针名;
     
     struct Student * pStu1 = &std1;
     
     指针间接访问结构体变量的值:
     (*pStu1).age = 19;
     pStu1->age = 19;
     
     
     结构体嵌套:
     struct Student
     {
     char *name;
     int age;
     struct Date birthday;
     };
     
     
     struct Date
     {
        int year;
        int month;
        int date;
     }
     struct Student stu1 = {"jack", 19, {2000, 10, 20}};
    
     
     结构体作为函数的参数: "值传递"
     int isAdult(struct Student stu){
     if (stu.age >=18)
        return 1;
     }else {
        return 0;
     }
     
     结构体作为函数的返回值:
     struct Student getAStudent(){
        struct Student stu1 = {"jack", 19, {2000, 10, 20}};
        return stu1;
     }
     
     // 返回指针
     struct Student *getAStudent(){
        struct Student *p1 = calloc(1, sizeof(struct Student));
        p1->name = "jack";
        ...
        return p1;
     }
     
     
     - 枚举
     创建一个数据类型,这个数据类型的变量的取值被限定
     enum 新类型
     {
     枚举值1, 枚举值2..... (每一个枚举值都有一个对应的整数,从0开始,依次递增)
     };
     
     enum ButtonType
     {
     ButtonTypeNormal,
     ButtonTypePress,
     ButtonTypeDisabled
     };
    
     声明枚举类型的变量:
     enum 新类型名称 变量名
     
     enum ButtonType type = ButtonTypeNormal;
    
     
     - typedef 将一个已经存在的数据类型取一个别名
     
     
     - 预处理指令
     #define 与 typedef 的区别:
     1).#define是一个预处理指令,在预编译的时候执行,把宏名换成宏值
        typedef是一个C代码,在运行的时候才会执行
     2).#define可以将任意的C代码取一个标识名
        typedef只能为数据类型取名
     
     
     #if
     #elseif
     #endif
     
     
     #ifdef
     #ifndef
     
     
     - static修饰局部变量
     1).修饰局部变量,这个变量就叫做静态变量
     2).静态变量不再存储在栈区域,而是存储在常量区
     3).第一次执行这个函数的时候,就会将这个静态变量声明在常量区,当函数执行完毕后,这个静态变量不会被回收,仍然存在
     后面再去执行的时候,声明静态变量的这句话会直接略过不会再执行
     
     - extern不能修饰局部变量
     
     - static修饰全局变量\函数:只能在当前模块中使用
     - extern修饰全局变量\函数:可以跨模块使用
    
    
    
    
    
    
    屏幕快照 2019-04-25 下午5.30.31.png 屏幕快照 2019-04-25 下午5.40.48.png 屏幕快照 2019-04-26 下午4.21.03.png 屏幕快照 2019-04-29 下午6.31.46.png 屏幕快照 2019-05-07 下午4.00.55.png

    相关文章

      网友评论

          本文标题:C语言笔记

          本文链接:https://www.haomeiwen.com/subject/biafgqtx.html