【C语言】10.static&extern,typedef,宏,条件编译,const

  • 全局变量:

    • 次一样启动就见面分配空间,直到程序结束。
    • 积存位置于静态存储区
    • 基本上独同名的全局变量指向一样片存储空间。
    • 全局变量默认为0
    • 分类:
      • 其间变量:只能当斯文件之中访问(加上static关键字)
      • 表面变量:可以当其余文件被访问的变量,默认有着全局变量都是外表变量。(什么还无加或是有extern声明)
  • static:

    • static对片变量的作用:

      • 延部分变量的生命周期,从程序启动暨程序退出,但是她并无改变量的作用域。也就是说,虽然保证它直接在,但是该不能够为此的时刻还是无克就此。
      • 概念变量的代码在全部程序运行期间一味会履同一次。注意,是概念变量的代码,也就是说只会定义一破。
    • static对全局变量的作用:

      • 出于静态全局变量的意图域局限于一个来源文件内,只能为该源文件内的函数公用,因此得以
        避免在其余源文件中滋生错误。

  • extern:

    • 假定声明的时节从不写extern,那系统会自动定义之变量,并拿那个开化为0。
    • 倘声明的时写extern了,那系统非会见自行定义是变量。
    • 貌似下用extern来声称变量是任何文件之外表变量。(?)
  • static 与 extern对函数的企图:

    • C语言默认所有函数都是表面函数,可以被别文件访问;而其中函数只能以按部就班文件被做客。

    • 同地,加上static关键字可以声明与概念一个函数变为内部函数,extern则足以声明与定义一个外表函数。

    • 例:

      // 声明一个内部函数
      static int sum(int num1,int num2);
      
      // 定义一个内部函数
      static int sum(int num1,int num2)
      {
          return num1 + num2;
      }
      
      // 声明一个外部函数
      extern int sum(int num1,int num2);
      
      // 定义一个外部函数
      extern int sum(int num1,int num2)
      {
          return num1 + num2;
      }
      

  • typedef:

    • 宏定义也堪形成typedef的干活,但是宏定义是由于预先处理完成的,而typedef则是在编译时得的,后者尤其灵活方便且是出错。

    • 例:

      typedef int INTEGER;
      
      typedef Integer MyInteger;
      
      typedef char NAME[20]; // 表示NAME是字符数组类型,数组长度为20。然后可用NAME 说明变量。
      NAME a; // 等价于 char a[20];
      
      typedef char * String;
      String myString = "hello";
      

      及结构体的宣示定义一样,在对结构体使用typedef时,也起三种植形式:

      struct Person{
          int age;
          char *name;
      };
      
      typedef struct Person PersonType; // PersonType person; person.age = ...
      
      /////////////////////////////////////////////////////////////////////
      
      typedef struct Person{
          int age;
          char *name;
      } PersonType;
      
      注意这个和结构体在定义的时候直接初始化一个值的区别,这个是由typedef关键字的,且是别名,而不是一个变量。
      
      /////////////////////////////////////////////////////////////////////
      
      typedef struct {
          int age;
          char *name;
      } PersonType;
      
      省略结构体名。
      
      /////////////////////////////////////////////////////////////////////
      
      // typedef和指向结构体的指针
      // 首先定义一个结构体并起别名 
        typedef struct { float x; float y; } Point;
      // 起别名
        typedef Point *PP;
      

      与枚举的声明定义一样,在针对枚举使用typedef时,也出三栽样式:

      enum Sex{
          SexMan,
          SexWoman,
          SexOther
      };
      typedef enum Sex SexType;
      
      /////////////////////////////////////////////////////////////////////
      
      typedef enum Sex{
          SexMan,
          SexWoman,
          SexOther
      } SexType;
      
      /////////////////////////////////////////////////////////////////////
      
      typedef enum{
          SexMan,
          SexWoman,
          SexOther
      } SexType;
      

      typedef和函数指针:(重要!)

      int add(int a, int b) {
          return a + b;
      }
      
      int main() {
          typedef int (*myFunction) (int, int); 
        //注意此时myFunction就是int (*myFunction) (int, int)的别名。表示这是一种指向函数的指针的类型。
          myFunction p = add; 
          p();
          return 0;
      }
      

    • 免带参数的宏定义:

      • 格式: #define 标示符
        字符串(“字符串”可以是常数、表达式、格式串等。)
      • 宏名的有用限制是自概念位置到文件截止。如果需要停止宏定义的作用域,可以用#undef命令。
    • 带动参数的宏定义:

      • 对牵动参数的大,在调用中,不仅要宏展开,而且若用实参去替换形参。

      • #define 宏名(形参表) 字符串

      • #define average(a, b) (a+b)/2

      • 宏名和参数列表中无可知来空格,否则空格后面的有字符串都作为替换的字符串。

      • 带来参数的宏在展开时,只发简单的字符和参数的替换,不进行任何计算操作。所以在概念宏时,一般用一个小括号括住字符串的参数。并且计算结果最也括起来防止误。

        #define Pow(a) ( (a) * (a) )
        
  • 条件编译

    • 以无数景象下,我们希望程序的内有些代码只有当满足一定原则时才开展编译,否则不介入编译(只有与编译的代码最终才能够为执行),这就是是基准编译。换句话说,在尺度编译中,不满足条件的会晤直接吃删去,并无会见与编译。
    • 标准编译和宏定义经常一起行使。

