C存储类、链接和内存管理--动态分配内存及类型限定词

目录

  • 存储类说明符
  • 存储类和函数
  • 动态分配内存
    • malloc函数
    • free函数
    • calloc函数
    • 动态分配内存的缺点
  • C类型限定关键字
    • constant定义全局常量
    • volatile关键字
    • restrict关键字

@

存储类说明符

C中存储类说明符共有5个,为auto register static extern typeddef,最后一个关键字typedef与内存存储无关。
规定:不可以在一个声明中使用一个以上存储类说明符
存储类说明符用来确定变量的存储类型。

存储类和函数

函数的存储类有两种:

  • 外部
  • 静态

在一个文件中定义的函数默认是外部的,也就是说其他文件可以调用它,只有使用static关键字修饰的函数才是函数定义所在文件所私有的函数,通常用来解决不同文件函数之间的命名冲突。

double a();//默认声明,函数a是外部的
extern int b();//此处显式声明b函数是在其他文件中定义的,可以省略。主要是为了让程序更清晰,除非函数声明使用了关键字`static`,否则默认其为`extern`的
static int c();//c函数只能在本文件中调用

动态分配内存

mallocfree函数原型存在于stdlib.h中。

malloc函数

在C中,一些数据的内存是由系统自动分配的,也允许程序主动要求分配。

int foo = 0;//系统自动分配内存空间用来存储一个int
char string[] = "I love you.";//系统自动为数组string分配正好装下字符串的内存空间
int bar[10];//要求分配10个用来存储int的内存空间

还可以手动分配内存。
extern void * malloc(unsigned int num_bytes)
函数malloc函数接受一个参数,该餐宿用于指定需要分配的内存字节数。malloc找到可用内存中一个区块,并返回该区块内存第一个字节的地址。它的void *返回值是一个通用指针,可以转换为其他指针类型。C中不要求强制转换,但C++中要求强制转换。如果malloc找不到符合要求的可用内存,它会返回空指针。例子:

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

例子中,分配了一块内存用于存储30个double类型数据,并将首字节地址赋值给了指针p

free函数

void free(void *p)
对应每个malloc函数调用,应该有对应的free调用来进行内存释放。它的参数是之前malloc函数分配内存块第一个字节的地址。也就是说分配的内存可用时间是从malloc执行结束开始到free释放内存为止。
例子:

#include <stdio.h>
#include <stdlib.h>

/*
 * test.c 编译后产生可执行文件test.exe或test.out
 */

int main() {

    double *p;
    int max;
    int number;
    int i = 0;

    puts("What's the number of \"double\" entries?");
    while (scanf("%d",&max) != 1){
//        setbuf(stdin,NULL);
        scanf("%*s");
        puts("Please input a integer:");
    }

    printf("max = %d\n",max);

    p = (double *) malloc(max * sizeof(double));
    if (p == NULL){
        puts("Memory allocation has failed. Try to Restart this program.");
        exit(EXIT_FAILURE);
    }

    puts("Enter the values(q to quit): ");
    while (i < max && scanf("%lf",&p[i]) == 1)
        ++i;
    printf("Here are the number of entries: %d\n",number = i);
    for (int j = 0; j < number; ++j) {
        printf("%7.2f ",p[j]);
        if (j % 7 == 6)
            putchar('\n');
    }
    if (i % 7 !=0)
        putchar('\n');
    printf("i = %d\n",i);
    puts("Done.");
    free(p);
    return 0;
}

calloc函数

malloc类似,不同的是calloc可以指定要分配单元数目以及每个单元所需要的字节数。calloc会默认将内存块中的各个位置0。
calloc分配的内存同样需要用free函数来释放。

动态分配内存的缺点

动态分配内存给了程序一定的自由,但是若是忘记释放内存,那么就会造成资源的浪费(内存泄漏)。而且相对于自动变量栈式管理,动态分配内存不是紧凑的连续分配,而是在内存中找合适的区块,会造成内存碎片,拖慢速度。

C类型限定关键字

constant定义全局常量

constant定义常量之前已经做了笔记,看这里。这里我们来看它与全局常量的关系。
constant定义全局常量有两种方式:
第一种方法:

//file1.c
const double PI = 3.14;

//file2.c
extern const double PI;

第二种:

//constant.h中定义常量,需要`static`关键字来修饰
static const double PI = 3.14;

//file1.c只需include 头文件即可
#include "constant.h"

//file2.c
#include "cobnstant.h"

第二种方法中,file1.cfile2.c文件都包含了constant.h头文件,那么这两个文件都会定义声明一个本文件私有的静态内部链接变量PI,其实是对constant.hPI值的拷贝。为什么必须要使用static关键字呢?因为如果不使用的话同一个静态外部链接变量就要在两个文件中定义声明两次,而我们知道外部变量只允许定义声明一次,其余的都应该是引用声明,定义两次会造成标识符冲突,还不如直接加个static修饰,为每个文件分别拷贝一个PI值给他们用。

volatile关键字

volatile告诉编译器某个变量除了能被程序本身修改之外,还可以被超出程序之外的其他部分改变。假定,有一个变量的值记录的是时间,那么不管程序有没有在运行,运行的如何,这个变量的值肯定是要随着时间变化而变化的,那么这个变量就应该加volatile修饰来提醒编译器。再来个例子:

int x = 10;
int val1 = x;
int val2 = x;

编译器注意到x变量被使用了两次而没有进行别的操作,那么他可以将x的值临时存储在寄存器中,那么当val2val1进行赋值操作时就会变快。但是,如果x的值可能被除了程序之外的部分改变,那么就应该这样:volatile int x = 10;来告诉编译器这个变量可能会如此,那么编译器就不会做出将x值存于寄存器这样的优化。一个变量既可以是constant,也可以是volatile的,因为不能被程序改变的量为常量,但可能被硬件改变,那么就是volatile的。这与Java中的volatile关键字可不一样。

