C语言笔记

 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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342