Pointers on C——11 Dynamic Memory Allocation.5

来源:互联网 发布:金山软件股价 编辑:程序博客网 时间:2024/06/03 03:57

11.6 Memory Allocation Examples


A common use for dynamic memory allocation is obtaining space for arrays whose sizes are not known until run time. Program 11.2 reads a list of integers, sorts them into ascending sequence, and prints the list.

动态内存分配一个常见的用途就是为那些长度在运行时才知的数组分配内存空间。程序1 1. 2 读取一列整数,并按升序排列它们,最后打印这个列表。


/*

** Read, sort, and print a list of integer values.

*/

#include <stdlib.h>

#include <stdio.h>

/*

** Function called by 'qsort' to compare integer values

*/

int

compare_integers( void const *a, void const *b )

{

register int const *pa = a;

register int const *pb = b;

return *pa > *pb ? 1 : *pa < *pb ? -1 : 0;

}

int

main()

{

int *array;

int n_values;

int i;

/*

** See how many numbers there will be.

*/

printf( "How many values are there? " );

if( scanf( "%d", &n_values ) != 1 || n_values <= 0 ){

printf( "Illegal number of values.\n" );

exit( EXIT_FAILURE );

}

/*

** Get memory to store them.

*/

array = malloc( n_values * sizeof( int ) );

if( array == NULL ){

printf( "Can't get memory for that many values.\n" );

exit( EXIT_FAILURE );

}

/*

** Read the numbers.

*/

for( i = 0; i < n_values; i += 1 ){

printf( "? " );

if( scanf( "%d", array + i ) != 1 ){

printf( "Error reading value #%d\n", i );

exit( EXIT_FAILURE );

}

}

/*

** Sort the values.

*/

qsort( array, n_values, sizeof( int ), compare_integers );

/*

** Print them out.

*/

for( i = 0; i < n_values; i += 1 )

printf( "%d\n", array[i] );

/*

** Free the memory and exit.

*/

free( array );

return EXIT_SUCCESS;

}

Program 11.2 Sort a list of integers sort.c

Memory to hold the list is dynamically allocated so that when you are writing the program you donʹt have to guess how many values the user might wish to sort.The only limit on the number of values that can be sorted is the amount of dynamic memory available to the program. When small lists are sorted, though, only as much memory as is actually needed is allocated, so memory is not wasted.

用于保存这个列表的内存是动态分配的,这样当你编写程序时就不必猜测用户可能希望对多少个值进行排序。可以排序的值的数量仅受分配给这个程序的动态内存数量的限制。但是,当程序对一个小型的列表进行排序时,它实际分配的内存就是实际需要的内存,因此不会造成浪费。


Now consider a program that reads strings. If you donʹt know the length of the longest string in advance you cannot use an ordinary array as a buffer. Instead, use dynamically allocated memory. When you find an input line that doesnʹt fit, reallocate a larger buffer and read the remainder of the line into it. The implementation of this technique is left as a programming exercise.

现在让我们考虑一个读取字符串的程序。如果你预先不知道最长的那个字符串的长度,你就无法使用普通数组作为缓冲区。反之,你可以使用动态分配内存。当你发现一个长度超过缓冲区的输入行时,你可以重新分配一个更大的缓冲区,把该行的剩余部分也装到它里面。这个技巧的实现留作编程练习。


/*

** Make a copy of a string in dynamically allocated memory. Note:

** caller is responsible for checking whether the memory was

** allocated! This allows the caller to respond to an error in

** any way they wish.

*/

#include <stdlib.h>

#include <string.h>

char *

strdup( char const *string )

{

char *new_string;

/*

** Ask for enough memory to hold the string and its

** terminating NUL byte.

*/

new_string = malloc( strlen( string ) + 1 );

/*

** If we got the memory, copy the string.

*/

if( new_string != NULL )

strcpy( new_string, string );

return new_string;

}

Program 11.3 Duplicate a string strdup.c

The input is read into this buffer, one line at a time. The length of the string is determined, and then memory is allocated to hold it. Finally, the string is copied into the new memory so the buffer can be used to read the next line.

