美文网首页
11-C语言修饰符和预处理指

11-C语言修饰符和预处理指

作者: 低头看云 | 来源:发表于2018-09-16 19:56 被阅读0次

    全局变量和局部变量

    • 局部变量

      • 局部变量就是从开始定义到遇见return 或者遇到}为止
      • 存储位置在内存的栈区中
      • 特点:
        • 相同作用域内不可以定义同名变量,
        • 不同作用域可以定义同名变量,但是内部变量会覆盖外面定义的变量
    • 全局变量

      • 全局变量的作用范围就是从定义的那行开始到文件结束
      • 存储在静态存储区
      • 特点: 多个同名的全局变量指向同一块存储空间

    auto 和 register 关键字

    • auto 关键字只能用于修饰局部变量的; 默认就是 auto
    • register: 告诉编译器,将局部变量存储到cpu寄存器下
    • auto 和 register共同点都是修饰局部变量的
    • 好处:
      • auto : 特点就是告诉编译器,局部变量离开作用域自动释放
      • register好处就是访问的速度回更快;

    static 关键字

    • static对局部变量的作用

      • 如果利用static修饰局部变量,那么会将局部变量的存储区域从栈去移动到静态区

      • 静态区只有程序结束才会释放

      • 没有用static修饰的变量

      #include <stdio.h>
      void test();
      int main()
      {
          test();
          test();
          test();
          return 0;
      }
      
      
      void test(){
          int num = 0;
          num++;
          printf("num = %i\n", num);  // 局部变量的时候输出1  1  1
      }
      
      
      • 用static修饰过的
      #include <stdio.h>
      void test();
      int main()
      {
          test();
          test();
          test();
          return 0;
      }
      
      
      void test(){
          static int num = 0;
          num++;
          printf("num = %i\n", num);  // 1  2  3
      }
      
      
    • static 对全局变量的作用

      • 全局变量默认是所有文件都可以使用
      • 但是如果想要让某个函数在一个文件内使用,那么就可以使用static进行修饰
    // main.c 下的文件, 引入了a.h这个头文件
    #include <stdio.h>
    #include "a.h"
    int main()
    {
        int res1 = sum(10, 20);
        printf("res1 = %i\n", res1);  // 如果函数被Static修饰,则外部文件不能访问报错
        int res2 = minus(10, 20);
        printf("res2 = %i\n", res2);
        return 0;
    }
    
    
    // a.c 下的文件
    static int sum(int num1, int num2){
        return num1 + num2;
    }
    int minus(int num1, int num2){
        return num1 - num2;
    }
    
    
    • static 对函数的作用就是上面的代码
      默认情况下, 所有的函数都是外部函数(默认有extern, 但是一般都省略不写)
      所以 static修饰函数,就是可以定义一个内部使用函数,不会被外界污染;

    • 注意点: 如果只有函数声明添加了static 与 extern, 而定义中没有添加static与
      extern, 那么 static的作用无效
      ;

    extern 关键字

    • extern对局部变量的作用

      • extern 用于声明一个变量,声明变量并不会开辟存储空间
      • extern 一般用于全局变量,不能用于局部变量
    • extern对全局变量的作用

      • 用于声明一个全局变量, 应为全局变量时从定义开始的哪一行才能进行使用
      • 那么使用extern关键字声明这个变量,那么在文件中可以放心使用
      • 没有使用extern 声明变量
      #include <stdio.h>
      
      int main()
      {
          num = 666;
          printf("num = %i\n", num);  // 错误 全局变量定义在使用之后
          return 0;
      }
      
      int num;
      
      • 使用extern 声明变量
      #include <stdio.h>
      extern int num;
      int main()
      {
          num = 666;
          printf("num = %i\n", num);  // 666
          return 0;
      }
      
      int num;
      

    编译处理过程

    • 源文件 --> 预处理 --> 汇编 --> 二进制 --> 和系统的二进制文件进行链接 --> 可执行程序

    预处理

    • 预处理指令
      • 文件包含 include
      • 宏定义 #define
      • 条件编译 #if #else #endif

    宏定义

    • 宏定义会在预处理的时候, 用宏定义对应的值来替换宏

    不带参数的宏定义

    • 格式: #define 标识符 字符串

      • 其中的"#"表示这是一条预处理命令.凡是以"#" 开头的均为预处理命令.
      • "define"为宏定义命令.
      • "标识符"为所定义的宏名.
      • "字符串" 可以是常数,表达式,格式串
      #define COUNT 5
      
      • 应用场景: 定义一个宏修改URL, 只需要改一个宏定义就好
    • 注意点:

      • 1.宏名一般用大写字母,以便与变量名区别开来.
      • 2.宏定义的后面不要写分号,因为宏定义是单纯的替换,会用宏的值来替换宏的名称
      • 3.宏定义分为两种,一种是带参数的宏定义,另一种是不带参数的宏定义
      • 定义一个宏时可以引用已经定义的宏名;
      #define R  3.0
      #define PI 3.14
      #define L  2*PI*R
      #define S  PI*R*R
      

    带参数的宏定义

    • 格式: #define 宏名(形参表) 字符串
    #define SUM(a, b) a+b
    
    • 注意点:
      • 宏定义无论有没有参数,都是单纯的替换
      #include <stdio.h>
      int sum(int a, int b){
          return a + b;
      }
      
      #define SUM(a,b) a+b
      
      int main()
      {
          int num1 = 10, num2 = 20;
           
          // int res = SUM(num1, num2);
          // int res = a + b;
          // int res = num1 + num2;
          int res = SUM(num1, num2);
          printf("res = %i\n", res);
          return 0;
      }
      
      
      • 宏的值的每个参数都需要加上(),宏的值的结果也需要加上();
        • 只有一个参数时
        #include <stdio.h>
        #define SUM(a) a*a   // 写法有点问题
        int main()
        {
            int num = 5;
            int res = SUM(num);  // 虽然正确输出
            printf("res = %i\n", res);
            return 0;
        }
        
        
        • 如果宏定义的时候不用小括号括住引发结果错误
        #include <stdio.h>
        #define SUM(a) a*a
        int main()
        {
        
            // int res = a * a;
            // int res = 3 + 3 * 3 + 3;
            int res = SUM(3 + 3); // 15  错误输出
            printf("res = %i\n", res);
            return 0;
        }
        
        
        • 只给宏的值加上括号还是容易引发输出错误
        #include <stdio.h>
        #define SUM(a) (a)+(a)
        int main()
        {
            
            // int res = (a) * (a);
            //int res = (3 + 3) + (3 + 3) / (2 + 2)  + (2 + 2);
            int res = SUM(3 + 3) / SUM(2 + 2);  // 输出11
        
            printf("res = %i\n", res);
            return 0;
        }
        
        • 正确写法: 宏的值的每个参数都需要加上(),宏的值的结果也需要加上()
        #include <stdio.h>
        #define SUM(a) ((a)+(a))
        int main()
        {
        
            // int res = ((a) * (a));
            //int res = ((3 + 3) + (3 + 3)) / ((2 + 2)  + (2 + 2));
            int res = SUM(3 + 3) / SUM(2 + 2);  // 输出1
            // 宏的值的每个参数都需要加上(),宏的值的结果也需要加上()
            printf("res = %i\n", res);
            return 0;
        }
        
    • 在企业开发中使用函数还是使用宏定义
      • 如果函数特别简单,一句话能搞定,那么可以使用宏定义,宏定义的效率更高
      • 宏定义在预处理时候就执行了,而函数是在运行的时候才会执行
      • 宏定义只会做简单的替换,而函数还需要分配内存空间

    宏定义的作用域

    • 宏定义的作用域和全局变量很像, 但是宏定义可以提前结束
    // 宏定义
    #define COUNT 666
    
    // 用来提前结束宏定义
    #undef COUNT   
    
    • 作用域是从定义的那一行开始, 直到文件末尾;
    • 可以通过 #undef 宏名 来提前结束宏的作用域

    条件编译

    • 条件编译和if else语句很像,但是也有区别
    • 区别:
      • 1.if else语句是在程序运行的时候执行的, 而#if #else #endif 是在预处理的时候执行的
      • 2.if else语句所有的代码都会编译到程序中, 而#if #else #endif中满足条件的语句才会编译到程序中
    • if 和 #else的三种写法 和 if else写法类似

      • 第一种:
      #if 0 == score
          printf("0分\n");
      #endif
      
      • 第二种:
      #if 0 == socre
          printf("0分\n");
      #else 
          printf("100分");
      #endif
      
      • 第三种:
      #define SCORE 100
      #if 100 == SCORE
          printf("100分\n");
      #elif 60 <= SCORE
          printf("及格了\n");
      #else
          printf("不及格\n");
      #endif
      
    • 注意点:
      • 条件编译中不能获取变量的值
      • 因为变量时程序执行的时候才有的,而条件编译时预处理的时候就执行了
      • 君生我未生 我生君已老
      • 条件编译一般是配合宏定义来使用, 因为宏定义和条件编译都是在预处理的时候执行的
    • 应用的小例子
    #define DEBUG 0
    #if DEBUG == 1
    #define URL "127.0.0.1"
    #else
    #define URL "www.it666.com"
    #endif
    
    // 判断有没有定义名称叫做SUM的宏
    #ifndef SUM
    // 如果没有定义定义就会进入到这里面,然后定义一个叫做SUM的宏
    #define SUM
    int sum (int num1, int num2);
    #endif
    

    文件包含 #include

    • include <>

    • <>会先从编译器的环境中查找对应的文件,如果没有再从系统的环境中查找对应的文件

    • include "xxx"

    • "xxx" 会先从当前项目环境中查找对应的文件,如果没有再从编译器的环境中查找对应的文件,如果还没有再从系统环境中查找对应的文件

    • include作用: 将指定文件中的代码原封不动的拷贝到#include的位置

    • inclued执行时间: 预处理的时候执行的

    • 注意点:

      • 已知函数的定义不可以重复,但是函数的声明可以重复定义
      • 但是如果重复声明函数,或重复导入.h文件会影响编译器的编译效率
      • 所以在C语言中有专门解决重复导入的方法
      // 判断有没有定义名称叫做SUM的宏
      #ifndef SUM
      // 如果没有定义定义就会进入到这里面,然后定义一个叫做SUM的宏
      #define SUM
      int sum (int num1, int num2);
      #endif
      
      • 特别注意:不要出现循环包含, 例如A的头文件包含B文件,B文件又包含A文件

    typedef 关键字

    • C语言不仅提供了丰富的数据类型,而且还允许由用户自己定义类型说明符,也就是说允许由用户为数据类型取 "别名";
    • 注意点:typedef相当于给人起了一个外号;所以typedef不是定义一个新的数据类型,而是定义一个新的名称
    • 格式: typedef 原类型名 新类型名;
      • 其中原类型名中含有定义部分,新类型名一般用大写表示,以便区别.
    typedef int INTEGER
    INTEGER a;  // 等价于int a;
    
    
    • 1.给普通指针起别名, 简化,更容易理解
    typedef char* String;
    String name = "cww";
    printf("name = %s\n", name);
    
    
    • 2.给结构体起别名
      • 2.1先定义结构体类型,再给结构体类型起别名
      struct Person{
          char *name;
          int age;
      };
      typedef struct Person ps;
      ps p = {"cww", 18};
      printf("name = %s\n", p.name);  // cww
      
      
      • 2.2定义结构体类型的同时, 给结构体类型起别名
      typedef struct Person{
          char *name;
          int age;
      } ps;
      
      ps p = {"cww", 18};
      printf("name = %s\n", p.name);  // cww
      
      
      • 2.3定义结构体类型的同时,给结构体类型起别名,并且省略原有类型的名称
      typedef struct{
          char *name;
          int age;
      } Person;
      // struct Person p = {"cww", 18};
      Person p = {"cww", 18};
      printf("name = %s\n", p.name);
      
      • 给共用体\枚举起别名,格式和结构体一模一样
    • 3.给指向函数的指针起别名
      • 如果给指向函数的指针起别名, 那么指针名称就是别名
    // 常规写法
    int (*funcP)(int , int);
    funcP = &sum;
    
    int (*funcP2)(int , int);
    funcP2 = &minus;
    
    // 注意点:如果给指向函数的指针起别名, 那么指针名称就是别名
    typedef int (*funcP)(int , int);
    funcP p1 = &sum;
    funcP p2 = &minus;
    
    int sum(int num1, int num2){
        return num1 + num2;
    }
    
    int minus(int num1, int num2){
        return num1 - num2;
    }
    
    
    • typedef和#define 注意点: 如果要给数据类型起别名, 那么一律用typedef即可;

    const 关键字

    • const是一个类型修饰符
      • 使用const修饰变量则可以让变量的值不能改变
    • 注意点:
      • const可以写在数据类型的前面,也可以写在数据类型的后面;

      • 对于基本数据类型来说,写在数据类型的前面和后面没有任何区别

      #include <stdio.h>
      
      int main()
      {
          // int num = 10; // 不加const, num是一个变量,可以被修改
      
          // 只要在变量前面加上const, 那么这个变量保存的数据就不会修改了
          // const int num = 10;
          int const num = 10;
          printf("num = %i\n", num);
          num = 666;  // 变量前面加上const ,则变量的值不会修改, 报错
          printf("num = %i\n", num);
          return 0;
      }
      
      
      • 但是对于指针来说, 写在数据类型的前面和后面就有区别了

    const 和指针

    • const 如果写在指针数据类型左边和右边(即const写在星号的左边的), 那么代表指针指向的内存空间不能修改, 但是指针的指向可以修改;
    #include <stdio.h>
    
    int main()
    {
      // 指针和const
        int num = 666;
        // 定义一个指针
        // int *p;
        // const int *p;  // const 写在数据类型的左边
        int const *p;   // const 写在数据类型的右边
        p = &num;
        printf("*p = %i\n", *p);
    
        // *p = 888;  // const 修饰的变量 不可以修改内存空间的值
        printf("*p = %i\n", *p);
    
        int value = 777;
        p = &value;  // 重新给p赋予value的地址
        printf("*p = %i\n", *p);  // 输出777
    
        return 0;
    }
    
    • const 如果写在指针星号的右边, 那么代表指针指向的内存空间可以修改,但是指针的指向不可以修改;
    #include <stdio.h>
    
    int main()
    {
    
        // 指针和const
        int num = 666;
        
        int * const p = &num;
    //    p = &num;
        printf("*p = %i\n", *p);  // 666
        *p = 888;
        printf("*p = %i\n", *p);  // 888
    
        int value = 777;
        // p = &value;   // 报错, const 写在星号的右边,则指针的指向不可以修改
        printf("*p = %i\n", *p);
    
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:11-C语言修饰符和预处理指

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