restrict关键字

restrict关键字只能用来修饰指针,表示某个数据对象的唯一访问方式就是该指针,方便编译器优化。用法:
int * restrict foo = (int *) malloc(10 * sizeof(int))。这里表明foo这个指针是数组的唯一访问方式。

int array[4] = {};
int *p = array;

这里就不能给prestrict限定词,因为array这个数组可以通过arrayp两种方式进行访问。

原文地址:https://www.cnblogs.com/bobliao/p/9916871.html

时间: 2024-10-09 22:26:56

C存储类、链接和内存管理--动态分配内存及类型限定词的相关文章

内存管理:内存泄漏和空悬指针

********内存管理基础********************* 内存管理:针对的是实例的内存占用的管理(放在堆里面) 实例:1:由class类型构建的实例,2:闭包对象 内存管理技术:ARC:Automatic Reference Count 自动:由语言本身帮我们销毁内存,不需要你手动销毁, 比如在c中就调用dealloc() 引用:let p = Person()  p就是对Person()这个对象的一个引用 计数: let p = Person() //一个 / / let pp

Linux内存管理 (一) 内存组织

内存管理是内核最复杂同时也是最重要的一部.其特点在于非常需要处理器和内核之间的协作. 首先内存划分为结点,在内核中表示为pg_data_t,每个结点划分为内存域. 以下的所有数据结构或代码都做了不同程度的精减,一方面是为了保留相关代码,除去细枝末叶,另一方面是为了美观. 结点的数据结构为 <mmzone.h>typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES]; /*内存结点所包含的内存域数组*/ struct zo

java基础---JVM内存管理以及内存运行机制学习总结

自己从网上搜资料拼接了一张JVM内存图:如下图所示: 我们思考几个问题: 1.jVM是怎么运行的? 2.JVM运行时内存是怎么分配的? 3.我们写的java代码(类,对象,方法,常量,变量等等)最终存放在哪个区? VM运行时数据区域: 1.程序计数器(program Counter Register):   是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的 方式去实 现),字节码解释器工作时就是通过改

深入C#内存管理来分析值类型&amp;引用类型,装箱&amp;拆箱,堆栈几个概念组合之间的区别

C#初学者经常被问的几道辨析题,值类型与引用类型,装箱与拆箱,堆栈,这几个概念组合之间区别,看完此篇应该可以解惑. 俗话说,用思想编程的是文艺程序猿,用经验编程的是普通程序猿,用复制粘贴编程的是2B程序猿,开个玩笑^_^. 相信有过C#面试经历的人,对下面这句话一定不陌生: 值类型直接存储其值,引用类型存储对值的引用,值类型存在堆栈上,引用类型存储在托管堆上,值类型转为引用类型叫做装箱,引用类型转为值类型叫拆箱. 但仅仅背过这句话是不够的. C#程序员不必手工管理内存,但要编写高效的代码,就仍需

内存管理 浅析 内存管理/内存优化技巧

内存管理 浅析 下列行为都会增加一个app的内存占用: 1.创建一个OC对象: 2.定义一个变量: 3.调用一个函数或者方法. 如果app占用内存过大,系统可能会强制关闭app,造成闪退现象,影响用户体验.如何让回收那些不再使用的对象呢?本文着重介绍OC中的内存管理. 所谓内存管理,就是对内存进行管理,涉及的操作有: 1.分配内存:比如创建一个对象,会增加内存占用: 2.清除内存:比如销毁一个对象,会减少内存占用. 内存管理的管理范围: 1.任何继承了NSObject的对象: 2.对其他非对象类

内存管理之内存池概述(转)

原文链接:http://www.xiaoyaochong.net/wordpress/index.php/2013/08/10/%E5%BC%95%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E4%B9%8B%E5%86%85%E5%AD%98%E6%B1%A0%E6%A6%82%E8%BF%B0/ 在我们编写代码的过程中,不可避免的要和内存打交道,在申请释放不太频繁的情况下,通常让系统进行内存管理即可.但是,直接使用系统调用malloc/free.new/delet

浅谈C语言内存管理、内存泄露、堆栈

1.内存分配区间: 对于一个C语言程序而言,内存空间主要由五个部分组成:代码段(.text).数据段(.data).静态区(.BSS).堆和栈组成. BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量和静态变量 (这里注意一个问题:一般的书上都会说全局变量和静态变量是会自动初始化的,那么哪来的未初始化的变量呢?变量的初始化可以分为显示初始化和隐式初始化,全局变量和静态变量如果程序员自己不初始化的话的确也会被初始化,那就是不管什么类型都初始化为0,这种没有显示初始

Objective-C 【内存管理&amp;手动内存管理 综述】

------------------------------------------- 内存管理 (1)Objective-C的内存管理 栈区    存放局部变量(由于基本数据类型占用的存储空间是固定的,由系统去分配,我们不用去管,故栈区存放基本数据类型,) 堆区    存放程序运行过程中动态分配的内存空间(对象类型是程序运行过程中动态分配的,他们的大小不固定.比如说是我们Person new申请来的,存放在堆区,也是我们需要管理的) ★所以内存管理的范围是   继承了NSObject的所有对象

第九讲.内存管理初级.(内存管理的方式,引用计数机制及影响计数的各个方法,dealloc方法,内存管理的基本原则,掌握copy的实现)

一.内存管理的方式. 1.进行内存管理的原因: 1>.由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等. 管理范围:任何继承NSObject的对象,对其他的基本数据类型无效. 2>.本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指