Most applications need routines for allocating and deallocating memory on request. The LabVIEW memory manager is the set of platform-independent memory manager functions you can use to dynamically allocate, manipulate, and deallocate memory. The memory manager supports dynamic allocation of both nonrelocatable and relocatable blocks, using pointers and handles.
If you need to perform dynamic memory allocation or manipulation from external code modules, use the memory manager. If your external code operates on data types other than scalars, you should understand how LabVIEW manages memory and know which utilities manipulate data.
The following code shows how the memory manager defines generic handle and pointer data types:
typedef uChar *UPtr;
typedef uChar **UHandle;
All data passed to or from a shared library is allocated using the memory manager. You should only use file manager functions, not the memory manager routines, to manipulate paths. Thus, your shared library should use the memory routines when working with parameters passed from the block diagram.
Applications use the following types of memory allocation:
Note��You can allocate memory for variables in C code using malloc. However, do not assign the resulting pointer to any variable you pass back to the LabVIEW block diagram. Use the memory manager functions if you want to create or resize memory associated with a variable passed from the LabVIEW block diagram. |
With static memory allocation, the compiler determines memory requirements when you create an application. When you launch the application, LabVIEW creates memory for the known global memory requirements of the application. The memory LabVIEW creates remains allocated while the application runs. Static memory allocation is simple to work with because the compiler handles all the details.
However, static memory allocation cannot address the memory management requirements of most real-world applications because you cannot determine most memory requirements until run time. Also, statically declared memory might result in larger memory requirements because the memory is allocated for the duration of the application.
With dynamic memory allocation, you reserve memory when you need it and free memory when you are no longer using it. Dynamic allocation requires more work than static memory allocation because you have to determine memory requirements and allocate and deallocate memory as necessary.
You can use the LabVIEW memory manager to dynamically allocate memory in the following ways:
The more conventional method uses pointers to allocate memory. With pointers, you request a block of memory of a certain size. The routine returns the address of the block of memory to your shared library. When you no longer need the block of memory, you call a routine to free the block of memory. You can use the block of memory to store data. You reference the data stored in the block of memory by using the address the manager routine returned when you created the pointer. You can make copies of the pointer and use them in multiple places in your application to refer to the same data.
Pointers in the LabVIEW memory manager are nonrelocatable, which means the manager never moves the memory block to which a pointer refers while that memory is allocated for a pointer. Because other references to the memory block do not become out of date, not moving the memory block allocated to a pointer avoids problems that occur when you need to change the amount of memory allocated to a pointer. If you need more memory, sufficient memory might not exist to expand the memory space of the pointer without moving the memory block to a new location. If an application had multiple references to the pointer, moving the memory block to a new location causes problems because each pointer refers to the old memory address of the data. Using invalid pointers can cause severe problems.
A second form of memory allocation uses handles. As with pointers, when you allocate memory using handles, you request a block of memory of a certain size. The memory manager allocates the memory and adds the address of the memory block to a list of master pointers. The memory manager returns a handle that is a pointer to the master pointer. If you reallocate a handle and it moves to another address, the memory manager updates the master pointer to refer to the new address. If you look up the correct address using the handle, you access the correct data.
Use handles to perform most memory allocation in LabVIEW. Pointers are available because in some cases they are more convenient and simpler to use.
Create a handle using DSNewHandle, which allows you to specify the size of the memory block, or DSNewAlignedHandle, which allows you to specify the size, alignment, and alignment offset of the memory block. Create a pointer using DSNewPtr. Create a handle or pointer and set it to all zeros using DSNewHClr, DSNewAlignedHClr, and DSNewPClr.
When you are finished with the handle or pointer, release it using DSDisposeHandle or DSDisposePtr.
If you need to resize an existing handle, use the DSSetHandleSize function, which determines the size of an existing handle. If you need to resize and realign an existing handle, use the DSSetAlignedHandleSize function, which determines the size, alignment, and alignment offset of an existing handle. You also can resize and realign an existing handle and set it to all zeros using DSSetAlignedHSzClr. Because pointers are not relocatable, you cannot resize them.
A handle is a pointer to a pointer. In other words, a handle is the address of an address. The second pointer, or address, is a master pointer, which means it is maintained by the memory manager. Languages that support pointers provide operators for accessing data by its address. With a handle, you use this operator twice�once to get to the master pointer, and a second time to get to the actual data.
Additional routines make it easy to copy and concatenate handles and pointers to other handles, check the validity of handles and pointers, and copy or move blocks of memory from one place to another.
This simple example demonstrates how to work with pointers and handles in C.
The following code shows how to work with a pointer to an int32:
int32 *myInt32P;
myInt32P = (int32 *)DSNewPtr(sizeof(int32));
*myInt32P = 5;
x = *myInt32P + 7;
...
DSDisposePtr(myInt32P);
The first line declares the variable myInt32P as a pointer to, or the address of, a 32-bit signed integer. The first line does not actually allocate memory for the int32. The first line creates memory for an address and associates the name myInt32P with that address. The P at the end of the variable name is a convention used in this example to indicate the variable is a pointer.
The second line creates a block of memory in the data space large enough to hold a single 32-bit signed integer and sets myInt32P to refer to this memory block.
The third line places the value 5 in the memory location to which myInt32P refers. The * operator refers to the value in the address location.
The fourth line sets x equal to the value at address myInt32P plus 7.
The last line frees the pointer.
The following code is the same example using handles instead of pointers:
int32 **myInt32H;
myInt32H =(int32**)DSNewHandle(sizeof(int32));
**myInt32H = 5;
x = **myInt32H + 7;
...
DSDisposeHandle(myInt32H);
The first line declares the variable myInt32H as a handle to a 32-bit signed integer. Strictly speaking, the first line declares myInt32H as a pointer to a pointer to an int32. As with the previous example, the first line does not allocate memory for the int32. The first line creates memory for an address and associates the name myInt32H with that address. The H at the end of the variable name is a convention used in this example to indicate the variable is a handle.
The second line creates a block of memory in the data space large enough to hold a single int32. DSNewHandle places the address of the memory block as an entry in the master pointer list and returns the address of the master pointer entry. Finally, the second line sets myInt32H to refer to the master pointer.
The third line places the value 5 in the memory location to which myInt32H refers. Because myInt32H is a handle, you use the * operator twice to get to the data.
The fourth line sets x equal to the value referenced by myInt32H plus 7.
The last line frees the handle.
This example shows only the simplest aspects of how to work with pointers and handles in C. Other examples show different aspects of using pointers and handles. Refer to a C manual for a list of other operators you can use with pointers and for more information about how to work with pointers.