static,const,extern,auto的区别和用法

1.const

const意味着"只读",下面的声明都是什么意思? 

const int a; 

int const a; 

const int *a; 

int * const a; 

int const * a const;

前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

结论:·;关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果 你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清 理的。)  ·; 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。  ·; 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。 

(1)欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初 始化,因为以后就没有机会再去改变它了;

(2)对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指 定为 const;

(3)在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值; 

(4)对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;

 (5)对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”。

关键字volatile有什么含意?并给出三个不同的例子。一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 

· ;并行设备的硬件寄存器(如:状态寄存器) 

· ; 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

· ; 多线程应用中被几个任务共享的变量

· ;一个参数既可以是const还可以是volatile吗?解释为什么。 

· ; 一个指针可以是volatile 吗?解释为什么。 

下面是答案: 

· ; 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 

·; 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 

2.static关键字的作用:

1)static修饰的变量存到静态存储区,该变量所占空间会一直保存到程序退出 ,只有第一次调用该函数的时候才会等于初始值,以后该变量都会保存改动后(最后一次调用)的值,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值应该慎重使用静态变量,因为静态变量会一直占据固定的内存。

2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问,在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明 它的模块内;

再下列情况使用静态变量:

1.需要变量能保留上次调用结束时的值。

2.希望变量只初始化一次,以后只是被引用,而不希望对其重新赋值。

实际上,程序员也可以不对 static 变量指定初始值,那么系统会在编译时自动为 static 变量分配默认的初始值。默认初始值都是广义的0.对整型 static 局部变量,该初始值总是0;对于浮点型的 static 局部变量,初始值总是0.0;对于指针型的 static 局部变量,初始值总是0x0(未指向任何有效对象的指针) 

3.extern "C"的作用:

