Pointers point to memory addresses that store values. There are different types of pointers for different data types like integers, characters, and doubles. Pointers can be initialized using the address-of operator (&) and dereferenced using the asterisk (*) operator to access the value stored at a memory address. Memory for pointers must be dynamically allocated using functions like malloc() and freed using free() to avoid memory leaks. Pointers allow passing arguments by reference so functions can modify values.
2. Heap & Pointers
● Types of storage:
– global (defined in the static data segment)
– automatic or local (defined in the program stack)
– dynamic
● Dynamic memory blocks required by the program(mer) come from a memory pool
called a heap—they are allocated and deallocated
● C does not have garbage collection. All allocated variables must be deallocated by the
program(mer)
● A pointer is a variable whose value is a memory address representing the allocated
memory block
3. Declaring a Pointer
● Different types of pointers required for different data blocks:
– int *p; /* An uninitialized pointer to an integer number or an
array of integer numbers of size sizeof(int) */
– char *s; /* A pointer to a character array—a string (if NULL-
terminated) */
– void *v; /* An unstructured uninitialized pointer—USE WITH
CAUTION! */
– int *p1, *p2, p3; /* Two integer pointers and an integer */
– int **pp; /* A pointer to a pointer */
– double ***foo; /* A pointer to a pointer to a double pointer */
● Types of pointers can be modified by casting:
– int *p = …;
– char *c = (char *)p; /* Now, c points to the first byte of the
number! */
– c++; /* Now, it points to the second byte! */
● Casting pointers should be avoided.
4. Declaring a Pointer
● Different types of pointers required for different data blocks:
– int *p; /* An uninitialized pointer to an integer number or an
array of integer numbers of size sizeof(int) */
– int* p1; /* Where and whether you put the space, is up to you */
– char *s; /* A pointer to a character array—a string (if NULL-
terminated) */
– void *v; /* An unstructured uninitialized pointer—USE WITH
CAUTION! */
– int *p1, *p2, p3; /* Two integer pointers and an integer */
– int **pp; /* A pointer to an integer pointer */
– double ***foo; /* A pointer to a pointer to a double pointer */
● Types of pointers can be modified by casting:
– int *p = …;
– char *c = (char *)p; /* Now, c points to the first byte of the
number! */
– c++; /* Now, it points to the second byte! */
● Casting pointers should be avoided.
5. Initializing a Pointer
● The & operator calculates the address of a static or automatic variable:
– int z = 0;
– int *zp = &z; /* The address of z is now in zp */
– printf (“z=%i, &z=%xn”, z, zp);
● Remember that the address of an automatic variable makes no sense outside of the
function:
– int *foobar (void) {
– int localVar = 617363;
– int *ptr = &localVar;
– return ptr; /* Bad idea! After foobar returns, localVar is no
more!—Unless it's also static */
– }
6. Dereferencing a Pointer
● To dereference a pointer is to obtain the value of the variable that it refers to. Operator
* dereferences a pointer:
– int var1 = 77, var2;
– int *ptr = &var1;
– var2 = *ptr; /* Now the value of var2 is 77 */
– (*ptr)++; /* Now, var1 is 78 */
– *ptr++; /* Dereference the pointer and then increment it; var1 is
still 78, but ptr changed by 1 */
– (*ptr) = 10; /* Now, var1 is 10 */
● Operators & and *, if properly used, are complementary: *&x is the same as x.
However, &*x is not legal.
7. “Constant” Pointers
● A pointer to a constant;
– const int *p; /* p can change, but *p cannot */
– int const *p; /* same as above */
● A constant pointer to a variable:
– int *const p; /* *p can change, but p cannot */
● A constant pointer to a constant:
– const int *const p; /* nothing can change */
8. Generic Pointers
● A generic pointer cannot be dereferenced:
– void *generic = ….;
– *generic = ... /* Illegal */
● Any pointer can be safely cast to a generic pointer:
– int var = 564;
– int *iptr = &var;
– void *generic = (void*)iptr;
● A generic pointer can be cast to a specific pointer of the original type, if known:
– *(int*)generic; /* 564 */
● What's a NULL? It's a macro defined in stdlib.h:
– #define NULL (void*)0
9. Pointer Arithmetic
● Operators +, -, ++, and – increment/decrement pointers by the number of object sizes:
– char *s = …;
– s++; /* s is incremented by 1 */
–
– int *ptr = …;
– ptr += 2; /* ptr is increased by 2*sizeof(int) */
● Very useful to work with arrays in general and strings (NULL-terminated character
arrays) in particular:
– char *source = “Hello, world”; /* source is the address of the
first element of the string */
– char *dest = …; /* allocate memory for the copy */
– while (*dest++ = *source++); /* copy while not NULL */
● Pointers of the same type can be subtracted, and the difference is the number of
objects between hgem:
– int diff = ptr2 – ptr1;
● Pointers of the same type can be compared with >, >=, <, <=, ==, and !=. Comparing
the pointers does not involve comparing the memory they point to.
10. Memory Allocation
● Memory allocation functions are defined in stdlib.h. They return a pointer to a freshly
allocated block or NULL in case of failure.
● Function void *malloc(size_t) gets a block of uninitialized memory.
– int *myArray;
– if (NULL == (myArray = malloc (32 * sizeof(int))) {
– perror (“malloc”);
– exit (EXIT_FAILURE);
– }
● Function void *calloc(size_t,size_t) gets a block zero-filled memory.
– if (NULL == (myArray = calloc (32, sizeof(int))) {...
● Function void *realloc(void*, size_t) changes the size of a previously allocated block
and returns a new pointer, if necessary. The content of the block is preserved.
– if (NULL == (myArray = realloc (myArray , 64 * sizeof(int))) {…
● If the return value is not saved, the allocated block is inaccessible (memory leak).
11. Memory Deallocation
● Any previously allocated block must be deallocated with free():
– free (myArray);
● The value of myArray did not change, but should not be used anymore. For safety,
nullify it:
– myArray = NULL;
12. Working with Memory Blocks
● memcpy() copies one block to another if they do not overlap
● memmove() copies one block to another; the blocks may overlap
● memcmp() compares two memory blocks, bytewise
● bzero() zero-fills a block
13. The Mystery of scanf()
● All parameters to scanf, expect for the format string, must be pointers to the previously
allocated storage variables:
– int a;
– double b;
– char c;
– char *str = malloc (128);
– if (4 != scanf (“%i %lf %c %s”, &a, &b, &c, str)) {… /* str is
already a pointer */
14. The Mystery of FILE*
● FILE* is a pointer to a variable of type FILE, which is defined in stdio.h. In fact, the
standard I/O streams are simply variables of this type:
– FILE *stdin, *stdout, *stderr;
15. Binary I/O
● Function long ftell(FILE*) returns the current reading/writing position in an open file.
● Function int fseek(FILE*, long offset, int mode) changes the current position in an open
file by offset. The mode can be SEEK_SET (from the beginning of the file), SEEK_CUR
(from the current position) or SEEK_END (from the end of the file).
– fseek (f, sizeof (double), SEEK_CUR); /* go to the next double */
● Function size_t fread(void *buff, size_t size, size_t count, FILE *f) reads size*count
bytes into the previously allocated buffer buff from the previously open file f:
– const int BUFF_SIZE = 1024;
– FILE *infile = fopen(“myfile.txt”, “r”); /* check return value */
– char *data = malloc (BUFF_SIZE); /* check return value */
– if (BUFF_SIZE != fread (data, 1, BUFF_SIZE, infile)) {…
● Function size_t fwrite(void *buff, size_t size, size_t count, FILE *f) writes size*count
bytes from the previously allocated buffer buff to the previously open file f. Both
functions return the number of successfully read/written objects.
16. Passing Arguments by Reference
● If you pass a pointer to an object to a function, the function can modify the object:
– void addToInt (int *ptr, int change) {
– (*ptr) += change;
– }
– ...
– int val = 10;
– addToInt (&val, 5); /* val is 15 now */
● Always pass large objects (arrays and structs) by reference
● If the object shall not be modified, use a const pointer:
– void functionThatShallNotChangeX (const int* x) { ...
● Pass small objects by reference only when the function shall change their value
17. Pointers to Blocks of Pointers
● Memory-allocating functions can allocate memory for pointers to other blocks:
– int **table;
– table = malloc (sizeof (int*) * 100); /* a table of 100 integer
pointers */
– for (i = 0; i < 100; i++)
– table[i] = malloc (sizeof (int) * 256); /* each pointer, in
turn, points to a block of 256 integers */
–
– /* Did I tell you that C does to define variables only before the
first executable statement of a function? */
–
– int *q = table[0]; /* the address of the first block */
– int c = table[0][0]; /* the value at the beginning of the first
block */