Another problem with the traditional malloc
API is that it
is very difficult to cope with chunks of memory that may expand during
runtime, or are of unknown length when the call to malloc
is
made. If you need to keep the memory over a function call boundary,
alloca
is of no use.
Typically, this is a problem when reading strings into an application -
either you have to scan the string once to calculate the length and then
again to copy it into a correctly sized block of memory; or you have to
call realloc
to fetch a bigger block of memory each time you
detect that you are about to go out of bounds. example 2.5 uses the second of these methods.
char * read_string (FILE *input) { size_t i = 0; size_t size = 10; /* Take a guess. */ char * string = (char *) malloc (1+ size); int c; while ((c = fgetc (input)) != EOF) { if (isspace (c)) break; if (i == size) { size *= 2; string = realloc (string, 1+ size); } string[i++] = (char) c; } string[i] = '\0'; return string; }
Example 2.5: Reading a string into memory -- malloc version
In effect obstack
s manage the resizing of allocated memory for
you, albeit in a far more efficient manner than the manual
realloc
ation from example 2.5. All of the functions and macros described in this
section can be accessed by including the obstack.h
header in your
file.
struct obstack | data type |
An opaque handle for an obstack . All of the functions for
managing obstack s take a pointer to one of these structures. You
can have as many obstacks in your program as you like, and each
is capable of holding many strings. However there can be only one
growing string in each obstack at any given time - when that
string is complete, you can finalise it and start another string in the
same obstack . As soon as you have done that, the finalised
string cannot be changed. You are not limited to strings in fact, you
can store any kind of growing memory object in an obstack, provided that
only one object in each obstack is active. Hence the term
stack: the active object is the top "plate" on the stack and can
be accessed and changed, but you can't get to the plates underneath.
|
Before you can call any of the obstack
functions, you need to
decide how those functions will allocate and release dynamic memory, and
must define the following macros in your source file:
#define obstack_chunk_alloc malloc #define obstack_chunk_free free
You can define these macros to use any memory allocation scheme, though
you must define them before any other calls to obstack
functions. Despite our use of malloc
and free
to manage
memory, obstack
s request and release memory in large chunks which
is more time efficient.
int obstack_init (struct obstack*obstack_handle) | glibc function |
This function initialises the obstack such that it is ready to
have objects stored in it. This function must always be called to
initialise an obstack before it can be used with any of the
other functions detailed in the rest of this section.
|
#include <obstack.h> #define obstack_chunk_alloc malloc #define obstack_chunk_free free static struct obstack *string_obs = NULL; ... char * read_string (FILE *input) { if (string_obs == NULL) { string_obs = (struct obstack *) malloc (sizeof (struct obstack)); obstack_init (string_obs); } while ((c = fgetc (input)) != EOF) { if (isspace (c)) break; obstack_1grow (string_obs, (char) c); } return (char *) obstack_finish (string_obs); }
Example 2.6: Reading a string into memory -- obstack version
In example 2.6 we keep
adding characters to the growing object as soon as they are read in from
the input
stream. The obstack
takes care of ensuring that
there is enough memory to add the characters, internally fetching more
memory from the system using obstack_chunk_alloc
as necessary.
Consequently the object might move occasionally as it is growing, so it
is important not to rely on the address of the object before it has
finished.
Remember that only one object in the obstack
can be growing at
any given time. The object is started implicitly as soon as any bytes
are added to it with the following function calls, but must be finished
explicitly using obstack_finish
.
int obstack_1grow (struct obstack*obstack_handle, char ch) | glibc function |
Simply grow the current object in obstack_handle by a single byte, ch. |
void *obstack_finish (struct obstack*obstack_handle) | glibc function |
Declare that the current object has finished growing, and return the address of the start of that object. The next time one of the grow functions is called for obstack_handle, a new object will be started. |
There are also other functions for growing the current object in an
obstack
by a different size than a single byte:
int obstack_ptr_grow (struct obstack*obstack_handle, void *data) | glibc function |
Grow the current object in obstack_handle by sufficient size to hold a pointer, and fill that space with a copy of data. |
int obstack_int_grow (struct obstack*obstack_handle, int data) | glibc function |
Grow the current object in obstack_handle by sufficient size to
hold an int , and fill that space with a copy of data.
|
int obstack_grow (struct obstack*obstack_handle, void *data, int size) | glibc function |
Grow the current object in obstack_handle by size bytes,
and fill that space by copying size bytes from data.
|
int obstack_grow0 (struct obstack*obstack_handle, void *data, int size) | glibc function |
Grow the current object in obstack_handle by size bytes,
and fill that space by copying size bytes from data,
followed by a null character.
|
int obstack_blank (struct obstack*obstack_handle, int size) | glibc function |
Grow the current object in obstack_handle by size bytes,
but leave them uninitialized. You can also shrink the current object by
passing a negative value in size, if you are careful not to reduce
the size of the object below zero.
|
int obstack_object_size (struct obstack*obstack_handle) | glibc function |
Return the size of the growing object. This function is useful for
ensuring that you don't decrease the size of an object below zero with
obstack_blank . If the current object has not yet been grown (for
example immediately after a call to obstack_finish ), this function
will return zero.
|