To cement your knowledge of pointers, objects and memory management we'll use three cases to illustrate how they are used when programming EMBOSS code. First a look at the AjPPdbtosp
object which holds sequence codes. It is not widely used but does illustrate a lot of the key points nicely. Then the AJAX string object AjPStr
and the special memory handling required to ensure failsafe memory allocation of the object functions. Finally the AJAX array objects to see how other dynamic objects are implemented.
Consider the following object which holds swissprot codes and accession numbers for a PDB code:
/* @data AjPPdbtosp ******************************************************* ** ** Ajax Pdbtosp object. ** ** Holds swissprot codes and accession numbers for a PDB code. ** ** AjPPdbtosp is implemented as a pointer to a C data structure. ** ** @alias AjSPdbtosp ** @alias AjOPdbtosp ** ** @attr Pdb [AjPStr] PDB code ** @attr Acc [AjPStr*] Accession numbers ** @attr Spr [AjPStr*] Swissprot codes ** @attr n [ajint] No. entries for this pdb code ** @attr Padding [char[4]] Padding to alignment boundary ** ** ** @@ ****************************************************************************/ typedef struct AjSPdbtosp { AjPStr Pdb; AjPStr *Acc; AjPStr *Spr; ajint n; char Padding[4]; } AjOPdbtosp; #define AjPPdbtosp AjOPdbtosp*
There is nothing new here other than Acc
and Spr
which are both pointers to AjPStr
objects. These would have better been declared by using AjPPStr
but that is not done here. As an AjPStr
is itself a pointer (to a string object proper) you can see that we're dealing with pointers to pointers. In this case Acc
and Spr
are used to create two arrays of strings as you can see in the constructor function (below).
Here is the constructor function:
/* @func ajPdbtospNew *********************************************************** ** ** Pdbtosp object constructor. Fore-knowledge of the number of entries is ** required. This is normally called by the ajPdbtospReadCNew / ** ajPdbtospReadNew functions. ** ** @param [r] n [ajint] Number of entries ** ** @return [AjPPdbtosp] Pointer to a Pdbtosp object ** @@ ******************************************************************************/ AjPPdbtosp ajPdbtospNew(ajint n) /* 1. */ { AjPPdbtosp ret = NULL; /* 2. */ ajint i=0; AJNEW0(ret); /* 3. */ ret->Pdb = ajStrNew(); /* 5. */ if(n) { AJCNEW0(ret->Acc,n); /* 4. */ AJCNEW0(ret->Spr,n); /* 4. */ for(i=0; i< n; i++) { ret->Acc[i]=ajStrNew(); /* 6. */ ret->Spr[i]=ajStrNew(); /* 6. */ } } ret->n = n; /* 7. */ return ret; }
The commented lines are as follows:
The first line declares that the function returns an object pointer of type AjPPdbtosp
. The parameter ajint n
is the size the Acc
and Spr
arrays should be, i.e. the number of pairs of Acc
/ Spr
values that the object will hold.
The next line declares a variable called ret
. This is the object pointer that is going to have memory allocated to it and will be returned to the calling function.
AJNEW0(ret);
is the line that allocates an object proper to the pointer ret
. This will now point to an instance of an AjPPdbtosp
memory. By the time AJNEW0(ret);
returns, memory space for an AjPPdbtosp
object is reserved. This means enough space for an AjPStr
, an ajint
and two pointers (AjPStr *
). Note that the two arrays or any string objects proper have not yet been allocated!
AJNEW0
sets all the structure elements to 0, this means the element n
is set to 0 and the three pointers are set to NULL
. AJNEW0
is a macro: it will allocate a single object of the correct type to any pointer that is passed to it - it can be used with any object.
Compare AJNEW0
to the two AJCNEW0
lines. AJCNEW0
will allocate an array of objects of any type and initialises the new variables to 0 or NULL
as required. In this case, arrays of n
objects each will be created. It is important to bear in mind here that ret->Acc
and ret-Spr
are passed to the macro. These are of the type AjPStr *
, which means that the "object" which they point to is in fact another pointer variable. Therefore these macro calls will allocate arrays of n
pointers, not arrays of instances of AjPStr
objects as one might (incorrectly) first imagine. They create an array of n
AjPStr
object pointers allocated to each of Acc
and Spr
. In other words, ret->Acc
and ret->Spr
will point to blocks of memory each holding n
pointer variables which are as yet NULL
(unallocated).
The arrays are created but still no strings yet. ret->Pdb = ajStrNew();
allocates memory for a string object to the pointer Pdb
in the new object. Notice that ->
is used to dereference the pointer ret
; and get to the Pdb
element. This is the standard way in C of accessing elements in a data structure when you have a pointer to that data structure.
The lines ret->Acc[i]=ajStrNew();
and ret->Spr[i]=ajStrNew();
allocate memory for the n
string objects for each array. It also illustrates how pointer and array notation can be used together. In this case, the 'i'th element of the arrays that ret->Acc
and ret->Spr
point to are accessed. The elements in these arrays are AjPStr
(object pointers) and a string object is allocated to each of them.
The rest is obvious. The integer in the object is set to the size of the arrays and the pointer to the new object, complete with an allocated string and two arrays of strings, is returned to the calling function by return ret;
. Note that the constructor should be coded to deal with negative arguments in a safe way, but that is not done here.
It is the job of the destructor function to free the object itself and any memory that its member elements might point to. The destructor safely clears up all of the memory that was allocated by the constructor. This is achieved by calling other destructor functions as appropriate and by using AJFREE
. The code is shown below:
/* @func ajPdbtospDel *********************************************************** ** ** Destructor for Pdbtosp object. ** ** @param [w] thys [AjPPdbtosp*] Pdbtosp object pointer ** ** @return [void] ** @@ ******************************************************************************/ void ajPdbtospDel(AjPPdbtosp *thys) /* 1. */ { AjPPdbtosp pthis = NULL; /* 2. */ ajint i; if(!thys) return; pthis = *thys; if(!pthis) return; ajStrDel(&pthis->Pdb); /* 3. */ if(pthis->n) { for(i=0; i< pthis->n; i++) { ajStrDel(&pthis->Acc[i]); /* 4. */ ajStrDel(&pthis->Spr[i]); /* 4. */ } AJFREE(pthis->Acc); /* 5. */ AJFREE(pthis->Spr); /* 5. */ } AJFREE(pthis); /* 6. */ (*thys)=NULL; /* 6. */ return; }
Again, this is broken down comment by comment:
The function, like all destructors, takes the address of the object pointer being deleted (AjPPdbtosp *thys
).
For convenience a second pointer is declared and is used in the following lines to dereference thys
. This is purely for reasons of clarity. The function returns if either an empty address was passed or if the pointer stored there is NULL
.
The string object in AjPPdbtosp
is deleted first by calling the default destructor function with the address of the string.
The string objects proper, referenced through the arrays, are deleted by calling ajStrDel
in a loop for every array element in both arrays.
AJFREE
is then called to delete the arrays themselves, referenced by pthis->Acc
and pthis->Spr
.
Once the loop terminates AJFREE
is again called, this time freeing memory for the AjPPdbtosp
object itself. The pointer is set to NULL
so that it's ready for re-use by the calling function.
It should be clear that although AJFREE
will free the memory pointed to by its argument, as used here it frees the arrays but not the string objects proper that are pointed to; that is the job of the ajStrDel
calls in the preceding code.
Here is a code snippet illustrating how the object constructor and destructor could be used. You'll notice they're used in exactly the same way as any other object:
int main() { AjPPdbtosp ptr=NULL; ptr = ajPdbtospNew(10); ajPdbtospDel(&ptr); /* ptr will have been reset to NULL now, and is ready for reuse */ ptr = ajPdbtospNew(10); ajPdbtospDel(&ptr); }
The string object (AjPStr
) is one of the simplest of all the AJAX objects. AJAX strings have more functions than any other datatype and are used by many other objects. Two features distinguish its use from standard C-type (char *
) strings. First, AJAX strings are dynamic objects, meaning that memory is dynamically reallocated as needed so that you never run out of space when using the object functions; a string will grow automatically as required. Second, AJAX strings are reference counted. This means that the object itself keeps track of how many references (pointers) to the string there are in the code that have been requested by calling library functions. It is not until all references to a string are deleted that the string itself is freed. This ensures that broken references to a string do not occur and that you always have a handle on objects in memory.
The string definition is shown below:
typedef struct AjSStr { ajuint Res; ajuint Len; char *Ptr; ajuint Use; ajint Padding; } AjOStr; #define AjPStr AjOStr* typedef AjPStr* AjPPStr;
A structure is defined called AjSStr
of 4 elements (Len
, Res
, Use
and Ptr
) and with three new datatype names, AjOStr
for the object itself, AjPStr
for the object pointer and AjPPStr for a pointer to an AjPStr
.
The Ptr
pointer is just a standard C one which holds a character string and Len
is its length. In contrast to C-type strings, the character string may or may not be NULL
terminated; the library functions for printing AjPStr
objects use the length field (Len
) for how many characters to print and won't stop at the first NULL
if there is one.
The Res
element indicates how much reserved dynamic memory is associated with the object. This is always at least equal to Len
but is often more. Res
is and should be outside your direct control. If you use a library call to add anything to the string then, if it'll fit within the memory given by Res
then the operation is performed immediately. If the memory required is larger than Res
then more memory is allocated and the Res
item is updated. More memory than required is usually allocated.
Use
is the string usage counter. Sometimes you'll want two or more references to a single string rather than making a genuine copy. EMBOSS functions that do this increment the string's usage counter. The usage counter is decremented when a call to destroy either the string itself, or a reference to it, is made. When the usage counter reaches zero the object will be deleted. All of this is function internals, so you don't need to worry about it as long as you don't change the object elements directly. If you intend altering the contents of an object then safety is guaranteed if you use the available library functions.
Finally, the Padding
element indicates the number of characters used to pad the string to its alignment boundary and is used only to keep pedantic compilers happy.
Code for the default constructor function is shown below:
/* @func ajStrNew ************************************************************* ** ** Default string constructor which allocates memory for a string. ** ** The null string usage pointer is incremented. ** ** @return [AjPStr] Pointer to an empty string ** @@ ******************************************************************************/ AjPStr ajStrNew(void) { return ajStrNewRef(strPNULL); }
You can see that the ajStrNew
is a wrapper function to ajStrNewRef
. It passes to ajStrNewRef
an interal string object called strPNULL
which is defined in ajstr.c
:
#define NULL_USE 1 char charNULL[1] = ""; AjOStr strONULL = { 1,0,charNULL,NULL_USE,0}; AjPStr strPNULL = &strONULL;
strPNULL
is a global variable for an empty object called the "AJAX NULL string". This has a single character of reserved memory, length of zero, a C-type string which is set to NULL
, a reference count of 1 and zero padding.
If you look at ajStrNewRef
:
/* @func ajStrNewRef ********************************************************** ** ** String referencing function which returns a pointer to the string passed in ** and increases its reference count. ** ** Not a true string copy, but if modified the new string will behave ** as a copy. ** ** @param [u] refstr [AjPStr] AJAX string object ** @return [AjPStr] Pointer to the string passed as an argument, ** with its use count increased by 1. ** @@ ******************************************************************************/ AjPStr ajStrNewRef(AjPStr refstr) { if(!refstr) ajFatal("ajStrNewRef source string NULL"); refstr->Use++; return refstr; }
All this function does is increase the reference count of the object that was passed and return the same pointer. It raises a fatal error if NULL
was passed.
In other words, a call to ajStrNew
doesn't immediately instantiate an AjSStr
object, it just returns the address of the "global AJAX NULL String". It's only when the char *
string (Ptr
) is given a non-NULL
value (by whatever means) that memory for the string object proper will be allocated. AJAX is programmed in this way for maximum speed and efficiency of string handling. You can see this for yourself if you print the reference count of a string which you have just allocated using ajStrNew
but not yet used. You might be surprised at the value of the usage count which is higher than you might expect. The reference count may well be in the hundreds owing to the call to embInit
in the application code, which itself makes, indirectly, many calls to ajStrNew
. If true objects had been allocated for all these strings the code would be less efficient.
Things are different if you call the alternative constructor function ajStrNewRes
, which allocates memory for a string with an initial reserved size:
/* @func ajStrNewRes ********************************************************** ** ** String constructor which allocates memory for a string of an initial ** reserved size (including a possible null). ** ** @param [r] size [ajuint] Reserved size (including a possible null). ** @return [AjPStr] Pointer to an empty string of specified size. ** @@ ******************************************************************************/ AjPStr ajStrNewRes(ajuint size) { AjPStr thys; thys = ajStrNewResLenC("", size, 0); return thys; }
The function declares an AJAX string object (thys
) then calls ajStrNewResLenC
to allocate the string, passing to that function the size (size
) of the required string:
/* @func ajStrNewResLenC ****************************************************** ** ** String constructor which allocates memory for a string of a given length ** and of an initial reserved size (including a possible null) and initialises ** it with the text string provided. ** ** @param [r] txt [const char*] Null-terminated character string to initialise ** the new string. ** @param [r] size [ajuint] Reserved size (including a possible null). ** @param [r] len [ajuint] Length of txt to save calculation time. ** @return [AjPStr] Pointer to a string of the specified size ** containing the supplied text. ** @@ ******************************************************************************/ AjPStr ajStrNewResLenC(const char* txt, ajuint size, ajuint len) { AjPStr thys; ajuint minlen; minlen = size; if(size <= len) minlen = len+1; thys = strNew(minlen); thys->Len = len; if(txt) memmove(thys->Ptr, txt, len+1); thys->Ptr[len] = '\0'; return thys; }
This function is an alternative constructor function. It sets a minimum string length (minlen
) to the requested reserved size (size
), or to the current length (len
) plus 1 (for a terminating NULL
character) if the requested size is not greater than the current length. It then calls strNew
which is a static function in ajstr.c
to allocate a string object (this function is shown below). The string length (thys->Len
) is set, and the specified text (txt
, which is an empty string when called by ajStrNewRes
as in this example) is copied (using memmove
) to the C-type string pointer (thys-Ptr
) in the AJAX string object. A terminating NULL
character is added.
Let's look at the static constructor function which actually allocates the string object:
/* @funcstatic strNew ****************************************************** ** ** Internal constructor for modifiable AJAX strings. Used by all the string ** parameterized constructors to allocate the space for the text string. ** The exception is ajStrNew which returns a clone of the null string. ** ** @param [rE] size [ajuint] size of the reserved space, including the ** terminating NULL character. Zero uses a default string size STRSIZE. ** @return [AjPStr] A pointer to an empty string ** @@ ******************************************************************************/ static AjPStr strNew(ajuint size) { AjPStr ret; if(!size) size = STRSIZE; AJNEW0(ret); ret->Res = size; ret->Ptr = AJALLOC0(size); ret->Len = 0; ret->Use = 1; ret->Ptr[0] = '\0'; strAlloc += size; strCount++; strTotal++; return ret; }
The function first checks that a reserved size has been specified, and sets this to the default length STRSIZE
if not. STRSIZE
is defined in ajstr.c
:
#define STRSIZE 32
The macro AJNEW0
is called. You'll recall that this is the equivalent of a calloc
and allocates memory to an object pointer (ret
) for a single object of a given type, in this case, a string. The memory is initialised to zero.
The rest of that block of code assigns correct values to the other elements in the string object. You can see that the reserved size is set to size
and the first character of the string is set to a NULL
character, meaning you have a new, empty string with the specified reserved size, a pointer to which is returned to the calling function.
The code also sets some global variables (strAlloc
, strCount
and strTotal
) used internally for statistics and for debugging strings. You needn't worry about those.
The internals of string destruction are simpler than construction. The default constructor ajStrDel
is shown below:
/* @func ajStrDel ************************************************************* ** ** Default string destructor which frees memory for a string. ** ** Decrements the use count. When it reaches zero, the string is removed from ** memory. If the given string is NULL, or a NULL pointer, simply returns. ** ** @param [d] Pstr [AjPStr*] Pointer to the string to be deleted. ** The pointer is always deleted. ** @return [void] ** @cre The default null string must not be deleted. Calling this ** routine for copied pointers could cause this. An error message ** is issued and the null string use count is restored. ** @@ ******************************************************************************/ void ajStrDel(AjPStr* Pstr) { AjPStr thys; if(!Pstr) return; if(!*Pstr) return; thys = *Pstr; --thys->Use; if(!thys->Use) { /* any other references? */ if(thys == strPNULL) { ajErr("Error - trying to delete the null string constant\n"); thys->Use = NULL_USE; /* restore the original value */ } else { AJFREE(thys->Ptr); /* free the string */ strFree += thys->Res; strFreeCount++; strCount--; thys->Res = 0; /* in case of copied pointers */ thys->Len = 0; AJFREE(*Pstr); /* free the object */ } } *Pstr = NULL; return; }
It is clear from AjPStr* Pstr
that the function takes the address of a string object pointer. The function first checks that NULL
is not passed (if(!Pstr)
) and that the pointer itself is not NULL
(if(!*Pstr)
). In other words, it ensures that the AjPStr
passed in by reference does actually point to something. The function must assume that it points to a string and this will be the case if there are no bugs in the code. This is why pointers when declared should be set to NULL
. If they are not and receive some junk value on start-up then this function (and many others like it) will mistakenly assume that it references valid memory and will, at best, head for a segmentation fault or bus error when it tries to address that memory.
The line --thys->Use;
reduces the reference count of the string by 1. If this becomes zero then AJFREE
is used to free the object. It is called twice, once to free the C-type string (AJFREE(thys->Ptr);
) and again to free the object proper (AJFREE(*Pstr);
). Some global variables (strFree
, strFreeCount
and strCount
) used internally for debugging and statistics are also set.
Finally, the string object pointer that was passed is set to NULL
(*Pstr = NULL;
) so that it's ready for re-use by the program.
Now we'll look at two string functions to see how pointers and memory are handled. ajStrMatchS
is a simple function for matching two AJAX strings:
/* @func ajStrMatchS ********************************************************** ** ** Simple test for matching two strings. ** ** @param [r] str [const AjPStr] String ** @param [r] str2 [const AjPStr] Second String ** @return [AjBool] ajTrue if two complete strings are the same ** @@ ******************************************************************************/ AjBool ajStrMatchS(const AjPStr str, const AjPStr str2) { if(!str || !str2) return ajFalse; if(!strcmp(str->Ptr, str2->Ptr)) return ajTrue; return ajFalse; }
It is passed two AJAX string objects and uses the C function strcmp
to compare the C-type strings in the object, returning ajTrue
if they are the same or ajFalse
otherwise. The function merely reads the value of the strings passed so will never need to allocate memory.
ajStrAssignS
is different. This function assigns the value of one string to another. The string is copied rather than just setting a reference (pointer) to the original:
/* @func ajStrAssignS ********************************************************* ** ** Copy a string to a string. ** ** This is a genuine copy (reference count isn't used). Useful where both ** strings will be separately overwritten later so that they can both remain ** modifiable. ** ** @param [w] Pstr [AjPStr*] Target string. ** @param [rN] str [const AjPStr] Source string. ** @return [AjBool] ajTrue if string was reallocated ** @@ ******************************************************************************/ AjBool ajStrAssignS(AjPStr* Pstr, const AjPStr str) { AjBool ret = ajFalse; AjPStr thys; if(!str) { return ajStrAssignC(Pstr, ""); } ret = ajStrSetRes(Pstr, str->Len+1); /* minimum reserved size, OR more */ thys = *Pstr; thys->Len = str->Len; memmove(thys->Ptr, str->Ptr, str->Len+1); return ret; }
The function takes the source string that is being copied (str
) and a destination string Pstr
). The destination string (Pstr
) will be modified, therefore the address must be passed (AjPStr* Pstr
).
You can see that if a NULL
pointer is passed for the source string then an empty string is written to the destination string by calling ajStrAssignC
. This is the failsafe mechanism that was mentioned before. The function should also check, as a safety measure, whether NULL
is passed for the address of the destination string, but it currently doesn't do this.
ajStrSetRes
is called (see below) to ensure that the destination string is a new string, not referenced by other string objects, and is big enough for its intended purpose. The length of the destination string (thys->Len
) is set and the C-type string in the source string (str->Ptr
) is copied (using memmove
) to the destination string (thys->Ptr
). ajTrue
is returned if the string was reallocated or ajFalse
otherwise.
ajStrSetRes
ensures a string is big enough for its intended purpose:
/* @func ajStrSetRes ********************************************************** ** ** Ensures a string is modifiable and big enough for its intended purpose. ** ** The target string is guaranteed to have a reference count of 1 and a ** minimum reserved size. ** ** @param [u] Pstr [AjPStr*] String ** @param [r] size [ajuint] Minimum reserved size. ** @return [AjBool] ajTrue if the string was reallocated ** @@ ******************************************************************************/ AjBool ajStrSetRes(AjPStr* Pstr, ajuint size) { AjPStr thys; ajuint savesize; savesize = size; /* often part of *Pstr, about to vanish */ if(!*Pstr) { *Pstr = ajStrNewRes(savesize); return ajTrue; } thys = *Pstr; if((thys->Use > 1) || (thys->Res < savesize)) { strCloneL(Pstr, savesize); return ajTrue; } return ajFalse; }
It takes the address of a target string and a minimum size (size
). If the target string is NULL
then a string with a reserved size is allocated using ajStrNewRes
. That function has already been explained. Otherwise, if the usage count is greater than 1 or if the current reserved size is less than that requested, the static function strCloneL
is called (see below) to make a copy of the string but with a usage count of 1 and a minimum reserved size. ajTrue
is returned if the string was reallocated or ajFalse
otherwise.
strCloneL
takes the address of a target string (Pstr
) and a reserved size (size
):
/* @funcstatic strCloneL ****************************************************** ** ** Makes a new clone of a string with a usage count of one and a minimum ** reserved size. ** ** @param [w] Pstr [AjPStr*] String ** @param [r] size [ajuint] Minimum reserved size. ** @return [void] ** @@ ******************************************************************************/ static void strCloneL(AjPStr* Pstr, ajuint size) { AjPStr thys; AjPStr ret; thys = *Pstr; ret = ajStrNewResLenC(thys->Ptr, size, thys->Len); ajStrDel(Pstr); *Pstr = ret; return; }
It calls ajStrNewResLenC
to allocate a string with a reserved size, as has already been explained. The original target string that was passed is deleted by calling the destructor ajStrDel
.
It should be said that strings are a special case and that the internals of memory management for most other objects are considerably simpler. It is only for strings, that are so widely used by the other libraries, that special handling is needed for reasons of safety and efficiency.
The memory management macros are nicely illustrated by the array handling functions in ajarr.c
. Here we'll consider the constructor and destructor functions for the AjPInt
and AjPInt2d
objects. These are dynamic one-dimensional (AjPInt
) and two-dimensional (AjPInt2d
) arrays of integers.
Here are the object definitions:
typedef struct AjSInt { ajuint Res; ajuint Len; ajint *Ptr; } AjOInt; #define AjPInt AjOInt* typedef struct AjSInt2d { ajuint Res; ajuint Len; AjPInt *Ptr; } AjOInt2d; #define AjPInt2d AjOInt2d*
Both objects include variables for the current length of the array (Len
) and the reserved size (Res
). AjPInt
includes a pointer (Ptr
) to ajint
which, when allocated, will point to an array of ajint
values. In contrast, AjPInt2d
includes a pointer (Ptr
) to AjPInt
which will eventually point to an array of AjPInt
object pointers.
ajIntNewRes
is a constructor for AjPInt
objects, allocating an array with an initial reserved size. The code is shown below:
/* @func ajIntNewRes ********************************************************* ** ** Constructor given an initial reserved size. ** ** @param [r] size [ajuint] Reserved size ** @return [AjPInt] Pointer to an empty integer array struct of specified size. ** @category new [AjPInt] Constructor with reserved size ** @@ ******************************************************************************/ AjPInt ajIntNewRes(ajuint size) { AjPInt thys; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(ajint)); thys->Len = 0; thys->Res = size; arrTotal++; arrAlloc += size*sizeof(ajint); ajDebug("ajIntNewRes size %d*%d %d\n", size, sizeof(ajint), size*sizeof(ajint)); return thys; }
AJNEW0
is used to allocate memory for a single AjPInt
object. AJALLOC0
is called to create an array of AJAX integers (ajint
) of size size
. arrTotal
and arrAlloc
are also set which are global variables used for debugging arrays.
ajIntDel
is the AjPInt
destructor function:
/* @func ajIntDel ************************************************************* ** ** Default destructor for AJAX integer arrays. ** ** If the given array is a NULL pointer, simply returns. ** ** @param [d] thys [AjPInt*] Pointer to the ajint array to be deleted. ** The pointer is always deleted. ** @return [void] ** @category delete [AjPInt] Default destructor ** @@ ******************************************************************************/ void ajIntDel(AjPInt *thys) { if(!thys || !*thys) return; ajDebug("ajIntDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arrFreeCount++; return; }
AJFREE
is called twice. The first call (AJFREE((*thys)->Ptr);
) frees the array of integers. The second call (AJFREE(*thys);
) frees the object itself. You can see that the pointer (thys
) that is passed to the function is set to NULL
using the code *thys = NULL;
.
ajInt2dNewRes
is a constructor for AjPInt2d
objects, allocating a 2D array with an initial reserved size for the first dimension. The code is shown below:
/* @func ajInt2dNewRes ******************************************************** ** ** Constructor given an initial reserved size. ** ** @param [r] size [ajuint] Reserved size 1st dim ** @return [AjPInt2d] Pointer to an empty integer 2d array struct of ** specified size. ** @category new [AjPInt2d] Constructor with reserved size ** @@ ******************************************************************************/ AjPInt2d ajInt2dNewRes(ajuint size) { AjPInt2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPInt)); thys->Len = 0; thys->Res = size; for(i=0;i<size;++i) thys->Ptr[i] = NULL; arr2dAlloc++; return thys; }
AJNEW0
is again called to create the basic object, an instance of an AjPInt2d
in this case. AJALLOC0
is called to create an array of pointers to 1D integer array objects (AjPInt
) of size size
. You can deduce from the code that the second dimensions of the array (the arrays of integers themselves) is not created until it is needed. This is for reasons of efficiency.
ajInt2dDel
is the AjPInt2d
destructor function:
/* @func ajInt2dDel *********************************************************** ** ** Default destructor for AJAX integer arrays. ** ** If the given array is a NULL pointer, simply returns. ** ** @param [d] thys [AjPInt2d*] Pointer to the ajint array to be deleted. ** The pointer is always deleted. ** @return [void] ** @category delete [AjPInt2d] Default destructor ** @@ ******************************************************************************/ void ajInt2dDel(AjPInt2d *thys) { ajint i; if(!thys || !*thys) return; ajDebug("ajInt2dDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res); for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajIntDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr2dFreeCount++; return; }
The function takes the address of the AjPInt2d
object (thys
) that is to be freed. To get to the object proper you must dereference thys
, i.e. everywhere in the function body where you see *thys
. You will recall that Ptr
references an array of AjPInt
object pointers, each of which points to array of integers.
The integer arrays are freed by calling the destructor function ajIntDel
in a loop. This destructor takes the address of a AjPInt
. Array notation is used to index the 'i
'th element of the AjPInt
array, having first dereferenced thys
((*thys)->Ptr[i]
). This retrieves an individual AjPInt
object, the address of which is needed by the destructor which is why you have ajIntDel(&((*thys)->Ptr[i]));
.
AJFREE
is then called twice. The first call (AJFREE((*thys)->Ptr);
) frees the array of AjPInt
. The second call (AJFREE(*thys);
) frees the AjPInt2d
object itself. You can see that the pointer (thys
) that is passed to the function is set to NULL
using the code *thys = NULL;
.
The function ajInt2dGet
is used to retrieve a value from a 2D integer array. The source code is below:
/* @func ajInt2dGet *********************************************************** ** ** Retrieve an element from an AJAX 2d integer array. ** ** If the given array is a NULL pointer, simply returns. ** ** @param [r] thys [const AjPInt2d] Pointer to the ajint array. ** @param [r] elem1 [ajuint] array element. ** @param [r] elem2 [ajuint] array element. ** ** @return [ajint] contents of array element ** @category cast [AjPInt2d] Retrieve an integer from an array ** @@ ******************************************************************************/ ajint ajInt2dGet(const AjPInt2d thys, ajuint elem1, ajuint elem2) { AjPInt t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad ajint array index [%d][%d]\n",elem1, elem2); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][]\n",elem1); return ajIntGet(t,elem2); }
The element in column elem1
and row elem2
will be retrieved from the array thys
. An error is raised if you try to inspect an element that has not been allocated. Otherwise the value of the element is returned.
The function ajInt2dPut
is used to load a 2D integer array element with a value. If the array is of insufficient size then the memory is extended as required. The source code is below:
/* @func ajInt2dPut *********************************************************** ** ** Load an integer 2d array element. ** ** If the given array is a NULL pointer an error is generated. ** If the array is of insufficient size then the array is extended. ** Negative indices generate an error. ** ** @param [w] thys [AjPInt2d*] Pointer to the ajint array. ** @param [r] elem1 [ajuint] array element. ** @param [r] elem2 [ajuint] array element. ** @param [r] v [ajint] value to load. ** ** @return [AjBool] true if any array was extended. ** @category modify [AjPInt2d] Load an integer array element ** @@ ******************************************************************************/ AjBool ajInt2dPut(AjPInt2d *thys, ajuint elem1, ajuint elem2, ajint v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d]\n",elem1, elem2); if(elem1 < (*thys)->Res) { ajDebug("ajInt2dPut [%u][%u] %d ([%u] %x)\n", elem1, elem2, v, (*thys)->Len, (*thys)->Ptr[elem1]); if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajIntNew(); return ajIntPut(&(*thys)->Ptr[elem1],elem2,v); } arrInt2dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajIntNew(); ajIntPut(&(*thys)->Ptr[elem1],elem2,v); return ajTrue; }
There is nothing new here that you haven't seen already. Work through it to test your understanding of pointers, particularly how pointer and array notation may be mixed.