输入被读入到缓冲区,每次读取一行。此时可以确定字符串的长度,然后就分配内存用于存储字符串。最后,字符串被复制到新内存。这样缓冲区又可以用于读取下一个输入行。


The function in Program 11.3, called strdup, returns a copy of its input string in dynamically allocated memory. The function first tries to obtain enough memory to hold the copy. The additional byte beyond the string length is needed to hold the NUL byte that terminates the string. If the memory was successfully allocated, the string is copied into the new memory. Finally, a pointer to the new memory is returned.

程序1 1.3中名叫strdup 的函数返回一个输入字符串的拷贝,该拷贝存储于一块动态分配的内存中。函数首先试图获得足够的内存来存储这个拷贝。内存的容量应该比字符串的长度多一个字节,以便存储字符串结尾的NUL 字节。如果内存成功分配,字符串就被复制到这块新内存。最后,函数返回一个指向这块内存的指针。


Notice that new_string will be NULL if the allocation failed for some reason, so a NULL pointer would be returned in this case.

注意,如果由于某些原因导致内存分配失败, new_string的值将为NULL 。在这种情况下,函数将返回一个NULL 指针。


This function is very handy. It is so useful, in fact, that many environments include it as part of the library even though the Standard does not mention it.

这个函数是非常方便的,也非常有用。事实上,尽管标准没有提及,但许多环境都把它作为函数库的一部分。


Our final example illustrates how you can use dynamic memory allocation to eliminate wasted memory with variant records. Program 11.4 is a modification of the inventory system example from Chapter 10. Program 11.4a contains the declarations for the inventory records.

我们的最后一个例子说明了你可以怎样使用动态内存分配来消除使用变体记录造成的内存空间浪费。程序1 1.4 是第10 章存货系统例子的修改版本。程序1 1.4a 包含了存货记录的声明。


As before, the inventory system must handle two types of records, those for parts and those for subassemblies. The first structure holds the information specific to a part (only a portion of this structure is shown), and the second holds information about subassemblies. The last declaration is for the inventory record. It contains some common data needed for both subassemblies and parts and a variant portion.

和以前一样,存货系统必须处理两种类型的记录,分别用于零件和装配件。第1 个结构保存零件的专用信息(这里只显示这个结构的一部分),第2 个结构保存装配件的专用信息。最后一个声明用于存货记录,它包含了零件和装配件的一些共有信息以及一个变体部分。


Because the different fields in the variant part are different sizes (in fact, the subassembly record is variable size), the union contains pointers to structures rather than the structures. Dynamic allocation lets the program create an inventory record that is the correct size for the item being stored, so there is no wasted memory.

由于变体部分的不同字段具有不同的长度(事实上,装配件记录的长度是可变的),所以联合包含了指向结构的指针而不是结构本身。动态分配允许程序创建一条存货记录,它所使用的内存的大小就是进行存储的项目的长度,这样就不会浪费内存。


Program 11.4b is a function that creates an inventory record for a subassembly.This task depends on the number of different parts the subassembly contains, so this value is passed as an argument.

程序1 1.4b 是一个函数,它为每个装配件创建一条存货记录。这个任务取决于装配件所包含的不同零件的数目,所以这个值是作为参数传递给函数的。


This function allocates three things: the inventory record, the subassembly structure, and the array of parts in the subassembly structure. If any of these allocations fails, any memory that was already obtained is freed and a NULL pointer is returned. Otherwise, the type and info.subassy->n_parts fields are initialized and a pointer to the record is returned.

这个函数为三样东西分配内存:存货记录、装配件结构和装配件结构中的零件数组。如果这些分配中的任何一个失败,所有已经分配的内存将被释放,函数返回一个NULL 指针。否则, type 和info.subassy->n_parts 字段被初始化,函数返回一个指向该记录的指针。


Obtaining memory for an inventory record to store a part is a little easier than for a subassembly because only two allocations are needed. This function is therefore not illustrated here.

