Introducing C++ Part 3 (More on pointers)

In this article “a C program” means a program written in C or C++.

Any area of knowledge can be understood at varying degrees, ranging from a cursory overview to an in-depth, intuitive understanding. That higher level of understanding for C can only be achieved with a solid understanding of pointers and the management of memory.

The key to comprehending pointers is understanding how memory is managed in a C program. After all, pointers contain addresses in memory. If we don’t understand how memory is organized and managed, it is difficult to understand how pointers work.

When a C program is compiled, it works with three types of memory:


Statically declared variables are allocated to this type of memory. Global variables also use this region of memory. They are allocated when the program starts and remain in existence until the program terminates. While all functions have access to global variables, the scope of static variables is restricted to their defining function.


These variables are declared within a function and are created when a function is called. Their scope is restricted to the function, and their lifetime is limited to the time the function is executing.


Memory is allocated from the heap and can be released as necessary. A pointer references the allocated memory. The scope is limited to the pointer or pointers that reference the memory. It exists until it is released.

SCOPE                                  LIFETIME

The entire file                        While Application Runs

The function it is declared within     While Application Runs

Automatic (local): 
The function it is declared within     While Function Executes

Determined by the pointers that        Until Memory is Freed
reference this memory 
(reference counted)             

Understanding these types of memory will enable you to better understand how pointers work. Most pointers are used to manipulate data in memory.

Understanding how memory is partitioned and organized will clarify how pointers manipulate memory.

A pointer variable contains the address in memory of another variable, object, or function. An object is considered to be memory allocated using one of the memory allocation functions, such as the malloc function.

A pointer is normally declared to be of a specific type depending on what it points to, such as a pointer to a char. The object may be any C data type such as integer, character, string, or structure. However, nothing inherent in a pointer indicates what type of data the pointer is referencing. A pointer only contains an address.

Faster and more efficient code can be written because pointers are closer to the hardware. That is, the compiler can more easily translate the operation into machine code. There is not as much overhead associated with pointers as might be present with other operators.

Many data structures are more easily implemented using pointers. For example, a linked list could be supported using either arrays or pointers. However, pointers are easier to use and map directly to a next or previous link. An array implementation requires array indexes that are not as intuitive or as flexible as pointers.

Dynamic memory allocation is effected in C through the use of pointers. The malloc and free functions are used to allocate and release dynamic memory, respectively. Dy‐ namic memory allocation enables variable-sized arrays and data structures, such as linked lists and queues. However, in the new C standard, C11, variable size arrays are supported.

Pointers represent a powerful tool to create and enhance applications. On the downside, many problems can occur when using pointers, such as:

• Accessing arrays and other data structures beyond their bounds

• Referencing automatic variables after they have gone out of existence

• Referencing heap allocated memory after it has been released

• Dereferencing a pointer before memory has been allocated to it
The NULL macro is a constant integer zero cast to a pointer to void. In many libraries, it is defined as follows:
#define NULL ((void *)0)

This is what we typically think of as a null pointer.

The actual internal representation of null is implementation-defined. The use of NULL and 0 are language-level symbols that represent a null pointer.

The ASCII NUL is defined as a byte containing all zeros. However, this is not the same as a null pointer.

A C-style string is represented as a sequence of characters terminated by a zero value.

The null string is an empty string and does not contain any characters.

Finally, the null statement consists of a statement with a single semicolon. As we will see, a null pointer is a very useful feature for many data structure implementations, such as a linked list where it is often used to mark the end of the list.

If the intent was to assign the null value to pi, we use the NULL type as follows:

 pi = NULL;

A null pointer and an uninitialized pointer are different. An uninitialized pointer can contain any value, whereas a pointer containing NULL does not reference any location in memory.

Interestingly, we can assign a zero to a pointer, but we cannot assign any other integer value.
A pointer can be used as the sole operand of a logical expression. For example, we can test to see whether the pointer is set to NULL using the following sequence:

if(pi) { // Not NULL, do Work; }

else { // Is NULL, exit;  }

A null pointer should never be dereferenced because it does not contain a valid address. When executed it will result in the program terminating.

NULL should not be used in contexts other than pointers. It might work some of the time, but it is not intended to be used this way.

It can definitely be a problem when used in place of the ASCII NUL character.

This character is not defined in any standard C header file.

It is equivalent to the character literal, ‘\0’, which evaluates to the decimal value zero.

Something to keep in mind (and keep sane):

int num;

int *pi = 0; // Zero refers to the null pointer, NULL

pi = #

*pi = 0; // Zero refers to the integer zero

We are accustomed to overloaded operators, such as the asterisk used to declare a pointer, to dereference a pointer, or to multiply.
The zero is also overloaded.

We may find this discomforting because we are not used to overloading operands.

Pointer to void

A pointer to void is a general-purpose pointer used to hold references to any data type.

An example of a pointer to void is shown below:

void *pv;

It has two interesting properties:

• A pointer to void will have the same representation and memory alignment as a pointer to char.

A pointer to void will never be equal to another pointer.

However, two void pointers assigned a NULL value will always be equal, because there are only one nullpointer which is shared throughout a program.

Any pointer can be assigned to a pointer to void.
It can then be cast back to its original pointer type.
When this happens the value will be equal to the original pointer value.
This is illustrated in the following sequence, where a pointer to int is assigned to a pointer to void and then back to a pointer to int:

int num;

int *pi = #

printf("Value of pi: %p\n", pi);

void* pv = pi;

pi = (int*) pv;

printf("Value of pi: %p\n", pi);

When this sequence is executed as shown below, the pointer address is the same:

Value of pi: 100

Value of pi: 100

Pointers to void are used for data pointers, not function pointers.

That’s it for now.

Until next time, Happy Hacking!

Dynamic Parallelism in CUDA Version 5!

After using all my spare time on Blender lately, I’m now going to digress into another realm.

After reading a TechBrief at the Nvidia Cuda Developer Society I had to wrap my head around something other than modeling, just for a little while! 🙂

In CUDA Version 5, you can now call a CUDA kernel from within another, without going via the CPU.
The “parent” kernel will launch a “child” grid, which can itself also create new work to form an execution hierarchy. The “parent” will only signal as completed once all children are done processing.
The recursive depth will be dependent on your GPU resources.

So, Dynamic Parallelism in CUDA 5 enables the use of a CUDA kernel to create (and sync) nested work via the device runtime API for triggering other kernels, perform memory management on the device and create streams and events all without needing to use a single line of CPU code!
A CUDA Kernel can also call GPU Libraries such as CUBLAS directly, without any CPU intervention.

The Device Runtime API in CUDA C/C++ is a subset of the CUDA Runtime API for the Host, keeping the same syntax for easy code reuse.

Here is an example of calling a kernel from within a kernel:

__global__ KernelChild(void* data){
 //Do something
__global__ KernelParent(void *data){
 if (threadIdx.x == 0) {
 KernelChild<<<1, 32>>>(data);
 //Do something
// On Host
KernelParent<<<8, 32>>>(data);

Reducing the traffic between the GPU and CPU on the PCI bridge will bring a key performance boost for things like fluid dynamics simulations or similar stuff requiring pre-processing passes over the data.

GPU Computing rocks!

Happy Summer Holidays!