extern是C/C++语言中表明函数和全局变量作用(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其他模块中使用。

4.auto

auto指定该变量采用自动存储机制-局部变量默认采用这种方式,对定义局部变量,auto关键字可以忽略。


在正常开发中,我们常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用预处理时间的关键字来修饰常量。那么关键字修饰的常量和宏的区别有哪些呢

编译时刻:宏是预编译(编译之前处理),const是编译阶段。

编译检查:宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。

宏的好处:宏能定义一些函数,方法。 const不能。

宏的坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换。

    注意:很多Blog都说使用宏,会消耗很多内存,我这验证并不会生成很多内存,宏定义的是常量,常量都放在常量区,只会生成一份内存。以测试过的确如此

首先来说一下static

static在修饰局部变量,

1.延长局部变量的生命周期,程序结束才回销毁。

2,局部变量智慧声称一份内存,只会初始化一次。

3.改变局部变量的作用域。

在修饰全局变量:

1.只能在本文件中访问,修改全局变量的作用域,生命周期不变。这句话是这样理解,当在一个a类的.m文件顶部写入一个全局变量

     NSString * testExtern =@"xulin";这样编译器会默认为这个变量创建一个“外部符号,当在例如一个b类的.m文件中协商

     externNSString * testExtern;会去葱外部符号表中取这个字段,可以使用,如果 NSString * testExtern = @"xulin";注释掉,会变异编译报错。如果我们用static生命��会发生 什么呢?在a类的.m文件中将 NSString * testExtern = @"xulin" 换成  static NSString * testExtern = @"xulin",这样当编译的时候会报错,因为用static生命的全局变量不会放入外部符号表中,这个testExtern只能在本文件中访问使用。


2.避免重复定义全局变量。

第二个我们来说一下const

const用来修饰的常量,并且是只读的,强制改的话会报错。

const int a = 10;

int const a = 10;

上面两个效作用时一样的。

最后来谈一下extern

externextern不能定义一个变量,extern表示的是已经存在一个变量,但是不在当前的编译单元内,需要在其他编译单元中寻找

在一个文件情况下extern:

int i=10; //这个是定义一个全局变量,值为10
extern int i=10; //这个是错误的,变量已经存在,怎么赋初值呢?extern不能定义一个变量

在多个文件情况下extern

文件一:(假设这个文件没有错误!!)
int a=10;
文件二:
extern int a; // 这个正确
extern float a; // 这个编译不会错,赋值会怎样呢?
extern int b; //这个找不到  报错
int a=9; //这个出错,全局变量多次定义   报错信息如下   这两个文件其实都没有互相应用的关系,难道也不能用用一个全局变量么?

ld: 1 duplicate symbol for architecture x86_64

clang: error: linker command failed with exit code 1 (use -v to see invocation)

int a; //同上



下面最新版。

一,static静态变量

1.在修饰局部变量的时候:

保证局部变量永远只初始化一次,在程序的运行过程中永远只有一份内存,生命周期类似全局变量,但是作用域不变。这句话怎么理解呢?还是上代码例子来讲解:

随便建一个工程,在一个控制器类上监听控制器view的点击时间方法;

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{    //声明一个局部变量i
    int i = 0;    //每次点击view来到这个方法时让i自增
    i ++;    //打印结果
    NSLog(@"i=%d",i);
}

输出日志如下:

2016-10-26 14:58:48.290 fff[2760:170260] i=1
2016-10-26 14:58:49.044 fff[2760:170260] i=1
2016-10-26 14:58:49.200 fff[2760:170260] i=1....
从输出日志中我们可以看到 i 一直等于1,这也是预料之中的,因为每次点击进入这个方法就会重新初始化一个全新的变量 i  = 0,加加了一次后值变为1,然后打印出结果为1,出了这个方法后局部变量 i 就被释放回收。所以每次打印出来的结果都为1。

但是我们再看看局部变量i被关键字static修饰后的情况:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{    //声明一个局部变量i
  static  int i = 0;    //每次点击view来到这个方法时让i自增
    i ++;    //打印结果
    NSLog(@"i=%d",i);
}

输出日志如下:

2016-10-26 15:07:34.276 fff[2817:175155] i=1
2016-10-26 15:07:35.347 fff[2817:175155] i=2
2016-10-26 15:07:35.761 fff[2817:175155] i=3
2016-10-26 15:07:36.057 fff[2817:175155] i=4
2016-10-26 15:07:36.415 fff[2817:175155] i=5....

上面日志中可以看到i的值一直在自增。什么,它不是每次进去都被初始化赋值为0了么,怎么能累加呢。这就是关键字static修饰的局部变量的作用,让局部变量永远只初始化一次,一份内存,生命周期已经跟全局变量类似了,只是作用域不变。

2.在修饰全局变量的时候:

使全局变量的作用域仅限于当前文件内部,即当前文件内部才能访问该全局变量。【可能到这里你会有疑问,我在一个cell的.h文件定义了这样一句, static nsstring * const name = @"haha",然后在控制器中#import引入了cell的.h,这个时候我们可以拿到name,这样不是和这句话矛盾了么?其实上面这句话是针对不使用#improt的情况,因为improt了相当于完全可以访问这个.h的所有变量属性函数等等了。这句话的真实含义应该蔗糖理解,如果不使用static修饰,并且在控制器中页不#improt这个cell的.h,我们可以在控制器的.m顶部全部变量的位置,写上 extern nsstring * const name ;然后我们就可以直接用了,有extern修饰,编译器会自动认为这个name已经在其他文件定义过了,是外部变量,直接使用即可。但是当cell的.h加上了static修饰,还是这样在控制器里extern nsstring * const name ;其实会编译报错,因为static已经修饰了代表职能在本文件访问,用extern不行的,因此我们可以得出结论,static修饰在本文件可用,只是针对extern而言。#improt和这个完全是两回事。不要混淆。并且这里有一个注意点,cell的.h写了static nsstring * const name = @"haha",在其她文件的.h就没办法写extern nsstring * const name ,因为命名冲突了,要不两个都在.m要不一个在.h一个在.m

iOS中在一个文件声明的全局变量,工程的其他文件也是能访问的,但是我又不想让其他文件访问,这时就可以用static修饰它了,比较典型的是使用GCD一次性函数创建的单例,全局变量基本上都会用static修饰。

下面是一个GCD一次函数创建的单利

@implementation LoginTool
//static修饰全局变量,让外界文件无法访问
static LoginTool *_sharedManager = nil;

+ (LoginTool *)sharedManager {   
   static dispatch_once_t oncePredicate;   
   dispatch_once(&oncePredicate, ^{
        _sharedManager = [[self alloc] init];
    });   
   return _sharedManager;
}
3.修饰函数

(3)修饰函数
static修饰函数时,被修饰的函数被称为静态函数,使得外部文件无法访问这个函数,仅本文件可以访问。这个在oc语言开发中几乎很少用,c语言倒是能看到一些影子,所以不详细探讨。

2、extern

这个单词翻译过来是“外面的、外部的”。顾名思义,它的作用是声明外部全局变量。这里需要特别注意extern只能声明,不能用于实现。

在开发中,我们通常会单独抽一个类来管理一些全局的变量或常量,下面来看看逼格比较高的一种做法:

我们可以在.h文件中extern声明一些全局的常量

//声明一些全局常量
extern 
extern NSString * const name;extern NSInteger const count;

然后在.m文件中去实现

#import //实现
NSString * const name = @"王五";
NSInteger const count = 3;

这样,只要导入头文件,就可以全局的使用定义的变量或常量。

其实上面这种写法很多人都会这样写,装逼神器么?然而我觉得就一个好处,就是不会将常量的具体值爆漏在.h,在.m赋值定义。

.h其实就是使用了extern的特性,编译起看到就会明白这个常量是在其他地方定义过了,.h只是声明外部全局变量。因为.h和根本没有引入.m,但是.h这里使用

extern NSString * const name其实已经拿到了真实值王五,并且如果在控制器中#improt这个.h,在控制器可以直接使用拿到name的王五这个真实值,就是利用了extern特性。
上面是extern一种使用场景,还有就是上面1中讲解static中的用法,在某个文件定义了 
NSString * const name = @"王五";但是某个文件和这个完全没有关联关系,但是我们照样可以在全局变量位置写上
extern。NSString * const name,从而拿到name使用。
虽然使用场景好像有点差别,但是都是利用了extern这个特性。extern声明  编译器久会认为这个常量在其他文件定义过了,不论和定义的那个文件相关或者不相关的,在这里都可以直接使用。

 1、const

这个单词翻译成中文是“常量”的意思。在程序中我们知道“常量”的值是不能变的,固定的。所以const关键字的作用就呼之欲出了:

(1) const用来修饰右边的基本变量或指针变量 (2) 被修饰的变量只读,不能被修改

下面举个最简单的例子:

//声明一个int类型的变量a,变量初始化值为10,并且变量a左边有一个const关键字修饰
int  const  a = 10;
//因为变量a被const修饰,就成为了只读,不能被修改赋值了,所以下面这行代码是错误的
a = 20;
//错误代码
//上面第一句代码和这句代码是等价的,都是修饰变量a让其只读
const  int   a = 10;

下面再看一组练习,这组练习完成后,相信你就完全理解const的用法:

分别指出下面四行代码中 *p 和p是只读还是变量

int  const  *p   //  *p只读 ;p变量
int  * const  p  // *p变量 ; p只读
const  int   * const p //p和*p都只读
int  const  * const  p   //p和*p都只读

注: 判断p 和p是只读还是变量,关键是看const在谁前面。如果只在p前面,那么p只读,p还是变量;如果在p前面,那么p只读 ,p变量。

const的常用用法:

//定义一个全局只读变量
NSString  * const Kname = @"appkey";
//static修饰后此全局变量只能本文件访问
static NSString *const Key = @"hddjj”;
参考链接:http://www.cocoachina.com/ios/20161110/18035.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值