ALL Blog C++ CODE CRAFT Debugging Game Development

Top 20 C pointer mistakes and how to fix them

After I graduated school with a BS in Electrical Engineering, I assumed that was the final time I was going to program in “C”. I couldn’t have been extra flawed. All through numerous factors in my profession, I’ve encountered and wrangled with an honest amount of “C” code both due to legacy or portability reasons.

Pointers are probably the most difficult and elementary part of the C Programming language. A lot of the mistakes I’ve made in class assignments and production code is in dealing with pointers. So here is my try to catalog a few of the widespread and not so widespread mistakes – something I ca refer back to the subsequent time I’ve to write production code in C. Hope it helps you as nicely.

Think about the next declaration:

int* p1, p2;

It declares an integer pointer p1 and an integer p2. Most of the time, the intent is to declare two integer pointers.

In the check code under, the last line will end in a compile error “Error C2440 ‘=’: cannot convert from ‘int *’ to ‘int’ ”

int essential()

int* p1, p2;

int n = 30;

p1 = &n;

p2 = &n; // error

This can be a pretty primary mistake that the majority trendy compilers will catch.

Advisable Fix:

Use the next declaration to declare two pointers of the same sort:

int *p1, *p2;

Alternatively, use a typedef – for example,

typedef int* Pint;

and then, use this sort when declaraing pointers:

Pint p1, p2; // yay – no funny * enterprise !

The usage of an uninitialized pointer sometimes leads to program crashes if the pointer accesses reminiscence it isn’t allowed to.

Think about the code under:

int fundamental()

int* p1; // p1 can level to any location in memory

int n = *p1; // Error on debug builds

printf(“%d”, n); // entry violation on release builds
return zero;

On debug builds in Visual Studio, you’ll first get the next error:

Run-Time Verify Failure #3 – The variable ‘p1’ is being used with out being initialized.

followed by:

“Exception thrown: learn entry violation.

p1 was 0xCCCCCCCC.”

0xcc is microsoft’s debug mode marker for uninitialized stack memory.

On launch builds, you’ll encounter a runtime crash on the road :printf(“%d”, n);

“Unhandled exception thrown: read access violation. p1 was nullptr.”

Beneficial Fix:
All the time initialize pointers to a legitimate value.

int essential()

int* p1; // p1 can point to any location in reminiscence

int m = 10;
p1 = &m; // initialize pointer with a legitimate value

int n = *p1; // No error on Debug

printf(“%d”, n); // no access violation on release builds
return 0;

That is more harmful, IMHO, than an uninitialized pointer.In this case, in contrast to an uninitialized pointer, you gained’t get a crash. As an alternative it will possibly lead to critical logic errors in your code.

Think about the code under:

int fundamental()

int* p1; // p1 can level to any location in reminiscence

int m;
p1 = &m; // initialize pointer with an uninitialized variable

int n = *p1;

printf(“%d”, n); // large adverse quantity in debug and zero in release on VC++
return zero;

On debug builds, it’ll end in a big adverse number like “-858993460”. In VC++, the end result might be zero however that isn’t assured by the C commonplace.  Extra specifically item 1652 in the referenced doc states that If an object that has automated storage period isn’t initialized explicitly, its worth is indeterminate.

Really helpful Fix:

Deceptively simple – do not assign pointers to uninitialized variables.

Another one of many novice errors the place the IDE/compiler will probably bail you out. Contemplate the code:

int most important()

int* p1; // p1 can point to any location in memory

int m = 100;
p1 = m; // error

return 0;

The problem is that p1 can include an tackle of an int and not the int value itself. You’ll get a compiler error:

“Error C2440 ‘=’: cannot convert from ‘int’ to ‘int *’ ”

Advisable Fix:

Assign the tackle of the integer variable to the pointer .

int primary()

int* p1; // p1 can point to any location in memory

int m = 100;
p1 = &m; // assign handle of m to p1

return 0;

If the intent is to increment a variable pointed to by a pointer, the following code fails to obtain that.

int primary()

int* p1; // create a pointer to an integer
int m = 100;
p1 = &m; // assign handle of m to p1

*p1++; // ERROR: we did not increment value of m

printf(“%dn”, *p1);
printf(“%dn”, m);

return zero;

Actually, p1 now points to an undefined reminiscence location. Once you run this code, you get the following output with the primary line corresponding to the value at the handle p1 points to.

-858993460
100

Really helpful Fix:
To increment a dereferenced pointer, use :
(*p1)++;

Think about the code under where variable m is allotted on the stack.

int fundamental()

int* p1; // create a pointer to an integer
int m = 100;
p1 = &m;

free(p1);//error – making an attempt to free stack reminiscence using free()

return 0;

Trying to free memory on the stack using the free() perform throws an access violation.

