mirror of
https://github.com/cs341-illinois/coursebook.git
synced 2026-10-02 08:04:38 +08:00
Rewrites about 70 unclear sentences across every chapter except deadlock; corrects the Mars Pathfinder, AT&T 1990 and Appnexus post-mortems against their sources; fixes the sig_atomic_t range claim; applies style fixes (list punctuation, "process's", lowercase "convoy effect", spaces before "?"); labels every post-mortem and points Meltdown/Spectre at the security chapter; points the Ed link at the course home page; restyles the AUTHORS.md listing as plain text while keeping it readable by pandoc; escapes a stray 50%; and uses intptr_t casts in the threads example. Removes the resolved and stale entries from future-concerns-for-review.md, which now lists only items that need an author's decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
151 lines
5.5 KiB
TeX
151 lines
5.5 KiB
TeX
\section{Pointers}
|
|
|
|
Pointers are variables that hold addresses.
|
|
These addresses have a numeric value, but usually, programmers are interested in the value of the contents at that memory address.
|
|
In this section, we will try to take you through a basic introduction to pointers.
|
|
|
|
\subsection{Pointer Basics}
|
|
|
|
\subsubsection{Declaring a Pointer}
|
|
|
|
A pointer refers to a memory address. The type of the pointer is useful -- it tells the compiler how many bytes need to be read/written and delineates the semantics for pointer arithmetic (addition and subtraction).
|
|
|
|
\begin{lstlisting}[language=C]
|
|
int *ptr1;
|
|
char *ptr2;
|
|
\end{lstlisting}
|
|
|
|
Due to C's syntax, an \keyword{int*} or any pointer is not actually its own type.
|
|
You have to precede each pointer variable with an asterisk.
|
|
As a common gotcha, the following
|
|
|
|
\begin{lstlisting}[language=C]
|
|
int* ptr3, ptr4;
|
|
\end{lstlisting}
|
|
|
|
Will only declare \keyword{*ptr3} as a pointer.
|
|
\keyword{ptr4} will actually be a regular int variable.
|
|
To fix this declaration, ensure the \keyword{*} precedes the pointer.
|
|
|
|
\begin{lstlisting}[language=C]
|
|
int *ptr3, *ptr4;
|
|
\end{lstlisting}
|
|
|
|
Keep this in mind for structs as well.
|
|
If one declares without a typedef, then the pointer goes after the type.
|
|
|
|
\begin{lstlisting}[language=C]
|
|
struct person *ptr3;
|
|
\end{lstlisting}
|
|
|
|
\subsubsection{Reading / Writing with pointers}
|
|
|
|
Let's say that \keyword{int\ *ptr} was declared.
|
|
For the sake of discussion, let us assume that \keyword{ptr} contains the memory address \keyword{0x1000}.
|
|
To write to the pointer, it must be dereferenced and assigned a value.
|
|
|
|
\begin{lstlisting}[language=C]
|
|
*ptr = 0; // Writes some memory.
|
|
\end{lstlisting}
|
|
|
|
What C does is take the type of the pointer which is an \keyword{int} and write \keyword{sizeof(int)} bytes from the start of the pointer, meaning that bytes \keyword{0x1000}, \keyword{0x1001}, \keyword{0x1002}, \keyword{0x1003} will all be zero.
|
|
The number of bytes written depends on the pointer type.
|
|
It is the same for all primitive types but structs are a little different.
|
|
|
|
Reading works roughly the same way, except you put the variable in the spot that it needs the value.
|
|
|
|
\begin{lstlisting}[language=C]
|
|
int doubled = *ptr * 2;
|
|
\end{lstlisting}
|
|
|
|
Reading and writing to non-primitive types gets tricky.
|
|
The compilation unit - usually the file or a header - needs to have the size of the data structure readily available.
|
|
This means that opaque data structures can't be copied.
|
|
Here is an example of assigning a struct pointer:
|
|
|
|
\begin{lstlisting}[language=C]
|
|
#include <stdio.h>
|
|
|
|
typedef struct {
|
|
int a1;
|
|
int a2;
|
|
} pair;
|
|
|
|
int main() {
|
|
pair obj;
|
|
pair zeros;
|
|
zeros.a1 = 0;
|
|
zeros.a2 = 0;
|
|
pair *ptr = &obj;
|
|
obj.a1 = 1;
|
|
obj.a2 = 2;
|
|
*ptr = zeros;
|
|
printf("a1: %d, a2: %d\n", ptr->a1, ptr->a2);
|
|
return 0;
|
|
}
|
|
\end{lstlisting}
|
|
|
|
As for reading structure pointers, don't do it directly.
|
|
Instead, programmers create abstractions for creating, copying, and destroying structs.
|
|
If this sounds familiar, it is what C++ originally intended to do before the standards committee went off the deep end.
|
|
|
|
\subsection{Pointer Arithmetic}
|
|
|
|
Just as you can add an integer to an integer, you can add an integer to a pointer.
|
|
However, the pointer type is used to determine how much to increment the pointer.
|
|
A pointer is moved over by the value added times the size of the underlying type.
|
|
For char pointers, this is trivial because characters are always one byte.
|
|
|
|
\begin{lstlisting}[language=C]
|
|
char *ptr = "Hello"; // ptr holds the memory location of 'H'
|
|
ptr += 2; // ptr now points to the first 'l''
|
|
\end{lstlisting}
|
|
|
|
If an int is 4 bytes then ptr+1 points to 4 bytes after whatever ptr is pointing at.
|
|
|
|
\begin{lstlisting}[language=C]
|
|
char *ptr = "ABCDEFGH";
|
|
int *bna = (int *) ptr;
|
|
bna +=1; // Would cause iterate by one integer space (i.e 4 bytes on some systems)
|
|
ptr = (char *) bna;
|
|
printf("%s", ptr);
|
|
\end{lstlisting}
|
|
|
|
Notice how only 'EFGH' is printed.
|
|
Why is that? Well as mentioned above, when performing 'bna+=1' we are increasing the \textbf{integer} pointer by 1, (translates to 4 bytes on most systems) which is equivalent to 4 characters (each character is only 1 byte).
|
|
Because pointer arithmetic in C is always automatically scaled by the size of the type that is pointed to, the ISO C standard forbids arithmetic on void pointers.
|
|
Having said that, compilers will often treat the underlying type as \keyword{char}.
|
|
Here is a machine translation.
|
|
The following two pointer arithmetic operations are equal.
|
|
|
|
\begin{lstlisting}[language=C]
|
|
int *ptr1 = ...;
|
|
|
|
// 1
|
|
int *offset = ptr1 + 4;
|
|
|
|
// 2
|
|
char *temp_ptr1 = (char*) ptr1;
|
|
int *offset = (int*)(temp_ptr1 + sizeof(int)*4);
|
|
\end{lstlisting}
|
|
|
|
\textbf{Every time you do pointer arithmetic, take a deep breath and make sure that you are shifting over the number of bytes you think you are shifting over.}
|
|
|
|
\subsection{So what is a void pointer?}
|
|
|
|
A void pointer is a pointer without a type.
|
|
Void pointers are used when either the datatype is unknown or when interfacing C code with other programming languages without APIs.
|
|
You can think of this as a raw pointer, or a memory address.
|
|
\keyword{malloc} by default returns a void pointer that can be safely promoted to any other type.
|
|
|
|
\begin{lstlisting}[language=C]
|
|
void *give_me_space = malloc(10);
|
|
char *string = give_me_space;
|
|
\end{lstlisting}
|
|
|
|
C automatically promotes \keyword{void*} to its appropriate type.
|
|
\keyword{gcc} and \keyword{clang} are not totally ISO C compliant, meaning that they will permit arithmetic on a void pointer.
|
|
They will treat it as a \keyword{char} pointer.
|
|
Do not do this because it is not portable - it is not guaranteed to work with all compilers!
|
|
|