C中的内存管理

存储类别、链接和内存管理

关键字:auto 、extern 、static 、register 、const、volatile、restricted、_Thread_local、_Atomic

函数:rand() ,srand(),time(),malloc,calloc().free()

如何确定变量的作用域(可见的范围)和生命周期

C中的内存管理_第1张图片
内存管理

存储类别

作用域

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

链接

C变量有3种链接属性:外部链接,内部链接或无链接。具有块作用域、函数作用域或函数原型作用域的变量都是无链接变量。这意味着这些变量属于定义它们的块、函数或原型私有。具有文件作用域的变量可以是外部链接或内部链接。外部链接变量可以在多文件程序中使用,内部链接变量只能在一个翻译单元中使用。

那么如何知道文件作用域变量是内部链接还是外部链接?,可以查看外部定义中是否使用了存储类别说明符static

存储期

作用域和链接面述了标识符的可见性。存储期面述了通过这些标识符访问的对象的生存期。C对象有4种存储期:静态存储期、线程存储期、自动存储期、动态分配存储期。

如果对象具有静态存储期,那么它在程序的执行期间一直存在。文件作用域变量具有静态存储期。

线程存储期用于并发程序设计,程序执行可被氛围多个线程。

块作用域的变量通常都是具有自动存储期。

自动变量

属于自动存储类别的变量具有自动存储期、块作用域且无链接。默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。为了更清楚地表达你的意图可以显式使用关键字auto

寄存器变量

变量通常存储在计算机内存中,如果幸运的话,寄存器变量存储在CPU的寄存器中,或者概括地说,存储在最快的可用内存中。与普通变量相比,访问和处理这些变量的速度更快。由于寄存器变量储存在寄存器而非内存中。所以无法获取寄存器变量的地址。

绝大多数方面,寄存器变量和自动变量都一样。也就是说,它们都是块作用域、无链接和自动存储期。使用存储类别说明符 register 便可声明寄存器变量:

int main(void){
    register int quick;
}

块作用域的静态变量。

静态变量(static variable)听起来自相矛盾,像是一个不可变的变量,实际上,静态的意思是改变量在内存中原地不懂,并不是说它的值不变。具有文件作用域的变量自动具有(也必须是)静态存储期

存储类别说明符

C语言有6个关键字作为存储类别说明符:auto 、register 、static、extern 、_Thread_local 和typedef。

auto
自动存储期。只能用于块作用域的变量声明中。由于在块中声明的变量本身就具有自动存储期。所以使用auto主要是为了明确表达要使用与外部变量同名的局部变量的意图。

register
只能用于块作用域的变量,它把变量归为寄存器存储类别,请求最快速度访问该变量。同时,还保护了该变量的地址不被获取。

static
具有静态存储期。只能用在当前定义作用域内

extern
书本解释过于复杂,其实属于一种默认。默认函数时候。只是当前定义extern的时候,说明我们是引用其他定义的函数。

掷骰子

/**
* 掷骰子
*/

#include "diceroll.h"
#include 
#include 

int roll_count = 0;

static int rollem(int sides)
{
    int roll;
    roll = rand() % sides +1;
    ++roll_count;

    return roll;
}

int roll_n_dice(int dice,int sides)
{
    int d;
    int total = 0;
    if(sides < 2){
        printf("Need at least 2 sides.\n");
        return -2;
    }

    if(dice < 1){
        printf("Need at least 1 die.\n");
        return -1;
    }
    for(d=0; d

main

#include "diceroll.h"
#include 
#include 
#include 

int main(void)
{
    int dice,roll;
    int sides;
    int status;

    srand((unsigned int) time(0));

    printf("Enter the number of sides per die,0 to stop.\n");

    while(scanf("%d",&sides)==1 && sides > 0)
    {
        printf("How many dice?\n");
        if ((status = scanf("%d",&dice)) !=  1)
        {
            if(status == EOF)
                break;
            else{
                printf("You should have entered aninteger.\n");
                printf("Let's begin again .\n");
                while(getchar() !='\n')
                    continue;
                printf("How many sides ? Enter 0 to stop\n");
                continue;
            }

        }
        roll = roll_n_dice(dice,sides);
        printf("You have rolled a %d using %d %d-sided dice.\n", roll dice,sides);
        printf("How many sides? enter 0 to stop\n");

    }
    printf("The rollem() function was called %d times\n",roll_count );
    printf("GOOD FORTUNE TO YOU!\n");

    return 0;
}

分配内存:malloc() 和 free()

之前我们讨论的存储类别有一个共同之处:在确定用哪种存储类别后,根据已制定号的内存管理规则。将自动选择其作用域和存储期。然而,还有更灵活地选择,即用库函数分配和管理内存。

C可以在程序运行时分配更多的内存。主要的工具是malloc()函数,该函数接受一个参数:所需的内存字节数。malloc()函数会找到合适的空闲内存快,这样的内存是匿名的。也就是说,malloc()分配内存,但是不会为其赋名。然而,它确实返回动态分配内存块的首字节地址。因此,可以把该地址赋给一个指针变量,并使用指针访问这块内存。

现在我们有3种创建数组的方法。

  • 声明数组时,用常量表达式表示数组的纬度,用数组名访问数组的元素。可以用静态内存或自动内存创建这种数组。
  • 声明变长数组(C99新增的特性)时,用变量表达式表示数组的纬度,用数组名访问数组的元素,具有这种特性的数组只能在自动内存中创建
  • 声明一个指针,调用malloc(),将其返回值赋给指针,使用指针访问数组的元素。该指针可以是静态的或自动的。
double item[n] //c99之前是不允许的

但是可以这样做:

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

通常,malloc()要与free()配套使用。free()函数的参数是之前malloc()返回的地址。

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

free(ptd);

类型限定符

**const、volatile、restrict 和 _Atomic。const **限定符先定数据在程序运行时不能改变。对指针使用const时,可先定指针本身不能改变或指针指向的数据不能改变,这取决于const 在指针声明中的位置。volatile 限定符表明,先定的数据除了被当前程序修改外,还可以被其他进程修改。改限定符的目的是警告编译器不要进行假定的优化。restrict 限定符也是为了方便编译器设置优化方案。restrict限定的指针是访问它所指向数据的唯一途径。

参考资料

内存管理(详细版)

[C Primer Plus (第六版) Stephen Prata 著]

你可能感兴趣的:(C中的内存管理)