“Unhandled exception at 0x0F7BFC79 (ucrtbased.dll) in CPointerMistakes.exe: 0xC0000005: Access violation reading location 0x47D2C000.”

Reminiscence on the stack(non-pointer variables) is completed implicitly by the system. It’s illegal to get reminiscence from the stack and return it to the heap.

Advisable Fix:
Use free() to deallocate reminiscence that has been previously allocated by malloc() or one in every of its variants. All the time keep in mind the place the reminiscence came from – stack or heap 🙂

Think about the following code – we allocate an integre pointer, use it , free the memory related to the pointer and then attempt to use the pointer once more. This’ll finish in undefined conduct – perhaps crashes depending on the state of the system/platform.

int fundamental()

int* p1;

if ((p1 = (int*)malloc(sizeof(int))) == NULL)

return 1;

*p1 = 99;
free(p1);

*p1 = 100; // BAD – undefined conduct

return zero;

Fix:

Never use a pointer after it has been freed. A superb follow is to set the pointer to NULL after it has been freed such that any try to use it once more is caught by an entry violation.A crash during improvement is best than undefined conduct after release 🙂

free(p1);
p1 = NULL;

Calling free() on a block of memory twice will lead to heap corruption. For example, the next code leads to an unhandled exception indicating heap corruption using MS VC++:

int principal()

char* str1 = (char*)malloc(strlen(“Thunderbird”) + 1);
strcpy_s(str1, strlen(“Thunderbird”) + 1, “Thunderbird”);

//…
free(str1); // first free
//…
free(str1); // double free

OUTPUT:

Unhandled exception at 0x77959D71 (ntdll.dll) in CPointerMistakes.exe: 0xC0000374: A heap has been corrupted (parameters: 0x7798D8D0).

This kind of situation prompted a security vulnerability in zlib which you’ll be able to read about here.

Beneficial Fix:

Don’t free the same block of memory twice! Merely assign NULL to a pointer after it has been freed. Subsequent makes an attempt to free a null pointer might be ignored by most heap managers.

char* str1 = (char*)malloc(strlen(“Thunderbird”) + 1);
strcpy_s(str1, strlen(“Thunderbird”) + 1, “Thunderbird”);

//…
free(str1); // first free
str1 = NULL;

For those who’re implementing one thing in C on this day and age, almost definitely you’re doing it with platform portability in thoughts. The dimensions of knowledge varieties can range throughout totally different platform architectures. For those who write one thing like malloc(2), you may need hassle porting it throughout platforms.

Beneficial Fix:
All the time use sizeof(sort) with malloc – for example:

malloc(sizeof(int))

In the code under, sizeof(arr) will appropriately decide the dimensions of the char array however a pointer to the array gained’t. The kind of *cp is const char, which may solely have a measurement of 1, whereas the kind of arr is totally different: array of const char.

int most important()

const char arr[] = “hello”;
const char *cp = arr;

printf(“Size of arr %lun”, (int)sizeof(arr));
printf(“Size of *cp %lun”, (int)sizeof(*cp));

return zero;

Really helpful Fix:
Never use sizeof on a pointer to an array to determine the dimensions of the array.

You want a pointer to a memory location to free / deallocate that memory. When you re-assign a pointer and there’s no other pointer pointing to that memory block, you can’t deallocate that previous reminiscence block. This causes a reminiscence leak.

Contemplate the code under:

int foremost()

int* p = (int*)malloc(sizeof(int)); // Let’s call this memory block 1
*p = 5;

p = (int*)malloc(sizeof(int)); // Now you haven’t any approach to delete reminiscence block 1 !!!

return 0;

“Memory block 1” isn’t inaccessible because we don’t have a pointer to it. With out having a pointer to a memory block, we can’t call free() on a block and we’ve created a garbage object in that block – in different phrases, we leaked reminiscence.

Really helpful Fix:

Generally, it’s not a good suggestion to recycle pointer variables. Use new pointer variables the place potential and keep in mind to set a pointer variable to NULL proper after it has been freed.

Given two pointers p and q, the task p = q doesn’t copy the block of memory pointed to by q right into a block of memory pointed to by p; as an alternative it assigns reminiscence addresses ( so that both p and q level to the identical memory location; changing the worth of that reminiscence location affects each pointers).

Contemplate the code under:

#embrace “stdafx.h”
#embrace
#embrace
#embrace
#embrace

typedef struct
char *mannequin;
int capacity;
Aircraft;

int most important()

Aircraft af1;
Plane af2;
Plane af3;

// Initialize af1
af1.mannequin = (char*)malloc(strlen(“Thunderbird”) + 1);
strcpy(af1.mannequin, “Thunderbird”);
af1.capacity = 320;

// Shallow copy, af2.modelNum factors to the same int as af1.modelNum
af2 = af1;

