C 基础 - 存储类别、连接与内存管理

1、存储类别

C语言提供几种存储方法,来存放在内存中变量的值。数组

从硬件方面去看,被存储的每个值都会占用必定的物理内存,C语言把这样的一块内存叫对象(Object)多线程

从软件方面去看,程序须要一种方法访问对象。声明变量是一种方法并发

 

一个变量具备不一样的存储类别,存储类别是指具备不一样的 存储期(Storage duration)做用域(scope) 连接(linkage)函数

有三个地方能够用于存储变量:普通内存运行时堆栈硬件寄存器。 oop

 

基本概念: 做用域, 连接, 存储周期

1. 做用域: 描述程序中可访问标识符的区域。一个C变量做用域优化

* 块做用域ui

* 函数做用域spa

* 函数原型做用域:指 int add(a, b); 中 a 与 b 的形参。 线程

* 文件做用域(全局变量):变量定义在函数的外面。翻译

 

2. C变量有3种连接属性:

* 外部连接(external): 能够在多个文件程序中使用。

* 内部连接(static): 只能在一个翻译单元中使用。

* 无连接: 具备块做用域、函数做用域、函数原型做用域的变量都是无连接变量,表示变量的可见范围属于块、函数私有。

 

int giants = 5;      // 文件做用域,外部连接, 其余文件可使用

static int dog = 4;  // 文件做用域, 内部连接, 文件私有 
             // static 关键字 描述了文件做用域的 连接特性与存储周期无关
int main(void)

{ 
  ...
   static int max(int x, int y);
  ...
} 

 

3. 存储期:做用域与连接描述了标识符的可见性,存储期描述了经过这些标识符访问的对象的生存期。

C对象有四种存储期:

* 静态存储期: 程序执行期间一直存在,不管是内部连接仍是外部连接的文件做用域变量都是静态存储期

* 线程存储期: 用于并发程序设计,线程结束以前一直存在。

* 自动存储期: 块做用域变量具备自动存储期,为了让块做用域变量具备静态存储期,在变量前加 static 关键字

* 动态分配存储期: 

 

4. 五种存储类别

存储类别 存储期 做用域 连接 声明方式
自动(automatic) 自动 块内
寄存器(register) 自动 块内,关键字register

静态外部连接(static with external linkage)

静态 文件 外部 全部函数外

静态内部连接(static with interanl linkage)

静态 文件 内部 全部函数外,关键字static

静态块做用域(static with no linkage)

静态 块内,傅关键字 static

 

 

 

 

 

 

 

 

 

 

例如以下

#include <stdio.h>

int a;            // 静态外部连接 (全局变量,文件做用域,外部连接,其余文件均可以使用)
static int d;     // 静态内部连接 (文件做用域,内部连接,仅限本文件内函数使用)
extern char str;  // 字符str是定义在其余文件中的全局变量
main() {
int b; // 自动变量 (main函数结束后,消失) static int c; // 静态块做用域 (main函数结束后,消失) retister int e; // 寄存器变量,没法获取寄存器变量的地址 ...... } print_r() { int r; // 自动变量(Print_r函数调用时分配,结束后,消失) static int e; // 静态块做用域,该变量的地址在内存中不会改变 }

 

 

程序案例:

/*
 * 此程序的功能用来讲明变量的5种存储类别
 *  storage.c
 */


#include <stdio.h>
void report_count();     // 函数原型
void accumulate(int k);  // 函数原型
int count = 0;           // 全局变量, 外部连接

int main(void)
{
    int value;       // 自动变量
    register int i;  // 寄存器变量
    
    printf("Enter a positive integer (0 to quit): ");
    while (scanf("%d", &value) == 1 && value > 0)
    {
        ++count;     // 引用全局变量
        for (i = value; i >= 0; i--)
            accumulate(i);    // 变量i是块变量,调用accumulate函数
        printf("Enter a positive integer (0 to quit): ");
    }
    report_count();
    
    return 0;
}

void report_count()
{
    printf("Loop executed %d times\n", count);
}

 

问题:关键字static做用是什么?

在C语言中,关键字 static 有三个明显的做用:

首先:一旦声明为静态变量,在编译时刻开始永远存在,不受做用域范围约束

* 若是是局部静态变量,则此静态变量只能在局部做用域内使用,超出范围不能使用,可是它确实还占用内存,还存在.
* 在模块内(但在函数体外),一个被声明为静态的变量能够被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
* 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

 

/* 
 * 此程序的功能用来讲明变量的5种存储类别 
 * storage_accumulate.c
 */
#include <stdio.h>

extern int count;       // 引用全局变量
static int total = 0;   // 全局静态变量,本文件内有效

void accumulate(int k); // 函数原型

void accumulate(int k)  // k是块做用域
{
    static int subtotal = 0;  // 块静态变量
    
    if (k <= 0)
    {
        printf("loop cycle: %d\n", count);  // 引用全局变量
        printf("subtotal: %d; total: %d\n", subtotal, total);
        subtotal = 0;
    }
    else
    {
        subtotal += k;
        total += k;
    }
}

编译命令:

gcc -o storage storage.c storage_acculate.c

 

关键字const有什么含意?
1) 只读。
2)使用关键字const也许能产生更紧凑的代码。
3)使编译器很天然地保护那些不但愿被改变的参数,防止其被无心的代码修改。
Dan Saks在他的文章里彻底归纳了const的全部用法,(译者:Embedded Systems Programming)