#define SCORE 67
#if SCORE > 90
    printf("优秀\n");
#else
    printf("不及格\n");
#endif

// 条件编译后面的条件表达式中不能识别变量,它里面只能识别常量和宏定义

#if 条件1
  ...code1...
 #elif 条件2
  ...code2...
 #else
  ...code3...
 #endif

#define SCORE 67
#if SCORE > 90
    printf("优秀\n");
#elif SCORE > 60
    printf("良好\n");
#else
    printf("不及格\n");
#endif
  • 顾,条件相似是判宏定义而非是判断变量,因为口径编译是以编译之前举行的论断,宏定义也是编译之前定义的,而变量是以运作时才来的。
  • 自然不要遗忘在末加上 #endif !
  • ifndef 条件编译指令 与 上面用法类似,不过大凡法相反的。

  • 动用标准编译指令调试 bug

    #define DEBUG1 0
    #if DEBUG1 == 0 //format是格式控制,##表示可以没有参数,__VA_ARGS__表示变量 #define Log(format,...) printf(format,## __VA_ARGS__)
    #else
    #define Log(format,...)
    #endif
    
    void test(){
        Log("xxxxx");
    }
    int main(int argc, const char * argv[]) {
        Log("%d\n",10);
        return 0;
    }
    

    这种情景用Log代替printf,在调节的时候我们就待变更宏定义的值,就得灵活的控制输出的语。比如把DEBUG1设置为0,变为调试状态,那么具有的printf就会见替代Log输出一些信;把DEBUG1设置为1,则关闭调试状态,则会拿Log代替为空,则非会见发出口。

    这边,‘…’指只是转换参数。这仿佛宏在被调用时,##表示成零个要多个参数,包括内部的逗号,一直顶到右括弧结束完。当为调用时,在宏体(macro
    body)中,那些符号序列集合将替代里面 的VA_ARGS标识符。

  • 还是用规范令在调节阶段设置有默认值,比如账号密码,等收尾调试只待改宏命令就是得关闭这个力量。

  • 以C语言中预防再次导入头文件之问题:

    #ifndef __xxxxxx__x__
    #define __xxxxxx__x__
    
    #include <stdio.h>
    
    #endif /* defined(__xxxxxx__x__) */
    

    照就是xxxxx里面的x.h文件,当别的文本导入这个文件之时段(#include),相当给复制这文件里之始末。如果导入了大多单文件未小心还导入了与一个,那么像面这样写是从来不问题的。因为预处理指令会首先检查是不是定义了

    __xxxxxx__x__
    

    使没概念,那么尽管定义,并且导入stdio.h文件。

    那么要下次遇见(这时候还是在预编译的上做到的),它还检查是不是定义了

    __xxxxxx__x__
    

    这次为刚刚咱们既定义了,那么其便见面一直了之令,所以无见面再次导入相同之腔文件。

  • 倘起更导入循环(比如a.h文件里导入了b.h,而b.h文件里导入了a.h),解决办法:

    苟a.h声明了一个add方法,b.h里面声明了一个minus方法,但是这重新引用循环,编译器会报错,那么可以b.h里面不再导入a.h就非会见产出循环导入的题目。但是我还要想以b.h里面访问a.h里面的add方法,但是此时b.h里面连没有导入a.h,那么我们尽管直将add方法的扬言复制到b.h里面即可。

  • const

    const的意图主要是简单个:

    1. 节约空间,避免不必要的内存分配。(Swift中建议采用let也是是原因?)

      #define PI 3.14159 //常量宏
      const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ...... double i=Pi; //此时为Pi分配内存,以后不再分配!
      double I=PI; //编译期间进行宏替换,分配内存
      double j=Pi; //没有内存分配
      double J=PI; //再进行宏替换,又一次分配内存! const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。
      

      编译器通常不呢普通const常量分配存储空间,而是将她保存在符号表中,这令她化一个编译期间的常量,没有了储存和读内存的操作,使得她的效率也充分高。

    2. 更加安全。

    使用:

    1. 每当变量名前或变量名后。

      int const x = 2;

      const int x = 2;

    2. 小心用const修饰指针:

      • const int *A; //const修饰指针,A可转移,A指向的价值未克叫涂改

      • int const *A; //const修饰指向的对象,A可易,A指向的靶子不可变

      • int *const A; //const修饰指针A, A不可变,A指向的目标可是转换

      • const int *const A;//指针A和A指向的对象还不可变

      先看“*”的位置

      如果const 在 *的左边 表示值不能够修改,但是因于好更改。

      如果const 在 *的下手 表示对不能够转,但是价值好转移

      如果在“*”的两侧还来const 标识指向和价值都非可知转。