// Modifying af2 will affect af1
printf(“%sn”, af1.model); // prints ThunderBird
strcpy(af2.model, “BlackHawk”);
printf(“%sn”, af1.model); // prints BlackHawk – when ThunderBird is predicted

// Deep Copy: If the intent is to get a replica of af1, use a deep copy – which principally
// means a member-wise cloning of values
af3.mannequin = (char*)malloc(strlen(“Thunderbird”) + 1);
strcpy(af3.model, af1.mannequin);
af3.capacity = af1.capability;

// Let’s run the same check:
strcpy(af1.model, “Thunderbird”);
printf(“%sn”, af1.mannequin); // prints ThunderBird

strcpy(af3.model, “BlackHawk”);
printf(“%sn”, af1.model); // prints ThunderBird as anticipated

//cleanup the heap allocated strings
free(af1.mannequin);
free(af3.model);

return 0;

OUTPUT:

Thunderbird
BlackHawk
Thunderbird
Thunderbird

So what simply occurred?

In the shallow copy case, af1 and af2 each points to the same reminiscence location. Any change to the reminiscence location by way of af2 is mirrored when af1 is used.

Within the deep copy case, once we modify af3 (which points to a completely totally different memory block than af1), the reminiscence block pointed by af1 isn’t affected.

Within the code under,. str1 and str2 points to the same memory block – so when str1 is freed, primarily the reminiscence block pointed to by str2 is freed. Any attempt to use str2 after str1 has been freed will trigger undefined conduct. Within the case of this system under – it’ll print some rubbish value.

int important()

char* str1 = (char*)malloc(strlen(“Thunderbird”) + 1);
strcpy(str1, “Thunderbird”);

char* str2 = str1;
printf(“%sn”, str1);

// … many strains of code
free(str1);

// .. many strains of code

printf(“%sn”, str2); // ERROR: reminiscence pointed to by q has been freed by way of p – you’ve undefined conduct

return zero;

OUTPUT:

Thunderbird
αf╓ // some garbage worth

There’s actually no great way round this in C besides to use static analyzers. In case you’re in C++, you should use shared_pointers – but use warning as suggested within the linked article. . There’s additionally an excellent discussion on Stackoverflow on this matter.

If in case you have allotted a block of n objects, do not attempt to access objects past this block ( which includes any objects in places p+n and past)

Contemplate the code under:

int primary()

const int SIZE = 10;
double *doubleVals;

if ((doubleVals = (double*)malloc(sizeof(double)*SIZE)) == NULL)

exit(EXIT_FAILURE);

doubleVals[SIZE – 1] = 20.21;
printf(“%lfn”, doubleVals[SIZE – 1]);

doubleVals[SIZE] = 25.99; // Error – we’ve only allocated blocks by way of SIZE-1 – you’re writing over memory you do not own
printf(“%lfn”, doubleVals[SIZE]);

return 0;

The statement doubleVals[SIZE] = 25.99 is actually writing over reminiscence it does not own – which may cause undefined conduct in packages.

Beneficial Fix:

All the time concentrate on the bounds of reminiscence allocated by your code and operate inside those protected limits.

Given a block of memory of SIZE objects pointed to by p, the final object within the block may be retrieved through the use of one other pointer q and setting it to (p+SIZE-1) as an alternative of (p+SIZE).

Contemplate the code under:

int essential()

const int SIZE = 10;
double *p;

if ((p = (double*)malloc(sizeof(double)*SIZE)) == NULL)

exit(EXIT_FAILURE);

for (int i = 0; i < SIZE; i++)

*(p + i) = i;

double *q = p;

//Incorrectly Access the last aspect
double lastVal = *(q + SIZE); // Error – the last aspect is at (q + SIZE – 1)
printf("%lfn", lastVal);

// Appropriately entry the final aspect
lastVal = *(q + SIZE – 1);
printf("%lfn", lastVal);

return zero;

The first print assertion incorrectly prints “0” whereas the final component is “9”. The second print statement fixes it by accessing the final component at (q + SIZE – 1)

Advisable Fix:

Rigorously apply the “off by one error” rules that you simply learnt for array access to pointers.

All the time use the appropriate pointer sort for the info. Think about the code under the place a pointer to an integer is assigned to a short:

int principal()

int num = 2147483647;
int *pi = &num;
brief *ps = (brief*)pi;
printf(“pi: %p Value(16): %x Value(10): %dn”, pi, *pi, *pi);
printf(“ps: %p Value(16): %hx Value(10): %hdn”, ps, (unsigned brief)*ps, (unsigned brief)*ps);

OUTPUT:

pi: 00DFFC44 Worth(16): 7fffffff Worth(10): 2147483647
ps: 00DFFC44 Worth(16): ffff Worth(10): -1

