12.2: Pointer Data Type
- Page ID
- 29104
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)Pointers in C/C++ with Examples
Pointers are symbolic representation of addresses. They enable programs to simulate call-by-reference as well as to create and manipulate dynamic data structures. It’s general declaration in C/C++ has the format:
Syntax:
datatype *var_name; So, an integer point would be defined as: int *ptr; //ptr can point to an address which holds int data
How to use a pointer?
- Define a variable, myVar, and assign it the value of 99.
- Define a pointer variable - the variable newPtr is defined as type pointer with the asterisk - *newPtr.
- Assigning the address of myVar to the newPtr using the address operator (&) which returns the address of that variable - the variable myVar has a value of 99, and it is stored at memory address 7FC0 (this memory location varies each time the code is run). So we assign this address value to newPtr, and when we use the asterisk, *newPtr it is referring to an address - which now contains the value of 66.
- Accessing the value stored in the address using unary operator (*) which returns the value of the variable located at the address specified by its operand. The same concept applies to a pointer to a pointer - again, it performs address arithmetic to get the new value, 33, into the proper memory location.
The reason we associate data type to a pointer is that it knows how many bytes the data is stored in. When we increment a pointer, we increase the pointer by the size of data type to which it points. This is an important concept and we will come back to it later.
Let's take a look at some pointer code
// C++ program to illustrate Pointers in C++ #include <bits/stdc++.h> using namespace std; int main() { int myVar = 20; //declare pointer variable int *myPtr; //note that data type of ptr and var must be same *myPtr = &myVar; // assign the address of a variable to a pointer cout << "Value at myPtr = " << myPtr << endl; cout << "Value at myVar = " << myVar << endl; cout << "Value at *myPtr = " << *myPtr << endl; return 0; }
Our first cout outputs the value in the variable myPtr, which is 0x7ffcb9e9ea4c - the address assigned to the variable myVar.
The next cout simply outputs the value stored in the variable myVar.
The last cout, because it uses the asterisk, prints out the value at the address 0x7ffcb9e9ea4c. The asterisk says, "Look at the value that is found at the address contained in this variable.
Here is the output from this code (the address value will change if you run this code because it will probably occupy a different location)
Value at myPtr = 0x7ffcb9e9ea4c Value at myVar = 20 Value at *myPtr = 20
References and Pointers
There are 3 ways to pass C++ arguments to a function:
- call-by-value
- call-by-reference with pointer argument
- call-by-reference with reference argument
// C++ program to illustrate call-by-methods in C++ #include <bits/stdc++.h> using namespace std; //Pass-by-Value int square1(int n) { //Address of n in square1() is not the same as n1 in main() cout << "address of n1 in square1(): " << &n << "\n"; // clone modified inside the function n *= n; return n; } //Pass-by-Reference with Pointer Arguments void square2(int *n) { //Address of n in square2() is the same as n2 in main() cout << "address of n2 in square2(): " << n << "\n"; // Explicit de-referencing to get the value pointed-to // Says: the value pointed to equals the value pointed to times the value pointed to // *n = *n * *n *n *= *n; } //Pass-by-Reference with Reference Arguments void square3(int &n) { //Address of n in square3() is the same as n3 in main() cout << "address of n3 in square3(): " << &n << "\n"; // Implicit de-referencing (without '*') n *= n; } int main() { // Call-by-Value int n1=8; cout << "address of n1 in main(): " << &n1 << "\n"; // Notice how we call the function from within the cout statement cout << "Square of n1: " << square1(n1) << "\n"; cout << "No change in n1: " << n1 << "\n"; //Call-by-Reference with Pointer Arguments int n2=8; cout << "address of n2 in main(): " << &n2 << "\n"; square2(&n2); cout << "Square of n2: " << n2 << "\n"; cout << "Change reflected in n2: " << n2 << "\n"; //Call-by-Reference with Reference Arguments int n3=8; cout << "address of n3 in main(): " << &n3 << "\n"; square3(n3); cout << "Square of n3: " << n3 << "\n"; cout << "Change reflected in n3: " << n3 << "\n"; return 0; }
Call by value:
- We pass a copy of the value, 8
- Notice that the square1() function is called from WITHIN the cout statement
- The function squares that value AND returns that squared value
- Remember, when a function ends - or returns - it goes back to where it was called from. When it returns a value, then that value basically replaces the original functino call in the code.
Call-by-Reference with Pointer Arguments
- Calling the square2() function, we pass an address, &n2
- The function square2(), hen declares it as *n - which points to the address of n2
- Notice - there is NOT a return statement here...because we inserted a new value at the address that was passed from main()
Call-by-Reference with Reference Arguments
- We call square3() passing an argument, BUT, in square3() it is declared as &n
- When we use n *= n - it is understood that we are dealing with the address, because that is how it was declared in the function signature
Output:
Call-by-Value address of n1 in main(): 0x7ffcdb2b4a44 address of n1 in square1(): 0x7ffcdb2b4a2c Square of n1: 64 No change in n1: 8 Call-by-Reference with Pointer Arguments address of n2 in main(): 0x7ffcdb2b4a48 address of n2 in square2(): 0x7ffcdb2b4a48 Square of n2: 64 Change reflected in n2: 64 Call-by-Reference with Reference Arguments address of n3 in main(): 0x7ffcdb2b4a4c address of n3 in square3(): 0x7ffcdb2b4a4c Square of n3: 64 Change reflected in n3: 64
In C++, by default arguments are passed by value and the changes made in the called function will not reflect in the passed variable. The changes are made into a clone made by the called function.
If wish to modify the original copy directly (especially in passing huge object or array) and/or avoid the overhead of cloning, we use pass-by-reference. Pass-by-Reference with Reference Arguments does not require any clumsy syntax for referencing and dereferencing.
Adapted from:
"Pointers in C/C++ with Examples" by Abhirav Kariya, Geeks for Geeks is licensed under CC BY-SA 4.0