高手在写代码的时候,会特别注重的一点就是,对外的方法对外,不对外的方法和数据,都是让其对外不可见,也就是俗称的,内部不想让别人直接访问的方法,变量,就不要让其可以访问到,从而保证几点:
1 不污染全局符号
2 数据和方法保证不被不规范的引用
而C语言中的关键字 static 就是起这个作用,我们这节来讲讲这个玩意。再说这个前,我想跟大家先讲一个内容,之前也说过。
代码段
数据段
BSS段
堆
栈
代码段是只读区域,这个段落不允许修改,加载时候会指定只读。
数据段分为,只读区域和可变区域。只读区域就是类似于常量数据,还有常量字符串这些,同时还有一些使用const 关键字修饰的,会放到这里,标记只读,保证数据不会被随意修改。
BSS段放置的是全局的未初始化的变量,这个编译成可执行文件因为没有初始化数据,所以可执行文件中就不占大小,但是如果初始化了,就会占大小。
堆,我们使用 malloc 申请内存空间,就是在这个区域,这个区域是需要主动去释放空间,否则会内存泄漏。
栈,函数内部的局部变量,以及函数之间调用,临时使用的空间,这个是依赖栈的压入,弹出机制,重复在用。(这里用一个网上图片,大家一目了然)
栈我们一般不用关注,它会自动平衡,但是我们一般在栈里面要考虑的就是栈溢出。栈也是有大小的,系统会分配一段区域,用在压栈,弹出操作,如果你两个函数相互调用,就会出现栈溢出。
为什么要说这个?其实就是因为 static 关键字,有一个功能,就是会让局部变量本该在栈上,用这个声明后,就会进入全局区域,也就是从栈区域,进入到数据区域。
下面我们来看下代码:add.c
hello.c
我们这里看下,没有#include “add.h”, 我们使用
gcc -o add.o -c add.c
gcc -o hello.o -c hello.c
gcc -o hello hello.o add.o
发现是可以编译过,也能链接过,也能运行。我们使用readelf -s add.o ,看到信息,add前面有个Bind的值是GLOBAL,这个就是说明,add是在可以让其他.o链接时候去查找的一个符号,它的类型是func函数。
然后我们把add.c 改成这个:(前面加上一个static)
编译链接就会失败,找不到add方法,那么我们readelf -s add.o再看下:
add前面Bind的值变成了LOCAL,这时候对外就不可见了。这也就是static在全局的时候,起的作用,将对应的变量,或者函数的可见范围,变成只对自身可见,对外不可见。
这个是为了降低重名的几率,也是防止自身的全局变量被外部访问,瞎修改的风险。
为了说明这个情况,我们来演示下这个过程,add.c改成这样:
hello.c 改成
编译执行后,最终结果输出17,是不是让我们一下子就不明白,a b两个值被外部随意修改,导致没法看懂代码业务。
我们看到有两个类型是 OBJECT 的数据,全局的,a和b。这样子这两个值就是可以被外部引用,进行修改的了。
如果我们把 add.c 中的 int a=5;改成static int a=5;那么最终链接的时候hello.o 就找不到a变量,导致链接失败。
这也就是static的作用,让只想对当前的.c能访问,做的对修饰的变量或者函数进行限定。
于是,我们常规的模式就是,对内的都用static进行修饰,然后放出对外函数,让其操作数据,保证入口的统一性,也更容易追踪。具体如下:
用静态修饰变量,不让外部直接访问,然后对外放出函数,让其通过函数修改数据,保证了入口的唯一性,这样子调试也很简单,直接在函数设置断点,打印堆栈,直接能找到调用方。
而如果说你让int a这种随意的变量放置到全局,就是一个灾难,会出现莫名奇妙被修改,找半天找不到被谁修改的问题。
说完了全局变量,全局函数前面加static,下来说下函数内部,添加static的妙用。
函数中的局部变量,默认是都在栈上面,这类变量最大的特点就是,函数返回之后,这段区域就会被其他函数调用时覆盖,重复利用,这也是栈的特点。
那么如果我们想做一个记数,用来记录这个 add 函数被调用的次数。该如何设计呢?
我们可以用一个全局静态变量来记录,比如 static int call_add_times=0;
然后在add方法里面进行记数,这样来操作。
这样子不舒服的一点就是,本该是在add内部的一个变量,被丢到全局区域,这样子就会有个问题,会被本文件的其他地方调用,但我们设计的这个变量,只想让add函数用。
于是,我们可以这样子处理:
int add()
{
static int call_add_times=0;
call_add_times++;
return a+b;
}
这样子操作后,call_add_times就会从栈区域进到全局变量区域,但是又能保证只对add函数可见,其他函数都调用不到。
你说,妙哉否?
我们在学习一些语言,技巧的时候,尽量要去思考它的使用场景,这样子的话,你就知道,它的每个设计,都是为了解决一类问题,也就是需求推动设计。
好了,今天就说的这里,不知道你学的开心不开心?下一节我们来说下,关于inline 关键字的用法,这个比较简单,我们下一节见!
喜欢,帮忙转发~~
~~ end not end ~~
热门文章
职场老鸟,互联网十年从业生涯,分享 [Java,Python,安卓,AI,爬虫] 技术文章,学习资料, 热点趣闻等。关注回复 1024 Python 电子书大全 面试资料,给你一份私藏的程序员好礼,永远更新中!赶紧来关注哦!
我的微信 code_gg_boy 。