static完全解析

Posted 阿甘横行

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了static完全解析相关的知识,希望对你有一定的参考价值。

  static是很重要的一个关键字,它出现在不同地方时存在不同的作用,一般用的少的话会觉得没必要使用,或者是对它的作用感觉云里雾里,这些都是我曾经的感觉,今天就对这个关键字进行一个全面的解析,揭开它的神秘面纱。

  但开始我想说一下对于这个关键字的了解的一个历程,最开始使用时我只了解一个它的作用,那就是可以在函数体内修饰一个局部变量,那么这个局部变量只会初始化一次(可以把初始化代码和使用变量的代码块放一起,而不用考虑是不是在循环中,会重复初始化变量的问题)。所以在写程序时我也是这样用的,一知半解的东西就这样写在代码里了。按理来说只是用它修饰局部变量,也不应该出什么问题,但事实是,程序的执行结果还是超出预期了。

  当时碰到一个需求,简化一下解释起来就是:给你一个线段的两个端点的坐标,如(0,10),需要你求这个线段的中点,并要得到中点和端点为两端的新线段的中点,这样多次套娃。再通俗一点就是求一个线段多次对折的中点值。我当时想的很简单啊,直接递归不就完了,所以第一次写出来的方法大致如下:

/****************************************************
 * 参数: n,折叠后线段的最小粒度
 * 参数: a,线段的左端点值
 * 参数: b,线段的右端点值
 ****************************************************/

#include<stdio.h>

int func(double n,double a, double b)
{
  double c = 0;
  if ((b - a)< 2*n) {
    return 0;
  }else {
    c = (a+b)/2;
    printf("%f \\n",c);
    return func(n, a, c) + func(n, c, b);
  }
}

int main()
{
  func(1, 0, 10);
  return 0;
}

  这个代码只做演示,参数校验啥的都没有,凑合看看。当时程序运行起来效果和设想的并不一样,但我苦思冥想,再经过去厕所放松一次才想明白问题出在哪里,那就是函数中的static关键字。

         未添加static结果

                                           

        添加static结果

  在func函数中变量c一旦用static关键字修饰后那么打印结果便会出现错乱,毫无规律,而变量中会在递归中被调用。后面我去查了static关键字的特性发现了端倪,下面就介绍一下其特性,大概可以从两点展开:

  1.生命周期。被static关键字修饰的变量无论是局部变量和全局变量都会在内存静态变量区获得专属的内存,并且生命周期会一直持续到程序结束。就算是静态局部变量也不会在函数调用后被销毁内存中的数据,而是会一直保存当前值在所属内存区域。

  2.作用域。被static关键字修饰的全局变量作用域在定义其的源文件中,修饰局部变量时则作用域只在函数体内。在华为代码规范中也有提到,只在一个源文件使用的全局变量需要加上static修饰,函数前面加static使得函数成为静态函数,此时函数的作用域仅限于本文本,使得不同的人编写不同的函数时,不用担心自己定义的函数是否与其它函数同名。局部变量最好不要使用static修饰,除非你对该变量的用途和使用一清二楚。

  ps:静态修饰变量在程序刚运行便会进行唯一一次初始化,并初始化为0值。

  像我上面碰到的问题,则是变量C由于被static修饰,在左递归完成后,C值改变然后传入右递归造成的。我当时并没有完全了解static关键字的作用,也完全没有料想到程序会出现的结果,这必须引以为戒。

额外拓展

  既然提到了static关键字,那么这里就对剩余几个需要关注的关键字再进行介绍:const, volatile, auto, register.这几个关键字有的常见,有的生僻,但是都有其独特的作用且易被误用或忽视,所以放在此处进行记录。

  auto:声明自动变量,缺省时编译器一般默认为auto;C语言中,当省略了数据类型,则使用auto关键字修饰的变量默认为int 型数据。

    volatile:说明变量在出现执行中可被隐含地改变 ,编译器不会去假设这个变量的值了。精确地说就是,编译器器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

  1). 并行设备的硬件寄存器(如:状态寄存器)。

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

  3). 多线程应用中被几个任务共享的变量。

  const:声明只读变量,其修饰的只读变量必须在定义的同时初始化。最常见用法可能是定义常量,但同样令人迷惑的便是其在修饰指针变量时的用法,但只要理解其特性,也就没那么神秘了。总的来说就是修饰的变量具有只读(不可写、不可更改)的特性。

  const int a;

  int const a; //const int a等价于int const a

  const int *a;//a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可改,如int b = 1;a = &b; )

  int * const a;//a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)

  int const * a const;//a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)

   register:声明寄存器变量;register变量必须是能够被CPU寄存器所接受的类型,也就意味着register变量必须是一个单个的值,并且长度一个小于或者等于整型长度,而且不能用&来获取register变量的地址,因为此变量可能不存放在内存中。

以上是关于static完全解析的主要内容,如果未能解决你的问题,请参考以下文章

无法解析片段中的 ViewModelProvider 构造?

Relay.js 没有正确解析组合片段

Android 逆向使用 Python 解析 ELF 文件 ( Capstone 反汇编 ELF 文件中的机器码数据 | 创建反汇编解析器实例对象 | 设置汇编解析器显示细节 )(代码片段

从 XML 声明片段获取 XML 编码:部分内容解析不支持 XmlDeclaration

CardView 在我的片段上不能完全滚动

完全下载文件时,将下载的文件从一个片段传递到另一个片段