Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

Understanding Pointers in C: Memory Addresses and Variables, Lecture notes of Object Oriented Programming

The concept of pointers in C programming language, using the analogy of houses and addresses. It covers the basics of pointers, including the address-of operator, dereferencing, and NULL pointers. It also discusses pointer arithmetic and arrays as pointers.

What you will learn

  • What is the difference between a pointer and a variable?
  • What is a pointer in C?
  • What is pointer arithmetic and how is it used in C?
  • How does the address-of operator work?
  • How do you initialize a pointer in C?

Typology: Lecture notes

2021/2022

Uploaded on 09/12/2022

sergeybrin
sergeybrin 🇺🇸

4.6

(8)

236 documents

1 / 12

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Pointers & Pass-by-reference
Apointer is a value that designates the address of some value.
Pointers are variables that hold a memory location as their value
Remember the address-of operator (&) we used with scanf()?
This operator gets the address of the operand
Pointers give us a way to save the result of the address-of operator into a variable
The type returned by the address-of is a pointer!
Assigning and dereferencing pointers
Instead of passing the result of the address-of operator to a function (e.g.
scanf
) let’s save the
result into a variable
1int a= 5; // declare & initialize an int to the value of 5
2int *b= &a;// declare & initialize a pointer to store the address
of the variable a
At this point,
b
doesn’t store the value 5. It stores the memory address of the variable
a
. The
variable astores the value 5, not b.
But how can we access the value of ausing the pointer b?
We use the
dereferencing operator
to tell the computer "take me to the memory location
stored by this pointer:
1int a= 5; // declare & initialize an int to the value of 5
2int *b= &a;// declare & initialize a pointer to store the address
of the variable a
3int c= *b;// declare & initialize an int to the value of the
dereferenced b, which is the value stored by a
The dereference operator is the complement to the address-of operator, similar to how subtrac-
tion is the complement to addition
House analogy
We are all familiar with houses and the address system we use with the post office
This is a great parallel to pointers in C.
We can think of variables as houses (a very large box to store data in - but we won’t worry about
the size of the house right now).
We can think of memory address as addresses
1
pf3
pf4
pf5
pf8
pf9
pfa

Partial preview of the text

Download Understanding Pointers in C: Memory Addresses and Variables and more Lecture notes Object Oriented Programming in PDF only on Docsity!

Pointers & Pass-by-reference

  • A pointer is a value that designates the address of some value.
  • Pointers are variables that hold a memory location as their value
  • Remember the address-of operator (&) we used with scanf()? - This operator gets the address of the operand
  • Pointers give us a way to save the result of the address-of operator into a variable - The type returned by the address-of is a pointer!

Assigning and dereferencing pointers

  • Instead of passing the result of the address-of operator to a function (e.g. scanf) let’s save the

result into a variable

1 int a = 5; // declare & initialize an int to the value of 5 2 int *b = &a; // declare & initialize a pointer to store the address of the variable a

  • At this point, b doesn’t store the value 5. It stores the memory address of the variable a. The

variable a stores the value 5, not b.

  • But how can we access the value of a using the pointer b? - We use the dereferencing operator to tell the computer "take me to the memory location

stored by this pointer:

1 int a = 5; // declare & initialize an int to the value of 5 2 int *b = &a; // declare & initialize a pointer to store the address of the variable a 3 int c = *b; // declare & initialize an int to the value of the dereferenced b, which is the value stored by a

  • The dereference operator is the complement to the address-of operator, similar to how subtrac-

tion is the complement to addition

House analogy

  • We are all familiar with houses and the address system we use with the post office
  • This is a great parallel to pointers in C.
  • We can think of variables as houses (a very large box to store data in - but we won’t worry about

the size of the house right now).

  • We can think of memory address as addresses

- Do addresses have houses of their own? NO! - But when we declare a pointer, we make a house specifically to store an address

  • Some questions (credit, though this is for C++ http://alumni.cs.ucr.edu/~pdiloren/C++_Pointers/

wherelive.htm):

Where do you live? (&)

  • Suppose we have the following code:

1 int paul = 21; // store the value 21 in paul's house 2 int tom = paul; // store the value in paul's house in tom's house (makes a copy) 3 int *melissa = &paul; // store address of paul in melissa's house

  • And suppose paul’s address is 1500.
  • What is the value stored in melissa’s house? - 1500 - melissa’s house stores a pointer
  • Let’s look at this as a diagram:

Figure 2: IMAGE

  • Now suppose we execute the following line:

1 *melissa = 30;

  • How do the houses update? - paul’s house is updated to store 30 - melissa’s house stays the same - dave’s house stays the same

* dave lives in a different house than paul, and the contents of dave’s house don’t change

when the contents of paul’s house change

NULL pointers

  • For most variable types, we have a default value we typically use by default. For instance, 0 is

the default type for int.

  • Pointers have no explicit default type (meaning will value will be garbage if you do not initialize

the pointer when you declare it).

  • We use a special marco (preprocessor definition) called NULL to indicate that this pointer does

not point to any memory address:

1 int *ptr = NULL; // does not point to anything 2 //now we can check if the pointer is safe to dereference (because it actually points to something) 3 if (ptr != NULL){ 4 *ptr = 5; // safe to dereference 5 }

  • If we don’t make sure we properly initialize a pointer to a memory address

Stress-testing your understanding of pointers:

  • What if we wanted a pointer to a pointer that points to an int? - This means the data type of this variable/house would point to a memory address that

points to the memory address of an int

