Introduction • Pointers enable programs to • accomplish pass-by-reference, • pass functions between functions, • manipulate strings and arrays, and • create and manipulate dynamic data structures that grow and shrink at execution time, such as linked lists, queues, stacks and trees. Pointer Variable Definitions and Initialization • A pointer contains the address of another variable that contains a specific value • The pointer points to that variable • A variable name directly references a value • A pointer indirectly references a value • Referencing a value through a pointer is called indirection Pointer Variable Definitions and Initialization Pointer Variable Definitions and Initialization Declaring Pointers • Define countPtr as an int *—a pointer to an integer: • int *countPtr; • Read right-to-left, “countPtr is a pointer to int” or “countPtr points to an object of type int.” • * indicates that the variable is a pointer. Pointer Variable Naming • Our convention – end each pointer variable name with Ptr • Other common naming conventions include starting the variable name with p (e.g., pCount) or p_ (e.g., p_count) Pointer Variable Definitions and Initialization Define Variables in Separate Statements • The * does not distribute to each variable: • int *countPtr, count; • countPtr is a pointer to int, but count is just an int • Always write the preceding declaration as two statements to prevent ambiguity: • int *countPtr; • int count; Initializing and Assigning Values to Pointers • Initialize pointers when they’re defined, or assign them a value • A pointer may be initialized to NULL, 0 or an address: • NULL points to nothing. • 0 is equivalent to NULL. NULL is preferred. • Assigning a variable’s address to a pointer is discussed in next sections Pointer Operators • Unary address operator (&) returns the address of its operand • int y = 5; • int *yPtr = &y; • initializes pointer variable yPtr with variable y’s address • yPtr is then said to “point to” y Pointer Operators • The following diagram shows the preceding pointer’s representation in memory, assuming that integer variable y is stored at location 600000 and the pointer variable yPtr is stored at location 500000 Pointer Operators • Unary indirection operator (*), also called the dereferencing operator • Apply to a pointer operand to get the value of the object to which the pointer points—known as dereferencing a pointer • yPtr points to y, which is 5, so the following statement prints 5 • printf("%d", *yPtr); • Dereferencing a pointer that has not been initialized with or assigned the address of another variable in memory is an error • Can cause a fatal execution-time error, accidentally modify data, produce incorrect results, or lead to a security breach Pointer Operators • Next program demonstrates the pointer operators & and * • Conversion specification %p outputs a memory location as a hexadecimal integer on most platforms • The & and * operators are complements of one another • The addresses in the output will vary across systems that use different processor architectures, different compilers and even different compiler settings Pointer Operators 1.#include <stdio.h> 2. 3. int main(void) { 4. int a = 7; 5. int *aPtr = &a; // set aPtr to the address of a 6. 7. printf("Address of a is %p\nValue of aPtr is %p\n\n", &a, aPtr); 8. printf("Value of a is %d\nValue of *aPtr is %d\n\n", a, *aPtr); 9. printf("Showing that * and & are complements of each other\n"); 10. printf("&*aPtr = %p\n*&aPtr = %p\n", &*aPtr, *&aPtr); 11. } Pointer Operators Address of a is 0x7fffe69386cc Value of aPtr is 0x7fffe69386cc
Value of a is 7 Value of *aPtr is 7
Showing that * and & are complements of each other
&*aPtr = 0x7fffe69386cc *&aPtr = 0x7fffe69386cc Passing Arguments to Functions by Reference • By default, arguments (other than arrays) are passed by value • Functions often need to modify variables in the caller or to receive a pointer to a large object to avoid the overhead of copying it (as in pass-by- value) • A return statement can return at most one value from a called function to its caller—pass-by-reference can enable a function to “return” multiple values by modifying the caller’s variables • Pointers and the indirection operator enable pass-by-reference • When calling a function with arguments that should be modified in the caller, you use & to pass each variable’s address • A function that receives the address of a variable in the caller can use the indirection operator (*) to modify the value at that location in the caller’s memory, thus effecting pass-by-reference Passing Arguments to Functions by Reference 1. // Cube a variable using pass-by-value. 2. #include <stdio.h> 3. 4. int cubeByValue(int n); // prototype 5. 6. int main(void) { 7. int number = 5; // initialize number 8. 9. printf("The original value of number is %d", number); 10. number = cubeByValue(number); // pass number by value to cubeByValue 11. printf("\nThe new value of number is %d\n", number); 12. } 13. 14. // calculate and return cube of integer argument 15. int cubeByValue(int n) { 16. return n * n * n; // cube local variable n and return result 17. } Passing Arguments to Functions by Reference 1. // Cube a variable using pass-by-reference with a pointer argument. 2. #include <stdio.h> 3. void cubeByReference(int *nPtr); // function prototype 4. 5. int main(void) { 6. int number = 5; // initialize number 7. printf("The original value of number is %d", number); 8. cubeByReference(&number); // pass address of number to cubeByReference 9. printf("\nThe new value of number is %d\n", number); 16. } 17. // calculate cube of *nPtr; actually modifies number in main 18. void cubeByReference(int *nPtr) { 19. *nPtr = *nPtr * *nPtr * *nPtr; // cube *nPtr 20. } Passing Arguments to Functions by Reference • Passing the address enables pass-by-reference • The function’s parameter is a pointer to an int called nPtr. • The expression *nPtr dereferences the pointer • The function assigns the calculation result to *nPtr—which is really the variable number in main—thus changing number’s value in main • Use pass-by-value unless the caller explicitly requires the called function to modify the argument variable’s value in the caller • Prevents accidental modification of the caller’s arguments and is another example of the principle of least privilege Passing Arguments to Functions by Reference Use a Pointer Parameter to Receive an Address • A function receiving an address must use a pointer parameter • void cubeByReference(int *nPtr) {
Pointer Parameters in Function Prototypes
• The function prototype for cubeByReference specifies an int * parameter • It’s not necessary to include pointer names in function prototype • They’re ignored by the compiler • But it’s good practice to include them for documentation purposes Passing Arguments to Functions by Reference Functions That Receive One-Dimensional Arrays • The compiler does not differentiate between a function that receives a pointer and one that receives a one-dimensional array • The function must “know” when it’s receiving an array versus. a single variable passed by reference • A parameter of the form int b[] is converted to int *b • The two forms are interchangeable Bubble Sort Using Pass-by-Reference • Let’s write a bubble sort program using two functions—bubbleSort and swap. • Function bubbleSort sorts the array. • It calls function swap to exchange the array elements array[j] and array[j + 1] • Remember that C enforces information hiding between functions, so swap does not have access to individual array elements in bubbleSort. • Because bubbleSort wants swap to have access to the array elements to be swapped, bubbleSort passes each of these elements by reference to swap—the address of each array element is passed explicitly. Bubble Sort Using Pass-by-Reference • Although entire arrays are automatically passed by reference, individual array elements are scalars and are ordinarily passed by value. • Therefore, bubbleSort uses the address operator (&) on each of the array elements in the swap call to effect pass-by-reference as follows • swap(&array[j], &array[j + 1]); • Function swap receives &array[j] in pointer variable element1Ptr. Bubble Sort Using Pass-by-Reference • Even though swap—because of information hiding—is not allowed to know the name array[j], swap may use *element1Ptr as a synonym for array[j]—when swap references *element1Ptr, it’s actually referencing array[j] in bubbleSort. • Similarly, when swap references *element2Ptr, it’s actually referencing array[j + 1] in bubbleSort Bubble Sort Using Pass-by-Reference • Even though swap is not allowed to say int hold = array[j]; array[j] = array[j + 1]; array[j + 1] = hold; precisely the same effect is achieved by int hold = *element1Ptr; *element1Ptr = *element2Ptr; *element2Ptr = hold; References • C How to Program, Ninth Edition by Deitel & Deitel, Pearson, 2022.