• C的存储类别、存储说明符、限定符


    1 对象:从硬件角度看,每个数据值都需要占用物理内存,这个物理内存在C语言中称之为对象;

      对象:可以存储一个或多个值的物理内存;

      对象使用"存储期"描述物理内存属性,"作用域"和"链接"来描述使用属性;

    int i = 3;
    /*该声明创建了标识符 i ,也就是存储着数据3的对象 i ;*/
    /*程序通过创建标识符来表示特定的对象,实现访问物理内存;*/

      1.1 存储期:在内存中保存的时间;

        1.1.1 静态存储期:程序开始执行到结束的时期;静态指的是变量的内存地址不会改变,是静态的;供静态变量使用;

        1.1.2 自动存储期:程序进入变量所在块到退出变量所在块的时期;程序进入块时为块区域变量分配内存,退出块时释放内存;以堆栈的方式来处理;供自动变量使用;

        1.1.3 动态分配存储期:由程序调用malloc()等来分配,内存块较为分散导致速度慢;供动态内存函数分配使用;

        1.1.4 线程存储期:程序执行被分为多个线程,在某个线程中被声明之后一直存在直到线程结束的时期;属于以 _Thread_local声明的对象,每个线程都有该变量的私有备份;

      1.2 链接属性

        1.2.1 无链接属性:变量为当前块私有;

    #include<stdio.h>
    int count;
    static int num; 
    void main(void)
    {
        int apple;/*作用域在当前块,无链接属性*/
        ....;
    }

        1.2.2 内部链接属性:变量为当前文件私有;

    #include<stdio.h>
    int count;                 /*变量为外部链接属性,可与其他.h文件共享*/
    static int num;            /*变量为内部链接属性,不与其他.h文件共享*/
    
    int sum(int a, int b)
    {
        static int sum_cnt=0;   /*sum_cnt具有静态存储期,所以sum_cnt从程序开始到结束一直存在,就算程序没调用sum函数,sum_cnt也存在;*/
        sum_cnt++;              /*由于sum_cnt定义在函数块内,所以sum_cnt只对当前函数可见;*/
        return a+b ;
    }
    
    void main(void)
    {
        extern int count;      /*与前面的count是同一个count,此处声明可省略*/
        int count;             /*与前面的count不是同一个count,这个作用域为当前区域块*/
        count=sum(5,6);
    }

        1.2.3 外部链接属性;

    #include<stdio.h>
    extern int count ;    /*在其他文件中已经声明的具有文件作用域的变量,本文件调用的话要用extern重新声明一下*/
    int sum;              /*全局变量未初始化,会默认初始化为0,局部变量不会默认初始化*/
    void main(void)
    { 
    ... 
    }
    /*关键字extern声明属于引用声明,extern声明不会引起内存分配,该变量必须在其他文件中有过定义声明(第1次声明)*/

      1.3 作用域:对象可以被访问的区域;

        1.3.1 文件作用域:从定义处到当前文件结尾的区域;定义在函数体外的变量具有文件作用域;有链接属性;

    #include<stdio.h>
    int units =0 ;                 /*具有文件作用域,被称为全局变量*/
    int index ;                    //外部链接属性,静态存储期
    static int inter_uints = 0 ;   //内部链接属性,静态存储期
    int main(void)
    {
       int x = 33;       //外层块变量
         while(x++<36)
       {
             int x =100 ; //内层块变量,与外层x不是一个变量;
             printf("%d,x here is 100",x);
         }
        printf("%d,x here is 33",x);
         for(i=0;i<3;i++)
         {
            static int stay = 1; 
            printf("%d,print stay result is 1,2,3",stay);
            stay++;
            //所有静态变量在程序载入内存时初始化已经执行完毕;此处写初始化表示作用域只在当前块区域;
          //静态变量只会初始化1次,具有时序记忆性;如果不赋值,默认初始化为0;
            //不能在函数的形参中使用static;
         }
    }

        1.3.2 块作用域 :花括号括起来的区域;定义在块中的变量具有块作用域;无链接属性;

    void function(int i,int j)
    {                  /*i,j,k的块作用域*/
       int k;          //无链接属性,自动存储期
       static int p ;  //无链接属性,静态存储期
       for( int m=0;m<i;m++)
       {               /*m,n的块作用域*/  
         int n;    
       }   
    }
    /*其他函数可以通过指针形参或返回值,间接访问块区域的静态变量*/

        1.3.3 作用域不同的变量同名不冲突,作用域相同的变量同名冲突;

           构造类型在C中,有不同于普通变量的名称空间,名字相同也不会与普通变量冲突报错;

      1.4 存储类说明符

        1.4.1 register:寄存器类型

    register int i ;
    //寄存器变量存储在寄存器中,无法获取寄存器变量的地址
    //如果寄存器不够,编译器就会将其设置为自动变量;

         1.4.2 auto:自动存储类型

    auto int apple;
    /*代码块内声明的变量默认为auto类型;无链接属性;自动变量不会默认初始值;*/

        1.4.3 static :静态存储类型

    static int apple ;
    /*不管声明在什么位置,作用域都是当前文件;并且从程序执行开始一直存在直到程序结束;全局变量(静态变量)默认初始值为0;*/

        1.4.4 extern:外部链接类型

    extern int count ;    
    /*在其他文件中已经声明的具有文件作用域的变量,本文件调用的话要用extern重新声明一下*/ /*关键字extern声明属于引用声明,extern声明不会引起内存分配,该变量必须在其他文件中有过定义声明(第1次声明)*/

     2 动态内存分配

      动态内存分配的优点是内存时在程序执行的时候根据需求确定的,不会浪费内存;

      像数组结构体之类的在内存空间需要在程序执行之前由编译器确定,可能会造成内存浪费或分配不足;

      2.1 malloc()与free()

    double * ptr ;
    ptr = ( double * ) malloc( 33*sizeof(double) ) ;
    /*malloc()根据所需内存字节数33*sizeof(double) ,从物理内存中找到适合的空闲块,将该空闲块的首地址作为void类型的指针返回*/
    /* void类型的指针可以被强制转换为任意类型的指针,如指向数组的指针,指向结构的指针等; */
    /*malloc()动态分配不到内存,返回NULL空指针;*/
    
    int * array;
    int i=0;
    array = (int *) malloc(10* sizeof(int) );
    while(i<10 && scanf("%d", &array[i])==1)
    i++;
    printf("enter done ! ");
    /*分配了一个10个int型的动态内存,然后将键盘输入填入内存*/ 
    free(array);
    /*释放malloc()动态分配的内存*/
    int * array;
    array = (int *) malloc(10* sizeof(int) );
    free(array);
    /*malloc()与free()在一个函数中应该成对使用;*/
    /*如果一个函数每次调用malloc(),执行完之后没有调用free(),那么该函数返回后,指向malloc内存的指针释放,但是malloc分配的内存却还是在;*/
    /*如此多次调用之后内存可能就会耗尽,这类问题称为内存泄漏;*/

      2.1 calloc()与free()

    double * price;
    price=(double *) calloc(100,sizeof(double));
    /*calloc()分配了100个存储单元,每个单元的大小为一个double型  的动态内存;*/
    /*并且calloc()会默认把块中的所有位都置为0;*/
    ...
    free(price);
    /*释放内存;*/

     3 ANSI C类型限定符

      3.1 const

    const int size = 12 ;
    const int days[12]={31,28,31,30,3,30,31,31,30,31,30,31};
    /*const将变量声明为常量,只有第一次对变量赋值有效,之后赋值无效*/
    
    const int size ;
    size = 12 ;
    /*此处为无效错误示范*/

        3.1.1 const 与指针

    // const  double * ptr ;    常用于函数形参中
    void sum(const double *ptr , int size);
    {
        double rates[size];
        for(int i=0; i<size;i++)
        {    
            rates[i]=*ptr ;
            ptr++;
        }
        ...
    }
    /* const  double * ptr ;  表示该指针指向的数据类型不会被指针修改,但是指针指向的地址可以修改*/
    /*需要注意:非const类型的数据地址可以赋值给const的地址,这只是表示我们使用该const变量的时候不会改变数据*/
    /*另外一提:普通指针不能指向const数据类型:普通指针可以修改数据,但是const数据又不许修改*/
    
    //double * const ptr2 ; 
    double rates[5]={8.9,[3]=3.2};
    double * const ptr2=rates;
    *ptr2=100.5;
    /* double * const ptr2 ; 表示该指针指向的地址不允许修改,但是该地址内的数据可以修改*/
    
    /* const double * const ptr;  表示不修改指针指向的数据类型和存储的地址*/
    /*  const double *ptr;和 double const * ptr;是等价的;*/

      3.2 volatile:告诉编译器不要优化编译,主要用于硬件地址以及在其他程序或其他线程中共享数据;

    volatile int i ;
    int val1,val2;
    val1=i;
    .../*一些不改变 i 值的代码*/
    val2=i;
    /*编译器第一次使用了i的值之后在寄存器中有临时缓存,val2再使用i值时默认从寄存器中提取,优化编译器速度;*/
    /*使用volatile,则告诉编译器不要优化,应该从内存地址中查找使用值;该值可能会被其他程序或硬件改变了*/
    volitile const int loc ;
    volitile const int *ploc ;
    /*表示 用const将硬件时钟设置为不能更改的变量,但是可以通过代理(其他程序或硬件)改变;我不理解这个意思*/
    /*const 和 volitile 同时使用的顺序不重要,反正都是限定符;*/

      3.3 restrict :告诉编译器要优化编译,只能用于指针,表明该指针是访问对象唯一且初始的方式;

    int * ptr = (int * ) calloc(100,sizeof(int));
    ptr[2]+3;
    prt[2]+5;
    /*表示  指针ptr  是唯一且初始的访问动态内存的方式;*/
    /*ptr[2]+3,ptr[2]+5 可以一起优化成 ptr[2]+8 编译;而不用担心中途ptr[2]的数值是否被其他代理改变了*/
    int array[10];
    int * parray = array ;
    /*此处的指针parray并不是访问数组array的唯一方式,也不是初始方式;其他地方如果有使用,编译器不会去优化编译;*/

      3.4 _Atomic:声明原子变量,由stdatomic.h和threads.h管理;

    #include<stdatomic.h>
    #include<threads.h>
    int
    age; age = 12 ; /*可以替换如下,将其变为原子对象,当程序对原子对象进行操作时,其他线程不能访问该对象;*/ _Atomic int age ; atomic_store(&age,12) ; /*C11新特性,有的编译器不一定支持;*/

      

  • 相关阅读:
    多层结构中,事务的运用。
    A private conversation
    Sql Server 日志清理 (数据库压缩方法)
    Basic of Ajax
    Persin Buttons
    不知为什么无缘无故加到了一个“邯郸.net俱乐部”,想退出,找不到入口.....
    Wokflow designer not working when openning workflow in nonworkflow VS 2005 project
    GridView中如何取得隐藏列的值?
    Error: cannot obtain value
    Too late
  • 原文地址:https://www.cnblogs.com/caesura-k/p/12340843.html
Copyright © 2020-2023  润新知