动态内存分配,C语言动态内存分配详解

  • 内容
  • 评论
  • 相关

动态内存是相对静态内存而言的。所谓动态和静态就是指内存的分配方式。动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存。

前面所写的程序大多数都是在栈上分配的,比如局部变量、形参、函数调用等。栈上分配的内存是由系统分配和释放的,空间有限,在复合语句或函数运行结束后就会被系统自动释放。而堆上分配的内存是由程序员通过编程自己手动分配和释放的,空间很大,存储自由。堆和栈后面还会专门讲,这里先了解一下。

传统数组的缺点

“传统数组”就是前面所使用的数组,与动态内存分配相比,传统数组主要有以下几个缺点:

1) 数组的长度必须事先指定,而且只能是常量,不能是变量。比如像下面这么写就是对的:

int a[5];
而像下面这么写就是错的:
int length = 5;
int a[length];  //错误

2) 因为数组长度只能是常量,所以它的长度不能在函数运行的过程当中动态地扩充和缩小。

3) 对于数组所占内存空间程序员无法手动编程释放,只能在函数运行结束后由系统自动释放,所以在一个函数中定义的数组只能在该函数运行期间被其他函数使用。

而动态内存就不存在这个问题,因为动态内存是由程序员手动编程释的,所以想什么时候释放就什么时候释放。只要程序员不手动编程释放,就算函数运行结束,动态分配的内存空间也不会被释放,其他函数仍可继续使用它。除非是整个程序运行结束,这时系统为该程序分配的所有内存空间都会被释放。

所谓“传统数组”的问题,实际上就是静态内存的问题。我们讲传统数组的缺陷实际上就是以传统数组为例讲静态内存的缺陷。本质上讲的是以前所有的内存分配的缺陷。正因为它有这么多缺陷,所以动态内存就变得很重要。动态数组能很好地解决传统数组的这几个缺陷。

malloc函数的使用

那么动态内存是怎么造出来的?在讲如何动态地把一个数组造出来之前,我们必须要先介绍 malloc 函数的使用。

malloc 是一个系统函数,它是 memory allocate 的缩写。其中memory是“内存”的意思,allocate是“分配”的意思。顾名思义 malloc 函数的功能就是“分配内存”。要调用它必须要包含头文件<stdlib.h>。它的原型为:

# include <stdlib.h>
void *malloc(unsigned long size);

malloc 函数只有一个形参,并且是整型。该函数的功能是在内存的动态存储空间即堆中分配一个长度为size的连续空间。函数的返回值是一个指向所分配内存空间起始地址的指针,类型为 void*型。

简单的理解,malloc 函数的返回值是一个地址,这个地址就是动态分配的内存空间的起始地址。如果此函数未能成功地执行,如内存空间不足,则返回空指针 NULL。

“int i=5;”表示分配了 4 字节的“静态内存”。这里需要强调的是:“静态内存”和“静态变量”虽然都有“静态”两个字,但是它们没有任何关系。不要以为“静态”变量的内存就是“静态内存”。静态变量的关键字是 static,它与全局变量一样,都是在“静态存储区”中分配的。这块内存在程序编译的时候就已经分配好了,而且在程序的整个运行期间都存在;而静态内存是在栈中分配的,比如局部变量。

那么,如何判断一个内存是静态内存还是动态内存呢?凡是动态分配的内存都有一个标志:都是用一个系统的动态分配函数来实现的,如 malloc 或 calloc。

calloc 和 malloc 的功能很相似,我们一般都用 malloc。calloc 用得很少,这里不做讲解,有兴趣的话可自行查阅。

如何用 malloc 动态分配内存呢?比如:

int *p = (int *)malloc(4);

它的意思是:请求系统分配 4 字节的内存空间,并返回第一字节的地址,然后赋给指针变量 p。当用 malloc 分配动态内存之后,上面这个指针变量 p 就被初始化了。

需要注意的是,函数 malloc 的返回值类型为 void* 型,而指针变量 p 的类型是 int* 型,即两个类型不一样,那么可以相互赋值吗?

上面语句是将 void* 型“强制类型转换”成 int*型,但事实上可以不用转换。C 语言中,void* 型可以不经转换(系统自动转换)地直接赋给任何类型的指针变量(函数指针变量除外)。

所以“int*p=(int*)malloc(4);”就可以写成“int*p=malloc(4);”。此句执行完之后指针变量 p 就指向动态分配内存的首地址了。

void和void*

可能有人会问,void 不是不会有返回值吗?为什么 malloc 还会有返回值?需要注意的是,malloc 函数的返回值类型是 void*,而不是 void。void*和void是有区别的。

void* 是定义一个无类型的指针变量,它可以指向任何类型的数据。任何类型的指针变量都可以直接赋给 void* 型的指针变量,无需进行强制类型转换。本教程后面很多函数的参数都是 void* 型的,表示它们可以接收任何类型的数据。

同样,根据我们上面所讲的,void* 型也可以直接赋给任何类型的指针变量,而无需进行强制类型转换,但前提是必须在C语言中。

注意,不能对 void* 型的指针变量进行运算操作,如指针的运算、指针的移动等。原因很简单,前面讲int*型的指针变量加 1 就是移动 4 个单元,因为 int* 型的指针变量指向的是 int 型数据;但是 void* 型可以指向任何类型的数据,所以无法知道“1”所表示的是几个内存单元。

另外,在“int*p=malloc(4);”中,指针变量 p 是静态分配的。前面介绍过,动态分配的内存空间都有一个标志,即都是用一个系统的动态分配函数实现的。而指针变量 p 是用传统的方式定义的,所以是静态分配的内存空间。而 p 所指向的内存是动态分配的。

那么,动态分配和静态分配到底有什么区别呢?稍后你就明白了。

我们在前面讲过,编程的时间长了就会发现编程中百分之八九十的问题都属于内存的问题,如内存什么时候分配、什么时候释放、由谁分配、由谁释放、怎么分配、怎么释放、哪块内存可以用、哪块内存不能用、哪块内存可以读、哪块内存可读可写、哪块内存不能读也不能写。这些问题形成了计算机语言的语法规则,如 C 语言语法、C++ 语法、Java 语法,它们本质上都是内存的问题。包括局部变量、静态变量等都一样。所以内存是很关键的问题。

下面利用“int*p=malloc(4);”语句给大家写一个很有意思的程序:

# include <stdio.h>
# include <stdlib.h>  //malloc()的头文件
int main(void)
{
    while (1)
    {
        int *p = malloc(1000);   
    }
    return 0;
}

这个程序是非常简单的一个木马病毒。只要运行一会儿,你的计算机就死机了。死机速度的快慢取决于 malloc 后面括号中数字的大小。数字越大,“死”得越快。我们可以试验一下:按“Ctrl+Alt+Delete”键打开 Windows 任务管理器,然后选择“性能”,如图 1 所示。



图 1
 

本文标题:动态内存分配,C语言动态内存分配详解

本文地址:https://www.hosteonscn.com/2642.html

评论

0条评论

发表评论

邮箱地址不会被公开。 必填项已用*标注