[iOS]static、extern、const关键字比较
[iOS]static、extern、const关键字比较
文章目录
- [iOS]static、extern、const关键字比较
- 全局区地址如何分配
- 静态区安全测试
- static、extern、const关键字
- 具体的例子
- 关于extern关键字
- 关于static关键字
- 关于静态变量
- 类的静态变量
- 参考博客
介绍这三个关键字之前先补一下课
全局区地址如何分配
先来一段代码
不难看出下面的clA bssA bssStr1属于未初始化的全局变量和静态变量,在BSS段
clB bssB bssStr2属于已初始化的全局变量和静态变量,在DATA段
int clA; int clB = 10; static int bssA; static NSString *bssStr1; static int bssB = 10; static NSString *bssStr2 = @"bss"; - (void)testConst { NSLog(@"clA == \t%p",&clA); NSLog(@"bssA == \t%p",&bssA); NSLog(@"bssStr1 == \t%p",&bssStr1); NSLog(@"clB == \t%p",&clB); NSLog(@"bssB == \t%p",&bssB); NSLog(@"bssStr2 == \t%p",&bssStr2); }
然后我们看打印结果
它们的内存分配有什么规律
可以看出:
未初始化的全局变量和静态变量,在BSS段
BSS段的地址分配时,是低地址 -> 高地址
已初始化的全局变量和静态变量,在DATA段
DATA段的地址分配,与变量定义的顺序无关
静态区安全测试
静态变量的作用范围是当前文件内。
相当于引用别的文件时,底层会深拷贝一份静态变量,放在了自己的文件中,以后访问及操作的都是本文件内的这个变量,对别的文件没有影响。
当前文件更改静态变量后,本文件内再访问,是更改后的值,但不影响别的文件中的这个静态变量的值。
别的文件引入静态变量后,拿到的是静态变量的初始值,修改后再访问是自己修改后的值。
static、extern、const关键字
extern
extern关键字用来声明变量或者函数是一个外部变量或者外部函数,也就是说告诉编译器该变量是在其他文件中定义的,编译的时候不要报错,在链接的时候按照字符串寻址可以找到这个变量或者函数
如果A.h中定义了全局变量比如int a;,那么在其他文件中的函数调用变量a的时候需要在对应头文件或者定义文件中(保证在使用这个变量前)使用extern int a;来声明这个变量,但是这样做有一个弊端,首先如果A.h中集中定义了大量的全局变量供其他文件使用,那么其他的调用文件中会重复的出现大量的extern语句,第二,如果其他文件直接引用A.h,那么会造成全局变量的重复定义,编译不过,等等
所以我们应该
1、在定义文件中定义全局变量, 比如A.m中定义全局变量 int aa;
2、在对应的头文件A.h中声明外部变量 extern int aa;
3、在使用aa变量的文件B.m中包含A.h;
在编译阶段,B编译单元虽然找不到该函数或变量,但它不会报错,它会在链接时从A编译单元生成的目标代码中找到此函数。
static
使用static修饰变量,就不能使用extern来修饰,即static和extern不可同时出现
static修饰的全局变量的声明与定义同时进行,即当你在头文件中使用static声明了全局变量,同时它也被定义了。
static修饰的全局变量的作用域只能是本身的编译单元。如果有多个文件包含了定义static变量的头文件,在其他编译单元使用它时,只是简单的把其值复制给了其他编译单元,其他编译单元会另外开个内存保存它,在其他编译单元对它的修改并不影响本身在定义时的值。(与头文件中定义const类似)
即在其他编译单元A使用它时,它所在的物理地址,和其他编译单元B使用它时,它所在的物理地址不一样,A和B对它所做的修改都不能传递给对方
多个地方引用静态全局变量所在的头文件,不会出现重定义错误,因为在每个编译单元都对它开辟了额外的空间进行存储。
一般定义static 全局变量时,都把它放在.m文件中而不是.h文件中,这样就不会给其他包含此头文件的编译单元里边重复生成变量。
在标准C++中引入命名空间之前,程序必须将名字声明为static,使他们用于当前编译单元。C++文件中静态声明的使用从C语言继承而来,在C语言中,声明为static的局部实体在声明它的文件之外不可见。
C++不赞成文件静态声明。C++标准取消了在文件中声明静态声明的做法。应该避免static静态声明,而在源文件中使用未命名的命名空间,在未命名的命名空间中定义变量。
未命名的命名空间仅在文件内部有效,其作用范围不会横跨多个不同的文件。
具体的例子
关于extern关键字
在头文件里这么写是合理的
//.h static NSString * const myString = @"foo";
但是其实这是不正确也并不安全的一种写法
想让这个常量字符串被其他的类所正确使用
那么在我的头文件里应该这么声明
//.h extern NSString * const myString;
然后在每个引用头文件的源文件内
//,m NSString * const myString = @"foo";
何意呢家人们 为什么要这么写 到底安全在哪里?
在每个编译单元内,也就是通俗说在每个.m文件内
用static const修饰的myString内容都是 foo 没问题
但这些myString其实是不同的对象
static修饰的全局变量的作用域只能是本身的编译单元。如果有多个文件包含了定义static变量的头文件,在其他编译单元使用它时,只是简单的把其值复制给了其他编译单元,其他编译单元会另外开个内存保存它,在其他编译单元对它的修改并不影响本身在定义时的值。
也就是说这里的static和const功能重复了
多个.m文件文件内存在的都是myString对象的复制
并没有实现多个文件共同使用咱们的一个myString对象
而extern关键字使我们的myString对象变成可共同使用的外部变量
然后就是注意extern的使用事项
1、在定义文件中定义全局变量, 比如A.m中定义全局变量 int aa;
2、在对应的头文件A.h中声明外部变量 extern int aa;
3、在使用aa变量的文件B.m中包含A.h;
关于static关键字
关于前面提到static的部分
我们来一段栗子
先在test1.h定义字符串 g_str
//test1.h #ifndef TEST1H #define TEST1H static char g_str[] = "123456"; void fun1(); #endif
在test1.cpp中使用变量 g_str
//test1.cpp #include "test1.h" void fun1() { cout cout g_str[0] = 'a'; cout cout fun1(); // a23456 fun2(); // 123456 } counter++; } + (NSInteger)getCounter { return counter; } @end public: void myMethod(); }; return a + b; }