1 int a = 5; 2 int *ptr = &a; 3 int **ptr2ptr = &ptr;

  • We can continue doing this over and over to get “deeper” into what points to what
  • Consider this complicated example:

1 int *p1, *p2, **p3, a = 5, b = 10; 2 p1 = &a; 3 p2 = &b; 4 p3 = &p2; 5 *p1 = 10; 6 p1 = p2; 7 *p1 = 20; 8 **p3 = 0; 9 printf("%i %i %i %i %i\n", *p1, *p2, **p3, a, b); 10 Answer: 11 0 0 0 10 0

Arrays and pointers

  • Arrays represent contiguous blocks of computer memory. Each element of an array is placed

immediately next to the preceding/next element of the array in memory.

1 /* two equivalent function definitions */ 2 int func( int *paramN); 3 int func( int paramN[]);

  • Pointers and array names can be used almost interchangeably. There are a few exceptions/things

to keep in mind:

1. You cannot assign a new pointer value to an array name (since the array name is a constant

value, and therefore immutable/non-modifiable).

2. The array name will always point to the first element of the array.

Pointer arithmetic

  • We can add/subtract integer values from pointers. This is called pointer arithmetic.
  • This is relevant for iterating over arrays using a pointer and pointer arithmetic
  • The following two expressions are equivalent:

1 *(arr+j) // access element using pointer arithmetic 2 arr[j]; // access element using [] operator

  • What does the first expression do? - Adds j*sizeof(arr type) to arr, and then dereferences that memory location - For instance, if we have an array of int s, each array element is 4 bytes long. - If arr starts at address 3500, the 5th element is located at memory address

3500+(5*sizeof(int)).

- Notice the sizeof() was not explicit, the compiler will automatically multiply j by the

size of each member of the array

  • Consider the following (figures, etc taken from here):

1 int *ip; 2 int a[10]; 3 ip = &a[3];

  • ip would end up pointing to the forth element of a.

Figure 4: IMAGE

  • Now suppose we wrote

1 ip2 = ip + 1;

  • Then we’d have:

Figure 5: IMAGE

Knowledge check

1 #include <stdio.h> 2 3 int main() 4 { 5 int array [5] = { 9, 7, 5, 3, 1 }; 6 7 printf("%p\n", ( void *) &array[1]); // print memory address of array element 1, must cast to void pointer to print 8 printf("%p\n", ( void *) array+1); // print memory address of array pointer + 1 9 10 printf("%d\n", array[1]); // prints 7 11 printf("%d\n", *(array+1)); // prints 7 (note the parenthesis required here) 12 13 return 0; 14 }

Iteration using pointer arithmetic

  • We can use pointer arithmetic to iterate over an array, instead of using integer indices

1 const size_t arr_len = 7; 2 char name[arr_len] = "Mollie"; 3 int numVowels(0); 4 // initialize the pointer to the beginning of the array

11 printf("a: %d, b: %d\n", a, b); 12 }

  • Pass-by-reference prevents the value from being copied and instead tells the function to directly

modify the variable stored in the caller’s scope

- This is clearly useful! - So far, we’ve only been able to return a single data type, but if we can modify parameters in

the caller’s scope, we have a way to “return” multiple values by telling the parameters “not

to copy” into the function’s scope.

  • But C does not support this.
  • Fortunately, pointers are just memory addresses.
  • If you copy a pointer, the memory location says the same.
  • This means we can create pass-by-reference behavior by passing pointers to functions - The pointers are copied into the function, but if we dereference and modify their value, we

aren’t changing the pointer, but the contents the pointer refers to.

- This is essentially pass-by-reference behavior - In the notes on arrays, we actually never needed to return the array! For instance:

1 //NOTICE: the asterisk (star) next to int indicates we are returning an array 2 int * add_to_zeroth_element( int arr[], size_t arr_len, int value){ 3 // this is just a dummy array operation, in practice you'll do wonderful and amazing things here 4 arr[0] += value; 5 // NOTICE: return the array, we don't use [] here, just the name of the array. 6 return arr; 7 } 8 9 void add_to_zeroth_element_no_return( int * const arr, size_t arr_len, int value){ 10 // this is just a dummy array operation, in practice you'll do wonderful and amazing things here 11 arr[0] += value; 12 // don't need 13 } 14 15 int main(){ 16 int arr[] = {1,2,3}; 17 // notice the type here has to match the return type of the function. Exactly what's going on here will be covered with pointers.

18 int * result = add_to_zeroth_element(arr, 3, 5); 19 20 for (j = 0; j < 3; ++j) 21 { 22 printf("%d ", arr[j]); 23 } 24 25 // increment once more on the first element, no return 26 add_to_zeroth_element_no_return(arr, 3, 5); 27 28 for (j = 0; j < 3; ++j) 29 { 30 printf("%d ", arr[j]); 31 } 32 }

Exercises

1. Write a program in C to add two numbers using pointers. Test Data : Input the first number : 5

Input the second number : 6 Expected Output : The sum of the entered numbers is : 11

1 #include <stdio.h> 2 int main() 3 { 4 int first, second, *ptr, *qtr, sum; 5 6 printf(" Input the first number : "); 7 scanf("%d", &first); 8 printf(" Input the second number : "); 9 scanf("%d", &second); 10 11 ptr = &first; 12 qtr = &second; 13 14 sum = *ptr + *qtr; 15 16 printf(" The sum of the entered numbers is : %d\n\n",sum); 17 18 return 0; 19 }

2. Write a program in C to print the elements of an array in reverse order using pointers