---
title: "Raw pointers (C++)"
description: "How to use raw pointers in C++"
ms.date: 02/03/2022
helpviewer_keywords: ["pointers [C++]"]
no-loc: [void, nullptr, const, char, new, delete]
---
# Raw pointers (C++)
A *pointer* is a type of variable. It stores the address of an object in memory, and is used to access that object. A *raw pointer* is a pointer whose lifetime isn't controlled by an encapsulating object, such as a [smart pointer](smart-pointers-modern-cpp.md). A raw pointer can be assigned the address of another non-pointer variable, or it can be assigned a value of [`nullptr`](nullptr.md). A pointer that hasn't been assigned a value contains random data.
A pointer can also be *dereferenced* to retrieve the value of the object that it points at. The *member access operator* provides access to an object's members.
```cpp
int* p = nullptr; // declare pointer and initialize it
// so that it doesn't store a random address
int i = 5;
p = &i; // assign pointer to address of object
int j = *p; // dereference p to retrieve the value at its address
```
A pointer can point to a typed object or to **`void`**. When a program allocates an object on the [heap](https://wikipedia.org/wiki/Heap) in memory, it receives the address of that object in the form of a pointer. Such pointers are called *owning pointers*. An owning pointer (or a copy of it) must be used to explicitly free the heap-allocated object when it's no longer needed. Failure to free the memory results in a *memory leak*, and renders that memory location unavailable to any other program on the machine. Memory allocated using **`new`** must be freed by using **`delete`** (or **`delete[]`**). For more information, see [`new` and `delete` operators](new-and-delete-operators.md).
```cpp
MyClass* mc = new MyClass(); // allocate object on the heap
mc->print(); // access class member
delete mc; // delete object (please don't forget!)
```
A pointer (if it isn't declared as **`const`**) can be incremented or decremented to point at another location in memory. This operation is called *pointer arithmetic*. It's used in C-style programming to iterate over elements in arrays or other data structures. A **`const`** pointer can't be made to point to a different memory location, and in that sense is similar to a [reference](references-cpp.md). For more information, see [`const` and `volatile` pointers](const-and-volatile-pointers.md).
```cpp
// declare a C-style string. Compiler adds terminating '\0'.
const char* str = "Hello world";
const int c = 1;
const int* pconst = &c; // declare a non-const pointer to const int
const int c2 = 2;
pconst = &c2; // OK pconst itself isn't const
const int* const pconst2 = &c;
// pconst2 = &c2; // Error! pconst2 is const.
```
On 64-bit operating systems, a pointer has a size of 64 bits. A system's pointer size determines how much addressable memory it can have. All copies of a pointer point to the same memory location. Pointers (along with references) are used extensively in C++ to pass larger objects to and from functions. It's often more efficient to copy an object's address than to copy the entire object. When defining a function, specify pointer parameters as **`const`** unless you intend the function to modify the object. In general, **`const`** references are the preferred way to pass objects to functions unless the value of the object can possibly be **`nullptr`**.
[Pointers to functions](#pointers_to_functions) enable functions to be passed to other functions. They're used for "callbacks" in C-style programming. Modern C++ uses [lambda expressions](lambda-expressions-in-cpp.md) for this purpose.
## Initialization and member access
The following example shows how to declare, initialize, and use a raw pointer. It's initialized using **`new`** to point an object allocated on the heap, which you must explicitly **`delete`**. The example also shows a few of the dangers associated with raw pointers. (Remember, this example is C-style programming and not modern C++!)
```cpp
#include
#include
class MyClass
{
public:
int num;
std::string name;
void print() { std::cout << name << ":" << num << std::endl; }
};
// Accepts a MyClass pointer
void func_A(MyClass* mc)
{
// Modify the object that mc points to.
// All copies of the pointer will point to
// the same modified object.
mc->num = 3;
}
// Accepts a MyClass object
void func_B(MyClass mc)
{
// mc here is a regular object, not a pointer.
// Use the "." operator to access members.
// This statement modifies only the local copy of mc.
mc.num = 21;
std::cout << "Local copy of mc:";
mc.print(); // "Erika, 21"
}
int main()
{
// Use the * operator to declare a pointer type
// Use new to allocate and initialize memory
MyClass* pmc = new MyClass{ 108, "Nick" };
// Prints the memory address. Usually not what you want.
std:: cout << pmc << std::endl;
// Copy the pointed-to object by dereferencing the pointer
// to access the contents of the memory location.
// mc is a separate object, allocated here on the stack
MyClass mc = *pmc;
// Declare a pointer that points to mc using the addressof operator
MyClass* pcopy = &mc;
// Use the -> operator to access the object's public members
pmc->print(); // "Nick, 108"
// Copy the pointer. Now pmc and pmc2 point to same object!
MyClass* pmc2 = pmc;
// Use copied pointer to modify the original object
pmc2->name = "Erika";
pmc->print(); // "Erika, 108"
pmc2->print(); // "Erika, 108"
// Pass the pointer to a function.
func_A(pmc);
pmc->print(); // "Erika, 3"
pmc2->print(); // "Erika, 3"
// Dereference the pointer and pass a copy
// of the pointed-to object to a function
func_B(*pmc);
pmc->print(); // "Erika, 3" (original not modified by function)
delete(pmc); // don't forget to give memory back to operating system!
// delete(pmc2); //crash! memory location was already deleted
}
```
## Pointer arithmetic and arrays
Pointers and arrays are closely related. When an array is passed by-value to a function, it's passed as a pointer to the first element. The following example demonstrates the following important properties of pointers and arrays:
- The **`sizeof`** operator returns the total size in bytes of an array
- To determine the number of elements, divide total bytes by the size of one element
- When an array is passed to a function, it *decays* to a pointer type
- When the **`sizeof`** operator is applied to a pointer, it returns the pointer size, for example, 4 bytes on x86 or 8 bytes on x64
```cpp
#include
void func(int arr[], int length)
{
// returns pointer size. not useful here.
size_t test = sizeof(arr);
for(int i = 0; i < length; ++i)
{
std::cout << arr[i] << " ";
}
}
int main()
{
int i[5]{ 1,2,3,4,5 };
// sizeof(i) = total bytes
int j = sizeof(i) / sizeof(i[0]);
func(i,j);
}
```
Certain arithmetic operations can be used on non-`const` pointers to make them point to another memory location. Pointers are incremented and decremented using the **`++`**, **`+=`**, **`-=`** and **`--`** operators. This technique can be used in arrays and is especially useful in buffers of untyped data. A `void*` gets incremented by the size of a **`char`** (1 byte). A typed pointer gets incremented by size of the type it points to.
The following example demonstrates how pointer arithmetic can be used to access individual pixels in a bitmap on Windows. Note the use of **`new`** and **`delete`**, and the dereference operator.
```cpp
#include
#include
using namespace std;
int main()
{
BITMAPINFOHEADER header;
header.biHeight = 100; // Multiple of 4 for simplicity.
header.biWidth = 100;
header.biBitCount = 24;
header.biPlanes = 1;
header.biCompression = BI_RGB;
header.biSize = sizeof(BITMAPINFOHEADER);
constexpr int bufferSize = 30000;
unsigned char* buffer = new unsigned char[bufferSize];
BITMAPFILEHEADER bf;
bf.bfType = 0x4D42;
bf.bfSize = header.biSize + 14 + bufferSize;
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //54
// Create a gray square with a 2-pixel wide outline.
unsigned char* begin = &buffer[0];
unsigned char* end = &buffer[0] + bufferSize;
unsigned char* p = begin;
constexpr int pixelWidth = 3;
constexpr int borderWidth = 2;
while (p < end)
{
// Is top or bottom edge?
if ((p < begin + header.biWidth * pixelWidth * borderWidth)
|| (p > end - header.biWidth * pixelWidth * borderWidth)
// Is left or right edge?
|| (p - begin) % (header.biWidth * pixelWidth) < (borderWidth * pixelWidth)
|| (p - begin) % (header.biWidth * pixelWidth) > ((header.biWidth - borderWidth) * pixelWidth))
{
*p = 0x0; // Black
}
else
{
*p = 0xC3; // Gray
}
p++; // Increment one byte sizeof(unsigned char).
}
ofstream wf(R"(box.bmp)", ios::out | ios::binary);
wf.write(reinterpret_cast(&bf), sizeof(bf));
wf.write(reinterpret_cast(&header), sizeof(header));
wf.write(reinterpret_cast(begin), bufferSize);
delete[] buffer; // Return memory to the OS.
wf.close();
}
```
## `void*` pointers
A pointer to **`void`** simply points to a raw memory location. Sometimes it's necessary to use `void*` pointers, for example when passing between C++ code and C functions.
When a typed pointer is cast to a `void` pointer, the contents of the memory location are unchanged. However, the type information is lost, so that you can't do increment or decrement operations. A memory location can be cast, for example, from `MyClass*` to `void*` and back again to `MyClass*`. Such operations are inherently error-prone and require great care to avoid errors. Modern C++ discourages the use of `void` pointers in almost all circumstances.
```cpp
//func.c
void func(void* data, int length)
{
char* c = (char*)(data);
// fill in the buffer with data
for (int i = 0; i < length; ++i)
{
*c = 0x41;
++c;
}
}
// main.cpp
#include
extern "C"
{
void func(void* data, int length);
}
class MyClass
{
public:
int num;
std::string name;
void print() { std::cout << name << ":" << num << std::endl; }
};
int main()
{
MyClass* mc = new MyClass{10, "Marian"};
void* p = static_cast(mc);
MyClass* mc2 = static_cast(p);
std::cout << mc2->name << std::endl; // "Marian"
delete(mc);
// use operator new to allocate untyped memory block
void* pvoid = operator new(1000);
char* pchar = static_cast(pvoid);
for(char* c = pchar; c < pchar + 1000; ++c)
{
*c = 0x00;
}
func(pvoid, 1000);
char ch = static_cast(pvoid)[0];
std::cout << ch << std::endl; // 'A'
operator delete(pvoid);
}
```
## Pointers to functions
In C-style programming, function pointers are used primarily to pass functions to other functions. This technique allows the caller to customize the behavior of a function without modifying it. In modern C++, [lambda expressions](lambda-expressions-in-cpp.md) provide the same capability with greater type safety and other advantages.
A function pointer declaration specifies the signature that the pointed-to function must have:
```cpp
// Declare pointer to any function that...
// ...accepts a string and returns a string
string (*g)(string a);
// has no return value and no parameters
void (*x)();
// ...returns an int and takes three parameters
// of the specified types
int (*i)(int i, string s, double d);
```
The following example shows a function `combine` that takes as a parameter any function that accepts a `std::string` and returns a `std::string`. Depending on the function that's passed to `combine`, it either prepends or appends a string.
```cpp
#include
#include
using namespace std;
string base {"hello world"};
string append(string s)
{
return base.append(" ").append(s);
}
string prepend(string s)
{
return s.append(" ").append(base);
}
string combine(string s, string(*g)(string a))
{
return (*g)(s);
}
int main()
{
cout << combine("from MSVC", append) << "\n";
cout << combine("Good morning and", prepend) << "\n";
}
```
## See also
[Smart pointers](smart-pointers-modern-cpp.md)
[Indirection Operator: *](indirection-operator-star.md)
[Address-of Operator: &](address-of-operator-amp.md)
[Welcome back to C++](welcome-back-to-cpp-modern-cpp.md)