下面的声明都是什么意思? 

const int a;     // 定义常整形数 a
int const a;     // 定义常整形数 a
const int *a; // a是一个指向常整形数的指针,值不可改,指针a可改 // const 在*以前,表示其值不能够改
int * const a; // a是一个指向整型数的常指针, 指针指向的整形数能够修改,指针不可改 // const 在*以后, 表示指针不可改
int const *a const; // a是一个指向常整型数的常指针, 指针与值都不可改

 

关键字volatile有什么含意? 并给出三个不一样的例子?

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。

精确地说就是,优化器在用到这个变量时必须每次都当心地从新读取这个变量的值,而不是使用保存在寄存器里的备份。 

下面是volatile变量的几个例子:
* 并行设备的硬件寄存器(如:状态寄存器)
* 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
* 多线程应用中被几个任务共享的变量

一个参数既能够是const还能够是volatile吗?解释为何。 

是的。一个例子是只读的状态寄存器。它是volatile由于它可能被意想不到地改变。它是const由于程序不该该试图去修改它。

 

一个指针能够是volatile 吗?解释为何。

是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。

 

下面的函数有什么错误:

int square(volatile int *ptr)
{
  return *ptr * *ptr;
} 

 

这段代码的目的是用来返回指针*ptr指向值的平方,可是,因为*ptr指向一个volatile型参数,编译器将产生相似下面的代码:

int square(volatile int *ptr) 
{ 
    int a,b; 
    a = *ptr; 
    b = *ptr; 
    return a * b; 
}

因为*ptr的值可能被意想不到地该变,所以a和b多是不一样的。结果,这段代码可能返不是你所指望的平方值!

 

正确的代码以下:

long square(volatile int *ptr)  
{ 
    int a; 
    a = *ptr; 
    return a * a; 
} 

 

 

2、分配内存 malloc ()与 free()

#include <stdlib.h>

void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);

 

1. malloc()函数

malloc自动分配内存,须要一个内存字节数做为参数,返回动态分配内存块的首字节地址。

若是malloc内存返回失败,则会返回NULL。

 

例如用malloc建立一个数组: 为30个double型的值请求内存空间,并设置ptd指向该位置。

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

1). 定义double型指针变量ptd

2). 30 * sizeof(double) 计算出须要的字节数

3). malloc()函数请求内存空间

4). (double*) 进程强制类型转换.

 

2. free()函数

free() 的参数是malloc参数返回的地址,释放malloc分配的内存。

所以,动态分配内存的周期从调用malloc到free为止。

 

一个动态分配内存与释放内存的例子

/* dyn_arr.c -- 动态分配一个数组 */
#include <stdio.h>
#include <stdlib.h> /* for malloc(), free() */

int main(void)
{
    double * ptd;  /* 存储动态分配内存的首地址 */
    int max;     /* 输入数组的最大个数 */
    int number;
    int i = 0;

    puts("What is the maximum number of type double entries?");
    scanf("%d", &max);   /* 输入数组的最大个数 */
    ptd = (double *) malloc(max * sizeof (double));  /* 分配内存 */
    if (ptd == NULL)    /* 检查是否分配成功 */
    {
        puts("Memory allocation failed. Goodbye.");
        exit(EXIT_FAILURE);
    }
    /* ptd now points to an array of max elements */
    puts("Enter the values (q to quit):");
    while (i < max && scanf("%lf", &ptd[i]) == 1)
        ++i;
    printf("Here are your %d entries:\n", number = i);
    for (i = 0; i < number; i++)
    {
        printf("%7.2f ", ptd[i]);
        if (i % 7 == 6)
            putchar('\n');
    }
    if (i % 7 != 0)
        putchar('\n');
    puts("Done.");
    free(ptd);

    return 0;
}

 

3. calloc() 函数

calloc 与 malloc的区别是calloc对申请分配的内存空间进行初始化为0,而后返回分配内存空间的首地址。

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

 

有两个参数:

第一个参数是所需的存储单元数量

第二个参数是存储单元的大小,以字节为单位

 

4. realloc()函数

realloc()函数用于修改一个原先已经分配的内存块大小。

 

经典问题:new delete 与malloc free 的联系与区别?

1. 都是在堆(heap)上进行动态的内存操做。 自动分配的变量都在栈上

2. malloc 与 free 是C库函数, new delete 是C++中运算符

 

问题:要求设置一绝对地址为0x67a9的整型变量的值为0xaa55。

int *ptr; 
ptr = (int *)0x67a9*ptr = 0xaa55;
相关文章
相关标签/搜索