Normally, a variable contains a direct reference to a specific value.
Pointer variables are variables that contain memory addresses as their values.
Pointers store the address of a variable with a particular value, establishing an indirect reference.
*
operatorint *p
, char *d
0
, NULL
, or a valid address.*
(e.g., *p
).Examples:
#include <stdio.h> int main() { int *p = NULL; int x = 42; = &x; p ("%d", *p); // Dereferencing of the pointer printfreturn 0; }
- This prints “
42
”#include <stdio.h> int main() { int *p = NULL; int x = 42; = &x; p += 100; x ("%d", *p); printfreturn 0; }
- This prints
142
#include <stdio.h> int main() { int *p = NULL; int x = 42; = &x; p += 100; x *p = *p + 200; ("%d", *p); printfreturn 0; }
- This prints
342
Addressof Operator: &
Dereference Operator: *
int y = 2;
("%x \n", *y); printf
—will just result in a syntax error.
Note: Most compilers will actually cancel out any
*
and&
operators next to each other, as they are complements, regardless of their order, so*&y
and&*y
would be valid ways to accessy
.
void *
is a pointer to an unspecified data type.
void *p
can point to an unspecified data type.int a = 4;
void *p;
int *c;
= &a;
p = (int *)p; // Here we assign the value of pointer p to the point c, after casting it to be able to do so c
const
and Pointersconst
: Qualifier that indicates that the value of a variable should not be modified.
Pointer arguments can be protected against accidental changed by using the qualifier const
.
Example: Making pointer arguments
const
// Disallow modifying the value at the pointer's address, // Allow changing the pointer's address. void f (const int *p) // POINTER TO CONSTANT DATA { int j; *p = 0; // Not permitted = &j; // Permitted p } // Allow modifying the value at the pointer's address, // Disallow changing the pointer's address. void g (int * const p) // CONSTANT POINTER TO DATA { int j; *p = 0; // Permitted = &j; // Not permitted p } // Disallow modifying the value at the pointer's address, // Disallow changing the pointer's address. void h (const int * const p) // CONSTANT POINTER TO CONSTANT DATA { int j; *p = 0; // Not permitted = &j; // Not permitted p }
Four Ways to Pass a Pointer to a Function:
void f(int *p)
++
moves array pointer to the next element.void f(const int *p)
void f(int * const p)
void f(const int * const p)
Note: In most cases at least one
const
should be used. It’s very rare that you want to modify both the pointer’s value and address.
Reference: A constant pointer.
int a[3];
int b[5];
= &b; // Invalid, because a is a constant pointer! (aka: reference!)
a = a + 1; // Invalid, because a is a constant pointer! a
Array: Defines a contiguous location of memory for its elements.
char a[] = "A string is just an array of chars!"
("%c", a[5]) // Prints "i" printf
When you subscript an array expression (a[5]
), C is actually computing the offset from the address of the first element in the array (each data type takes up a certain amount of memory) and dereferencing the result.
General Formula: addr(ptr + i) = addr(ptr) + sizeof(type) * i
&a[0]
and a
both actually point to the same location in memory: The start of the contiguous block of memory assigned to the array.
#include <stdio.h>
#include <string.h>
int main()
{
char a[] = "A string is just an array of chars!";
int SAME = memcmp(&a[0], a, sizeof(char));
if (SAME == 0)
("&a[0] and a point to the same address!");
printf}
"&a[0] and a point to the same address!"
C places the NULL terminal (\0
) at the end of character arrays (null-terminated strings) so that the end of the string can be known.
#include <stdio.h>
int main()
{
char a[] = "A string is just an array of chars!";
// Method A: Printing each character by using array syntax
int index = 0;
while (a[index] != '\0')
{
("%c", a[index]);
printf++;
index
}
("");
puts// Method B: Printing each character by pointer arithmetic
char *p = a;
while (*p != '\0')
{
("%c", *p);
printf++;
p}
}
Like for awk
, prefix and postfix only changes what is returned by the operator. The operator will still increment or decrement the target variable just fine, and which one you use only matters if you’re using the return value.
++x
: Pre-increment x. Returns x+1.--x
: Pre-decrement x. Returns x-1.x++
: Post-increment x. Returns x.x--
: Post-decrement x. Returns x.“Does it hurt when I do that?”
Expression | Meaning |
---|---|
*aPtr++ or *(aPtr++) or (*aPtr)++ | Post-increment aPtr . Returns *aPtr before increment |
*++aPtr or *(++aPtr) | Pre-increment aPtr . Returns *aPtr after increment |
++*aPtr or ++(*aPtr) | Pre-increment *aPtr . Returns *aPtr after increment |
Note: Unlike C++, C has no smart pointers.
Dangling Pointers: Pointer that contains the address of a heap-dynamic variable that has been deallocated.
Example: Dangling pointer
int *x, *y; = malloc(sizeof(int)); x *x = 1; = x; y (x); free("%d", *y); // Will print garbage printf
int *x, *y; = malloc(sizeof(int)); x *x = 1; = x; y = NULL; y (x); free("%d", *y); // Will segfault printf
Memory Leak or Garbage Creation: Lost-heap dynamic variables.
Example: Memory leaks
int *x = malloc(100); = NULL; x
- The 100 bytes of memory we didn’t
free()
won’t be cleared until the program endsint *x = { 1, 2, 3 }; int *y = { 4, 5, 6 }; = &y; x
{ 1, 2, 3 }
is now garbage we no longer have access to.