如何正确使用const,static,extern

 如何正确使用const,static,extern

一,const的使用

1. 什么是const

   * 介绍:const是一个C语言(ANSI C)的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一定帮助
   * 定义:const修饰的数据类型是指常类型,常类型的变量或对象的值是不能被更新的。
   * 目的:const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。
      *  const与宏的区别(面试题):
                const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量。
                编译时刻:宏是预编译(编译之前处理),const是编译阶段。
                编译检查:宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。
                宏的好处:宏能定义一些函数,方法。 const不能。
                宏的坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换。
      示例:       
  // 常见的常量:抽成宏
     #define XMGAccount @"account"
     #define XMGUserDefault [NSUserDefaults standardUserDefaults]
  // 字符串常量
     static NSString * const account = @"account";
    - (void)viewDidLoad {
     [super viewDidLoad];
     // 偏好设置存储
     // 使用宏
     [XMGUserDefault setValue:@"123" forKey:XMGAccount];
     // 使用const常量
     [[NSUserDefaults standardUserDefaults] setValue:@"123" forKey:account];
   }
    * 主要作用(了解)
        (1)可以定义const常量,具有不可变性。 
  例如:const int Max=100; Max++会产生错误; 
   (2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。
  例如: void f(const int i) { .........} 编译器就会知道i是一个常量,不允许修改; 
   (3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!
  如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可! 
   (4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。 还是上面的例子,如果在函数体内修改了i,编译器就会报错; 
  例如: void f(const int i) { i=10;//error! } 
   (5) 可以节省空间,避免不必要的内存分配。 例如: 
  #define PI 3.14159 //常量宏 
  const double Pi=3.14159; //此时并未将Pi放入RAM中 ...... 
  double i=Pi; //此时为Pi分配内存,以后不再分配! 
  double I=PI; //编译期间进行宏替换,分配内存 
  double j=Pi; //没有内存分配 
  double J=PI; //再进行宏替换,又一次分配内存! 
  const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干份拷贝。 
 (6) 提高了效率。 
  编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

2、const一般使用

1.用const修饰指针变量
(1)const 类型 * 变量名:可以改变指针的指向,不能改变指针指向的内容。
       int x = 1;  
       int y = 2;  
       const  int *px = &x; // 让指针px指向变量x  
       px = &y; // 改变指针px的指向,使其指向变量y  
       *px = 3; // 改变px指向的变量x的值,出错:Read-only variable is not assignable  
(2)类型 * const 变量名:可以改变指针指向的内容,不能改变指针的指向
       int x = 1;  
       int y = 2;  
       int * const px = &x; // 让指针px指向变量x  
       px = &y;    // 改变px的指向,出错:Read-only variable is not assignable  
       (*px) += 2; // 改变px指向的变量x的值  
(3)const 类型 * const 变量名:指针的指向、指针指向的内容都不可以改变
       int x = 1;  
       int y = 2;  
       const  int * const px = &x; // 让指针px指向变量x  
       px = &y;    // 改变px的指向,出错:Read-only variable is not assignable  
       (*px) += 2; // 改变px指向的变量x的值,出错:Read-only variable is not assignable  
2.创建文件域的字符串常量
  (1)如果指针指向的是可变的字符串对象(如:NSMutableString),即使加const修饰该对象,该对象也是可变的,如:
- (void)viewDidLoad {
    [super viewDidLoad];
    //定义指针str, 指向只读对象
    const NSMutableString * str =[NSMutableString stringWithString:@"OneDream,"];
    NSLog(@"%@",str);
    NSLog(@"%p",str);
    NSLog(@"%p",&str);
    NSLog(@"----分割线----");
    
    //改变str指向的对象的值
    [str appendString:@"OneWorld"];
    NSLog(@"%@",str);
    NSLog(@"%p",str);
    NSLog(@"%p",&str);
    NSLog(@"----分割线----");
    
    //改变str的指向
    str =[NSMutableString stringWithString:@"A new string"];
    NSLog(@"%@",str);
    NSLog(@"%p",str);
    NSLog(@"%p",&str);
    NSLog(@"----分割线----");
}
输出结果为:
2016-08-11 15:13:23.925 LXNetworkking[7113:115952] OneDream,
2016-08-11 15:13:23.925 LXNetworkking[7113:115952] 0x7ff222497ac0
2016-08-11 15:13:23.925 LXNetworkking[7113:115952] 0x7fff5a987a58
2016-08-11 15:13:23.926 LXNetworkking[7113:115952] ----分割线----
2016-08-11 15:13:23.926 LXNetworkking[7113:115952] OneDream,OneWorld
2016-08-11 15:13:23.926 LXNetworkking[7113:115952] 0x7ff222497ac0
2016-08-11 15:13:23.926 LXNetworkking[7113:115952] 0x7fff5a987a58
2016-08-11 15:13:23.926 LXNetworkking[7113:115952] ----分割线----
2016-08-11 15:13:23.926 LXNetworkking[7113:115952] A new string
2016-08-11 15:13:23.927 LXNetworkking[7113:115952] 0x7ff222643e00
2016-08-11 15:13:23.927 LXNetworkking[7113:115952] 0x7fff5a987a58
2016-08-11 15:13:23.927 LXNetworkking[7113:115952] ----分割线----
(2)如果指向的是不可变对象(如:NSString),就不用担心指向的对象的值被改变了,但是指针的指向也可以被改变
- (void)viewDidLoad {
    [super viewDidLoad];
    //定义指针str, 指向只读对象
    const NSString * str =[NSMutableString stringWithString:@"OneDream,"];
    NSLog(@"%@",str);
    NSLog(@"%p",str);
    NSLog(@"%p",&str);
    NSLog(@"----分割线----");
    
    //改变str的指向
    str =@"A new string";
    NSLog(@"%@",str);
    NSLog(@"%p",str);
    NSLog(@"%p",&str);
    NSLog(@"----分割线----");
}
输出结果为:
2016-08-11 15:17:18.075 LXNetworkking[7250:118893] OneDream,
2016-08-11 15:17:18.076 LXNetworkking[7250:118893] 0x7f8403ddcc80
2016-08-11 15:17:18.076 LXNetworkking[7250:118893] 0x7fff544caa58
2016-08-11 15:17:18.076 LXNetworkking[7250:118893] ----分割线----
2016-08-11 15:17:18.076 LXNetworkking[7250:118893] A new string
2016-08-11 15:17:18.076 LXNetworkking[7250:118893] 0x10b7350e0
2016-08-11 15:17:18.076 LXNetworkking[7250:118893] 0x7fff544caa58
2016-08-11 15:17:18.077 LXNetworkking[7250:118893] ----分割线----
(3) 如果想创建一个只读的字符串常量,应该设定指针只读,并指向不可变对象
- (void)viewDidLoad {
    [super viewDidLoad];
    //定义指针str, 指向只读对象
    NSString * const str =@"One Dream";
    NSLog(@"%@",str);
    NSLog(@"%p",str);
    NSLog(@"%p",&str);
    NSLog(@"----分割线----");
    
    //改变str的指向
    str =@"A new string";   错误:Read-only variable is not assignable  
    NSLog(@"%@",str);
    NSLog(@"%p",str);
    NSLog(@"%p",&str);
    NSLog(@"----分割线----");
}

3.总结:

    * 那么声明文件域的字符串常量的做法是:在文件首部作以下声明
          static NSString * const gString = @"Global";  
    * 必须在声明时就对常量赋值。否则在使用时不能再对其赋值,从而使用的是指向nil的指针。
    * 如果要声明文件域的是基本数据类型常量,在文件首部声明变量时用static const修饰,并赋初值。例如:
          static const NSUInteger gInteger = 100;  

二,extern

         作用: 只是用来获取全局变量(包括全局静态变量)的值,不能用于定义变量
              * 怎么理解这句话呢?
                   就是说,我们在其他类(比如LXView)中定义了,全局变量. 但是我们想在某个类中(比如:ViewController)使用这个全局变量, 我们无需引入头文件,仅仅通过
                   extern +我们定义的全局变量. 就可以对相应的全局变量进行操作.
        例如:LXView.h中声明, 在LXView.m中初始化, 在ViewController中引用
              * 在 @interface~ @end的外部定义
                  lxView.h   
         #import <UIKit/UIKit.h>
         NSString * name; //全局变量
         @interface lxView : UIView
         @end
                 lxView.m
         #import "lxView.h"
         @implementation lxView
         NSString * name =@"hello!";
         @end
                 ViewController.m        
         #import "ViewController.h"
         @implementation ViewController
         - (void)viewDidLoad {
          [super viewDidLoad];
          extern NSString * name;
          NSLog(@"%@",name);
         }
                打印结果:                
 2016-08-11 17:38:06.937 LXNetworkking[10672:170415] hello!   
           * 在 @interface~ @end的内部定义                    
         #import <UIKit/UIKit.h>
         @interface lxView : UIView
         NSString *name; error
         //  * 这个是不合法的:因为OC的类会将这个全局变量当做成员属性来处理,而成员属性是需要加{}的,所以不合法;
         //  * 声明全局变量的时候默认带有extern,这里必须显式声明.即为: extern  NSString *name ;
         //假如 此处为@property(nonatomic) NSString * name; 则无需添加extern, 因为xocde在编译时会默认添加 extern 关键词
         @end
                 lxView.m
         #import "lxView.h"
         @implementation lxView
         NSString * name =@"hello!";
         @end
                 ViewController.m
        #import "ViewController.h"
        @implementation ViewController
        - (void)viewDidLoad {
         [super viewDidLoad];
          extern NSString * name;
          NSLog(@"%@",name);
         }
                 打印结果:  
2016-08-11 17:54:26.566 LXNetworkking[11120:177772] hello!
             * 在 @interface~ @end的外部定义并初始化 
                   lxView.h
        #import <UIKit/UIKit.h>
        extern NSString * name =@"hello!"; //必须添加 extern
        @interface lxView : UIView
        @end
                 lxView.m
       #import "lxView.h"
       @implementation lxView
       @end
                 ViewController.m
       #import "ViewController.h"
       @implementation ViewController
       - (void)viewDidLoad {
           [super viewDidLoad];
            extern NSString * name;
            NSLog(@"%@",name);
       }
                 打印结果:
           2016-08-11 17:38:06.937 LXNetworkking[10672:170415] hello!   
             * 在 @implementation ~ @end的内部定义并初始化              
                  lxView.h
         #import <UIKit/UIKit.h>
         @interface lxView : UIView
         @end
                  lxView.m
         #import "lxView.h"
         @implementation lxView
         extern NSString * str = @"hello!"; //extern 可带可不带
         @end
                 ViewController.m
        #import "ViewController.h"
        @implementation ViewController
         - (void)viewDidLoad {
            [super viewDidLoad];
            extern NSString * name;
            NSLog(@"%@",name);
        }
       打印结果:
  2016-08-11 17:38:06.937 LXNetworkking[10672:170415] hello!     
           * 在 @implementation ~ @end的外部定义并初始化
            lxView.h
      #import <UIKit/UIKit.h>
      @interface lxView : UIView
      @end
           lxView.m
<pre name="code" class="objc">     #import <UIKit/UIKit.h>
     extern NSString * str = @"hello!";//extern 可带可不带
     @interface lxView : UIView
     @end
            ViewController.m 
 
      #import "ViewController.h"
      @implementation ViewController
      - (void)viewDidLoad {
        [super viewDidLoad];
        extern NSString * name;
        NSLog(@"%@",name);
       }
  打印结果:
 2016-08-11 17:38:06.937 LXNetworkking[10672:170415] hello! 
 总结:
   * 工作原理:先在当前文件查找有没有全局变量,没有找到,才会去其他文件查找。
   * extern声明,仅适于修饰全局变量,不能去修饰其他的变量。一般情况下默认,定义的全局变量都带有extern,所以不过多解释。

三,static 

  1.static修饰全局变量

     * 在全局变量前加static,全局变量就被定义成为一个全局静态变量(全局变量和静态全局变量的生命周期是一样的,都是在堆中的静态区,在整个工程执行期间内一直存在)
        特点如下
         1)存储区:静态存储区没变(静态存储区在整个程序运行期间都存在);
         2)作用域:全局静态变量在声明他的文件之外是不可见的。准确地讲从定义之处开始到文件结尾。非静态全局变量的作用域是整个源程序(多个源文件可以共同使用);而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。
        好处:   
         1)不会被其他文件所访问,修改;
         2)其他文件中可以使用相同名字的变量,不会发生冲突。

  2.static修饰局部变量

     * 在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。
        特点如下:
         1)存储区:有栈变为静态存储区rw data,生存期为整个源程序,只能在定义该变量的函数内使用。退出该函数后, 尽管该变量还继续存在,但不能使用它;
         2)作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。

  3.static修饰函数

     * 在函数的返回类型前加上关键字static,函数就被定义成为静态函数。
        函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。只能被本文件中的函数调用,
        而不能被同一程序其它文件中的函数调用
       好处
        1)其他文件中可以定义相同名字的函数,不会发生冲突
        2) 静态函数不能被其他文件所用。