为零件存货记录分配内存较之装配件存货记录容易一些,因为它只需要进行两项内存分配。因此,这个函数在此不予解释。


/*

** Declarations for the inventory record.

**

** Structure that contains information about a part.

*/

typedef struct {

int cost;

int supplier;

/* etc. */

} Partinfo;

/*

** Structure to hold information about a subassembly.

*/

typedef struct {

int n_parts;

struct SUBASSYPART {

char partno[10];

short quan;

} *part;

} Subassyinfo;

/*

** Structure for an inventory record, which is a variant record.

*/

typedef struct {

char partno[10];

int quan;

enum { PART, SUBASSY } type;

union {

Partinfo *part;

Subassyinfo *subassy;

} info;

} Invrec;

Program 11.4a Inventory system declarations inventor.h

/*

** Function to create a SUBASSEMBLY inventory record.

*/

#include <stdlib.h>

#include <stdio.h>

#include "inventor.h"

Invrec *

create_subassy_record( int n_parts )

{

Invrec *new_rec;

/*

** Try to get memory for the Invrec portion.

*/

new_rec = malloc( sizeof( Invrec ) );

if( new_rec != NULL ){

/*

** That worked; now get the SUBASSYINFO portion.

*/

new_rec->info.subassy =

malloc( sizeof( Subassyinfo ) );

if( new_rec->info.subassy != NULL ){

/*

** Get an array big enough for the parts.

*/

new_rec->info.subassy->part = malloc(

n_parts * sizeof( struct SUBASSYPART ) );

if( new_rec->info.subassy->part != NULL ){

/*

** Got the memory; fill in the fields

** whose values we know and return.

*/

new_rec->type = SUBASSY;

new_rec->info.subassy->n_parts =

n_parts;

return new_rec;

}

/*

** Out of memory: free what we've got so far.

*/

free( new_rec->info.subassy );

}

free( new_rec );

}

return NULL;

}

Program 11.4b Dynamic creation of a variant record invcreat.c


Program 11.4c contains the last part of this example: a function that destroys inventory records. This function works for either type of inventory record. It uses a switch statement to determine the type of record it was given and then frees all dynamically allocated fields in the record. Finally, the record is deleted.

程序1 1.4c 包含了这个例子的最后部分:一个用于销毁存货记录的函数。这个函数对两种类型的存货记录都适用。它使用一条switch 语句判断传递给它的记录的类型并释放所有动态分配给这个记录的所有字段的内存。最后,这个记录便被删除。


A common mistake made in situations like this one is to free the record before freeing the memory pointed to by fields in the record. After the record has been freed,you may no longer safely access any of the fields that it contains.

在这种情况下,一个常见的错误是在释放记录中的字段所指向的内存前便释放记录。在记录被释放之后,你就可能无法安全地访问它所包含的任何字段。


/*

** Function to discard an inventory record.

*/

#include <stdlib.h>

#include "inventor.h"

void

discard_inventory_record( Invrec *record )

{

/*

** Delete the variant parts of the record

*/

switch( record->type ){

case SUBASSY:

free( record->info.subassy->part );

free( record->info.subassy );

break;

case PART:

free( record->info.part );

break;

}

/*

** Delete the main part of the record

*/

free( record );

}

Program 11.4c Destruction of a variant record invdelet.c


Although it is a little less obvious, the following code fragment is a slightly more efficient implementation of Program 11.4c.

下面的代码段尽管看上去不是非常的一目了然,但它的效率比程序1 1.4c 稍有提高。


if( record->typ == SUBASSY )

free( record->info.subassy->part );

free( record->info.part );

free( record );


This code does not distinguish between subassemblies and parts when freeing the variant part of the record. Either member of the union can be used, as free does not care which type of pointer it gets.

这段代码在释放记录的变体部分时并不区分零件和装配件。联合的任一成员都可以传递给free函数,因为后者并不理会指针所指向内容的类型。

上一章  Pointers on C——11 Dynamic Memory Allocation.4

原创粉丝点击