文章目录mallocfree(calloc ) )动态内存分配和变长数组内存类和动态内存分配
malloc malloc ) )在可用内存中找到适当大小的块。
内存是匿名的;
也就是说,malloc ()已分配内存,但未指定名称。
但是可以返回该内存第一个字节的地址。
因此,可以将该地址分配给指针变量,然后使用该指针访问该内存。
char表示一个字节,因此以前将malloc ) )定义为指向char的指针类型。
但是,ANSIC标准使用了一种新类型:指向void的指针。 此类型用作“通用指针”。
函数malloc ) )可用于返回数组指针、结构指针等,因此必须将返回值类型分配给相应的类型。
在ANSIC中,为了使程序明确适应指针的类型分配,将void指针的值分配给另一个类型的指针不会引起类型冲突。
如果找不到所需的空间,malloc ()将返回空指针。
malloc ) )创建数组。 malloc ) )在程序运行时请求存储块。 它还需要一个指针来存储块在内存中的位置。
例如,有以下代码: 双精度* Ptd; ptd=(双精度* ) malloc ) 30*sizeof (双精度);
此代码请求30个双精度值的空间,并将ptd指向该空间所在的位置。
请注意,ptd被声明为指向单个双精度值的指针,而不是指向30个双精度值的数据块的指针。
请记住数组的名称是第一个元素的地址。
因此,当ptd指向内存块的第一个元素时,它可以与数组名称一样使用。
这意味着可以使用表达式ptd[0]访问内存块的第一个元素,使用pd[1]访问第二个元素。
正如您前面所学,可以在指针元件上使用数组名称,也可以在数组元件上使用指针。
有三种创建数组的方法:
1 .声明数组,在声明数组时在常量表达式中指定数组维数,并允许通过数组名称访问数组元素。
2 .声明变长数组,声明时在变量表达式中指定数组维数,然后用数组名称访问数组元素。 这是C99的特性。
3 .声明指针,调用malloc (,并使用该指针访问数组元素。
使用第二种或第三种方法,可以在常规数组声明中做不到。
创建“动态阵列”。 也就是说,这是一个数组,在运行程序时分配内存,在运行程序时可以选择大小。
例如,假设n是整数。 在C99之前,不能这样做:
如果double item[n]:/*是变量,则在C99或更早版本中不允许。 */但是,C99之前的编译器也可以:
ptd=(双* ) malloc (n * sizeof ) double ); /*可以。 */这行得通。 另外,如您所见,这比使用可变长度数组更灵活。
通常,对于每个malloc ()调用,应该调用一次free ) )。
函数free (的参数是以前从malloc )返回的地址,用于释放以前分配的内存。
这样,分配的内存的持续时间为malloc ) )以分配内存,然后为free ) )以释放内存进行回收。
假设malloc (和free )管理内存池。
每次调用malloc (时将内存分配给程序使用,每次调用free ) )时将内存放回池中,以便可以重用内存。 注意:
free (的参数必须是由malloc ) )分配的内存块的指针。 无法使用free ()释放以其他方式分配的内存,如数组声明。 头文件stdlib.h中有malloc (和free ) )的原型。 我不知道,哈哈)
使用malloc (),程序可以确定和创建运行时所需的数组大小。
方案清单12.14说明了这种可能性。
为指针ptd分配内存块地址,并像数组名称一样使用ptd。
程序还调用了exit ()函数。 此函数的原型位于stdlib.h,用于在内存分配失败时退出程序。
值EXIT_FAILURE也在此头文件中定义。 标准库有两个返回值,确保它们在所有操作系统上运行。
退出成功(或等效于0 )表示程序成功结束;
EXIT_FAILURE表示程序异常结束。
此外,一些操作系统(如UNIX、Linux和Windows )可以接受其他整数值。 程序列表12.14 dyn_arr.c程序
/*dyn_arr.c --为数组动态分配存储空间*/# include stdio.h # include stdlib.hint main { double * ptd; int max; Int编号器; int i=0; puts (whatisthemaximumnumberoftypedoubleentries? ' );//入口:入口
输入scanf("%d", &max);//输入个数ptd = (double*)malloc(max * sizeof(double));if (ptd == NULL) {exit(EXIT_FAILURE);}///*ptd现在指向有max个元素的数组*/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]);/*.2表示输出数据保留小数点后2位小数,第3位四舍五入;7表示输出数据在终端设备上占用7个字符宽度右对齐,实际数据位数(包括小数点)小于7时左边用空格补齐,大于7时按实际位数向右扩展输出。*/if(i % 7 == 6)putchar('n');}if(i % 7 != 0)putchar('n');puts("Done.");free(ptd);return 0; }下面是一个运行示例。该例中输入了6个数,但程序只处理了5个,因为我们将数组大小限定为5。
代码分析:
malloc()可能无法获得所需数量的内存。在那种情形下,函数返回空指针,程序终止。 if (ptd == NULL) {exit(EXIT_FAILURE);//内存分配失败}
如果成功地分配了内存,程序将把ptd视为一个具有max个元素的数组的名字。
注意:
在程序末尾附近的函数free()。
它释放malloc()分配的内存。
函数free()只释放它的参数所指向的内存块。
在这个特定例子中,使用free()不是必须的,因为在程序终止后所有已分配的内存都将被自动释放。
然而在一个更复杂的程序中,能够释放并再利用内存将是重要的。
使用动态数组将获得什么?
主要是获得了程序灵活性。
假定知道一个程序在大多数情况下需要的数组元素不超过100个;
而在某些情况下,却需要l0000个元素。
在声明数组时,不得不考虑到最坏情形并声明一个具有10000个元素的数组。
在多数情况下,程序将浪费内存。
如果有一次需要10001个元素,程序就会出错。
您可以使用动态数组来使程序适应不同的情形。
在编译程序时,静态变量的数量是固定的:在程序运行时也不改变。
自动变量使用的内存数量在程序执行时自动增加或者减少。
但被分配的内存所使用内存数量只会增加,除非您记得使用free()。
例如,假定有一个如下代码勾勒出的函数,它创建一个数组的临时拷贝:
假定我们如暗示的那样没有使用free()。
当函数终止时,指针temp作为一个自动变量消失了。
但它所指向的16000个字节的内存仍旧存在。
我们无法访问这些内存,因为地址不见了。
由于没有调用free(),不可以再使用它了。第二次调用gobble(),它又创建了一个temp,再次使用malloc()分配16000个字节的内存。
第一个16000字节的块已不可用,因此malloc()不得不再找一个l6000字节的块。
当函数终止时,这个内存块也无法访问,不可再利用。
但循环执行了1000次,因此在循环最终结束时,已经有1600万字节的内存从内存池中移走。
事实上,在到达这一步前,程序很可能已经内存溢出了。
这类问题被称为内存泄漏(memory leak),可以通过在函数末尾处调用free()防止该问题出现。
内存分配还可以使用calloc()。
典型的应用如下:
long * newmen;newmen = (long *) calloc (100, sizeof( long));与 malloc()类似,calloc()在ANSI以前的版本中返回一个 char 指针,在ANSI中返回一个void指针。
如果要存储不同类型,应该使用类型指派运算符。
这个新函数接受两个参数,都应是无符号的整数(在ANSI中是size_t类型)。
第一个参数是所需内存单元的数量,第二个参数是每个单元以字节计的大小。
在这里,long使用4个字节,因此这一指令建立了100个4字节单元,总共使用400个字节来存储。
使用 sizeof(long)而不是 4 使代码更具可易移植性。
它可在其他系统中运行,这些系统中 long不是4字节而是别的大小。
函数calloc()还有一个特性:
它将块中的全部位都置为0(然而要注意,在某些硬件系统中,浮点值0不是用全部位为0来表示的)。
函数free()也可以用来释放由calloc()分配的内存。
动态内存分配是很多高级编程技巧的关键。
在17章“高级数据表示”中我们将研究一些。您自己的C库可能提供了其他内存管理函数,有些可移植,有些不可以。您可能应该抽时间看一下。
动态内存分配与变长数组变长数组(Variable-Length Array,VLA)与malloc()在功能上有些一致。例如,它们都可以用来创建一个大小在运行时决定的数组:
int valmal(){int n;int * pi;scanf("%d", &n);pi = (int *) malloc(n*sizeof(int));int ar[n];//变长数组 pi[2] = ar[2] = -5;}一个区别在于 VLA 是自动存储的。
自动存储的结果之一就是 VLA 所用内存空间在运行完定义部分之后会自动释放。
在本例中,就是函数 vlamal()终止的时候。因此不必使用 free()。
另一方面,使用由 malloc()创建的数组不必局限在一个函数中。
例如,函数可以创建一个数组并返回指针,供调用该函数的函数访问。
接着,后者可以在它结束时调用 free()。
free()可以使用不同于 malloc()指针的指针变量:必须一致的是指针中存储的地址。
VLA 对多维数组来说更方便。
您可以使用 malloc()来定义一个二维数组,但语法很麻烦。
如果编译器不支持 VLA 特性,必须固定一维的大小,正如下面的函数调用:
有必要查看一下指针声明。函数malloc()返回一个指针,因此p2必须是适当类型的指针。
下面的声明:
表明p2指向一个包含6个int值的数组。
这意味着p2[j| 将被解释为一个由6个整数构成的元素,p2[ i ][ j ]将是一个int值。
第二个指针声明使用变量来指定p3所指数组的大小。
这意味着p3将被看作一个指向 VLA 的指针,这正是代码不能在C90标准中运行的原因。
您可能正在为存储类和动态内存分配之间的联系感到疑惑。
我们来看一个理想模型。可以认为程序将它的可用内存分成了三个独立的部分:一个是具有外部链接的、具有内部链接的以及具有空链接的静态变量的:一个是自动变量的:另一个是动态分配的内存的。
(静态变量):
在编译时就已经知道了静态存储时期存储类变量所需的内存数量,存储在这一部分的数据在整个程序运行期间都可用。
这一类型的每个变量在程序开始时就已存在,到程序结束时终止。
(动态变量):
然而,一个自动变量在程序进入包含该变量定义的代码块时产生,在退出这一代码块时终止。
因此,伴随着程序对函数的调用和终止,自动变量使用的内存数量也在增加和减少。
典型地,将这一部分内存处理为一个堆栈。
这意味着在内存中,新变量在创建时按顺序加入,在消亡时按相反顺序移除。
(动态内存分配):
动态分配的内存在调用malloc()或相关函数时产生,在调用free()时释放。
由程序员而不是一系列固定的规则控制内存持续时间,因此内存块可在一个函数中创建,而在另一个函数中释放。(malloc 可以跨函数调用)
由于这点,动态内存分配所用的内存部分可能变成碎片状,也就是说,在活动的内存块之间散布着未使用的字节片。
不管怎样,使用动态内存往往导致进程比使用堆栈内存慢。
本文选自《C primer plus》