Discover that it seems that the first hexadecimal digit saved at tackle 100 is 7 or f, depending on whether or not it is displayed as an integer or as a short. This apparent contradiction is an artifact of executing this sequence on somewhat endian machine.If we deal with this as a short number and solely use the first two bytes, then we get the brief worth of –1. If we treat this as an integer and use all four bytes, then we get 2,147,483,647.

Beneficial Fix:

All the time use the right pointer sort for a selected knowledge sort – int* for int , double* for double and so on.

Typically we would like to examine if the contents of two objects are similar – for instance verify if two strings are equal.

Within the code under, clearly the intent was to examine if both strings are “Thunderbird”. However, we ended up comparing the reminiscence addresses with the assertion “str1 == str2”. Here str1 and str2 are primarily pointers to totally different reminiscence addresses which holds the identical string.

int major()

char* str1 = (char*)malloc(strlen(“Thunderbird”) + 1);
strcpy(str1, “Thunderbird”);

char* str2 = (char*)malloc(strlen(“Thunderbird”) + 1);
strcpy(str2, “Thunderbird”);

if (str1 == str2)

printf(“Two strings are equaln”);

else

printf(“Two strings are NOT equaln”);

The code might be made to work as meant, i.e., examine string contents by making the following modifications:

if (strcmp(str1,str2) == 0) // Are the contents of the strings the identical

printf(“Two strings are equaln”);

Beneficial Fix:

All the time keep in mind to examine the contents of the memory location pointed to by pointers as an alternative of evaluating the tackle of pointer themselves.

While C pointers and Arrays can be utilized interchangeably in most situations, they don’t seem to be fairly the same. Here’s an instance of the place it is a recipe for entry violation.

// File1.cpp

int global_array[10];

// File2.cpp

extern int *global_array;

int essential()

for (int i = 0; i < 10; i++)

global_array[i] = i; // Access Violation

return zero;

In File2.cpp, global_array is said as an pointer however outlined as an array in File1.cpp. At a high degree, the compile generates totally different code for array indexing and entry by way of pointer.

Beneficial Fix:

Change the declaration so it does match the definition, like:

// File1.cpp

int global_array[10];

// File2.cpp

extern int global_array[];

int fundamental()

for (int i = zero; i < 10; i++)

global_array[i] = i; // NO Entry Violation

return zero;

Notice: An in depth dialogue is beyond the scope of this article. The perfect rationalization of this concern I found was in the section, “Chapter 4. The Shocking Truth: C Arrays and Pointers Are NOT the Same!” in Deep C Secrets. It’s a unbelievable e-book when you actually need to develop into an skilled C programmer – extremely beneficial.

When an software terminates, most operating methods don’t zero out or erase the heap memory that was in use by your software. The reminiscence blocks used by your software may be allotted to another program, which may use the contents of non-zeroed out memory blocks. Simply think about you requested for a security query from the consumer and stored it in heap reminiscence – it’s all the time a good idea to erase that memory block contents before returning the reminiscence to the Operating System by way of free().

int essential()

char* userSecurityQuestion = (char*)malloc(strlen(“First Pet?”) + 1);
strcpy_s(userSecurityQuestion, strlen(“First Pet?”) + 1, “First Pet?”);

//…
// Accomplished with processing safety query – saved in secured db and so on.

// Now set this system memory to zero earlier than returning memory back to OS
memset(userSecurityQuestion, zero, sizeof(userSecurityQuestion));
free(userSecurityQuestion);

Features pointers are used extensively in lots of giant scale production system. It’s additionally important to understand extra superior concepts like callbacks, events in Win32 or lambdas in normal C++.

Right here’s an example of perform pointer in linux kernel:

struct net_device_ops
int (*ndo_init)(struct net_device *dev);
void (*ndo_uninit)(struct net_device *dev);
int (*ndo_open)(struct net_device *dev);
int (*ndo_stop)(struct net_device *dev);
netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,
struct net_device *dev);

If code like this makes your head swivel, no sweat – mine did too when i began my career. 🙂

The problem is that the majority school degree C programs seldom does any deep exploration of perform pointers, whereas when you’re in business, it’s far and wide. Here is an effective ebook that has an in-depth remedy of C perform pointers :Understanding and Utilizing C Pointers.

C is among the oldest languages in use as we speak. Pointers types the guts and soul of C. Pointers will not be solely useful for writing production quality code but in addition in class for understanding the concepts behind self referential knowledge buildings like linked listing and binary timber. Even in case you are working in a high degree language like Java or C#, an object is actually a pointer. So, research pointers properly as a result of they hold displaying up in coding interviews and tech screens – I wouldn’t be stunned should you get a question comparable to the code snippets in this article and requested “what’s wrong with this piece of C code?”.

Good luck !