四、static与const联合使用

      static与const作用:声明一个只读的静态变量
      开发使用场景:在一个文件中经常使用的字符串常量,可以使用static与const组合
        * 开发中常用static修饰全局变量,只改变作用域
        * 为什么要改变全局变量作用域,防止重复声明全局变量。
        * 开发中声明的全局变量,有些不希望外界改动,只允许读取。
        * 比如一个基本数据类型不希望别人改动
  // 声明一个静态的全局只读常量
  static const int a = 20;
  // staic和const联合的作用:声明一个静态的全局只读常量
  // iOS中staic和const常用使用场景,是用来代替宏,把一个经常使用的字符串常量,定义成静态全局只读变量.
  // 开发中经常拿到key修改值,因此用const修饰key,表示key只读,不允许修改。
  static  NSString * const key = @"name";
  // 如果 const修饰 *key1,表示*key1只读,key1还是能改变。
  static  NSString const *key1 = @"name";

五、extern与const联合使用

开发中使用场景:在多个文件中经常使用的同一个字符串常量,可以使用extern与const组合。
原因:
static与const组合:在每个文件都需要定义一份静态全局变量。
extern与const组合:只需要定义一份全局变量,多个文件共享。
全局常量正规写法:开发中便于管理所有的全局变量,通常搞一个GlobeConst文件,里面专门定义全局变量,统一管理,要不然项目文件多不好找。
GlobeConst.h
extern NSString * const nameKey = @"name";
GlobeConst.m
#import <Foundation/Foundation.h>
NSString * const nameKey = @"name";


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值