美文网首页
重温C语言(6)之存储类别

重温C语言(6)之存储类别

作者: 鸡蛋绝缘体 | 来源:发表于2020-02-09 10:19 被阅读0次

    原文:https://mp.weixin.qq.com/s/aDziV-VJ-5Wr6eitt7FaoA

    对象

    被存储的每一个值都占用一定的物理内存,这样的内存称为对象。

    int a = 73;
    

    a 是一个标志符。指定了硬件内存中的对象的方式,并提供了存储在对象中的值。

    int * p = &a;
    

    p 是一个标志符,指定了储存地址的对象。但是 *p 不是标志符,是个表达式, 它指向了一个对象。

    作用域

    作用域描述程序中可访问标识符的区域。
    一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或文件作用域。块是用一对花括号括起来的代码区域。

    函数原型作用域: int a(int a, double b) 小括号扩起来的就是函数原型作用域。
    文件作用域即声明的变量在所在文件从定义处到文件末尾都可用,这个变量也称为全局变量。

    int a = 10; // 本文件和同程序的其他文件都可以使用 a变量
    static b = 22;  // 只有本文件可以访问 b
    int main(){
    }
    

    如果文件要使用其他文件中的外部变量,则必须用 extern 声明该变量。

    extern char Name;
    

    存储期

    静态存储期

    存储在静态内存中存储地址不变但值可以变,在程序的执行期间一直存在,关键字 static 声明的变量变量具有静态存储期。

    线程存储期

    自动变量

    默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。可以使用 auto 关键字来显示的定义它。
    变量具有自动存储期意味着,程序在进入该变量声明所在的块时变量存在,程序在退出该块时变量消失。原来该变量占用的内存位置现在可做他用。
    自动变量不会初始化,除非显式初始化它。

    int a; // a 变量的值是之前占用分配给 a 的空间中的任意值(如果有的话),它不会是0
    int b = 10;
    

    寄存器变量

    变量通常储存在计算机内存中。如果幸运的话,寄存器变量储存在CPU的寄存器中,或者概括地说,储存在最快的可用内存中。
    由于寄存器变量储存在寄存器而非内存中,所以无法获取寄存器变量的地址。
    register 声明寄存器变量,编译器必须根据寄存器或最快可用内存的数量衡量你的请求,或者直接忽略你的请求,这个时候寄存器变量会成为普通变量,不管怎样都不能使用地址运算符。

    register int a;
    

    存储类别说明符

    1. auto说明符表明变量是自动存储期,只能用于块作用域的变量声明中;
    2. register说明符也只用于块作用域的变量,它把变量归为寄存器存储类别,请求最快速度访问该变量。同时,还保护了该变量的地址不被获取;
    3. 用static说明符创建的对象具有静态存储期,载入程序时创建对象,当程序结束时对象消失;
    4. extern说明符表明声明的变量定义在别处;

    注意:外部函数可以被其他文件的函数访问,但是静态函数只能用于其定义所在的文件。

    doubleg amma(double);/*该函数默认为外部函数*/
    static double beta(int, int);
    extern double delta(double, int);
    

    在同一个程序中,其他文件中的函数可以调用gamma()和delta(),但是不能调用beta(),因为以static存储类别说明符创建的函数属于特定模块私有。

    随机数

    rand()函数生成随机数字。这个函数取值范围是0RNAD_MAX之间,RAND_MAX被定义在stdlib.h中,其值通常是INT_MAX。

    下面实现一个自己的随机数程序:
    myrand.c 文件:

    static unsigned long int next = 1;
    
    unsigned int myrand(void) {
        // 生成伪随机数的魔法公示
        next = next * 1103515245 + 12345;
        return (unsigned int)(next / 65536) % 32768;
    
    }
    

    main.c 文件:

    #include <stdio.h>
    #include "myrand.c"
    extern unsigned int myrand(void);
    int main() {
        for (int i = 0; i < 10; ++i) {
            printf("%d\n", myrand());
        }
    }
    

    分配内存

    malloc()

    malloc() 函数可以在程序运行时分配更多的内存,该函数接受一个参数:所需的内存字节数。malloc()函数会找到合适的空闲内存块,这样的内存是匿名的,它会返回动态分配内存块的首字节地址。

    如果malloc()分配内存失败,将返回空指针。

    double * ptd;
    ptd = (double *) malloc(30*sizeof(double));
    

    上面代码为30个double类型的值请求内存空间,并设置ptd指向该位置。注意,指针ptd被声明为指向一个double类型,而不是指向内含30个double类型值的块。
    动态分配的内存数量只会增加,除非用free()进行释放。
    free() 函数的参数是指向malloc() 分配的内存的指针,用来释放这块内存。

    calloc()

    long * newmem;
    newmem = (long *)calloc(100, sizeof(long));
    

    calloc()malloc()类似, 返回指向void的指针。calloc()函数接受两个无符号整数作为参数(ANSI规定是size_t类型)。第1个参数是所需的存储单元数量,第2个参数是存储单元的大小(以字节为单位)。它会把分配的块所有位都设为0.

    也需要用 free() 函数进行释放。

    存储类别和动态内存分配有何联系?

    静态存储类别所用的内存数量在编译时确定,该类别的变量在程序开始执行时被创建,在程序结束时被销毁。

    自动存储类别的变量在程序进入变量定义所在块时存在,在程序离开块时消失。

    动态分配的内存在调用malloc()或相关函数时存在,在调用free()后释放。使用动态内存通常比使用栈内存慢。

    ANSIC类型限定符

    const

    声明对象的值可以初始化但是不能被修改。

    volatile

    volatile限定符告知计算机,代理(而不是变量所在的程序)可以改变该变量的值。通常,它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。例如,一个地址上可能储存着当前的时钟时间,无论程序做什么,地址上的值都随时间的变化而改变。

    restrict

    restrict关键字允许编译器优化某部分代码以更好地支持计算。它只能用于指针,表明该指针是访问数据对象的唯一且初始的方式。

    _Atomic

    并发程序设计把程序执行分成可以同时执行的多个线程,。当一个线程对一个原子类型的对象执行原子操作时,其他线程不能访问该对象。_Atomic 用来声明原子类型的变量。

    相关文章

      网友评论

          本文标题:重温C语言(6)之存储类别

          本文链接:https://www.haomeiwen.com/subject/ladgxhtx.html