230 likes | 380 Views
Referencing addresses, Pointers. Pointer definitions, usage, operations, arrays of pointers, function pointers. The Pointer Concept and Usage in C. Concept of Pointer as memory address Pointer definitions Usage of pointers Operations on pointers Function arguments: Call by Value/Reference
E N D
Referencing addresses, Pointers Pointer definitions, usage, operations, arrays of pointers, function pointers
The Pointer Concept and Usage in C • Concept of Pointer as memory address • Pointer definitions • Usage of pointers • Operations on pointers • Function arguments: Call by Value/Reference • Arrays of pointers and Pointer types of Functions • The const qualifier • Pointers to Functions
Concept of Pointer as memory address • All programs exist in RAM when executing • Data and Code • Variables and data structures (eg. arrays) are allocated memory locations (addresses) • In static blocks, in stack frames, on the heap • It is often useful to exploit knowledge of data structures along with location, or address, data • It is the ability to define a variable that stores address values, coupled with the ability to indirectly access meaningful data values, that makes pointers useful.
Code Block Address CODE Concept of Pointer as memory address • For program codes produced by a compiler, RAM is partitioned into blocks of memory according to what kind of data is placed in the blocks • Executable instructions (code) • Data • Stacks • Heaps • Buffers • For programmer defined variables, names are used • intintVarname = 64 ; • float fltVarName = 3.14159 ; • Although we usually think of the value stored in a variable, the variable name actually refers to a RAM address (at which the value is stored) • However, the names of scalar variables do not correspond directly to their address location values • intVarnamedoes not have a value ! Data Block Address DATA 64 intVarname 3.14159 fltVarname Heap Block Address HEAP Stack Block Address STACKS Buffer Block Address BUFFERS
The strongly typed nature of the C language dictates that all pointers must be type-specific. Each pointer variable is said to point to a RAM address where it is expected that data of a compatible type is stored. Any type incompatibility will be reported as a compiler error. Pointer definitions • C defines two pointer related operators • * - dereferencing (asterisk) • & - address_of (ampersand) • Examples: • // Variable name for an integer storage with value 5intVarname = 5 ; • /* Variable name for a pointer-to-integer storage with value set to the RAM address of Varname */int * ptrVarname = &Varname ;
Usage of pointers • Declarations • int N, *ptrN ; // N is an int variable, ptrN is pointer • double * X, * Y ; // Must always use * for each pointer • Assigning values • ptrN = &N ; // Use the address-of operator to get the RAM address • X = Y ; // Can assign type compatible pointers • *X = 24.53 ; /* Use dereferencing to assign compatible values that will be stored at the RAM location X is pointing to */ • *Y = *X ; /* Copy the value stored where X is pointing to where Y is pointing */ • WARNING! • Be careful to separate the notion of data value from memory address • Variables that hold meaningful data provide direct access to RAM • Pointers that hold address data provide indirect access to RAM when dereferencing them to get at meaningful data
Usage of pointers • There is a special constant that deserves emphasis – NULL • If a pointer variable is not assigned a value then it cannot referenced. It is useful to have a value that can be referenced, but which indicates no particular value of the address • int * ptr = NULL ; • This provides advantages in programming logic because one can test the value of a pointer • if ( ptr != NULL ) { /* perform logic */ } • We will see much more use of NULL pointers in future discussion of dynamic linked lists and other advanced data structures
Operations on pointers • The following operations are meaningful for pointers and define pointer arithmetic • Assume the declarations:intaN [100] ; char aC [256] ; double aD [ 1000 ] ;int * pN ; char * pC ; double * pD ; • The following are valid statements: • pN = aN ; pC = aC ; pD = aD ; // Array names are pointers !! • pN = &aN[0] ; pC= &aC[0] ; pD = &aD[0] ; // Equivalent to above • pN = pN + 1 ; // Points to aN[1] • pN++ ; ++pN ; // …. and then to aN[2], then aN[3] • pN -= 3 ; // …. and now back to aN[0] • Note above that we have used +, -=, and ++ • All arithmetic operations on pointers must involve integer addition or subtraction only !
Operations on pointers If A, B, C and D are compatible pointers, then consider the expressions: A – B OK A + B ERRA – B + C OK A + B – C ERR A – B + C – D OK A + (B - C) OK (A – B) + (C – D) OK A – B – D + C ERR • Pointers are often used with arrays. Assume the following: • float A[1000], * ptrA = &A[0], * ptrB = &A[49] ; • Consider the following equivalent statements • A[5] = 6.73 ;ptrA[5] = 6.73 ; *(ptrA+5) = 6.73 ; • Now consider a problem. Using pointer arithmetic only, determine the array subscript corresponding to the position of the pointer relative to the beginning of the array: • Assume: int Subscript ; • Answer: Subscript = ( ptrB – ptrA ) / sizeof( float ) ; // the value above is 49 • Note that pointers can be subtracted from each other, but not added!
Arrays of pointers It is important to note that in this example the actual values stored in X never move position (ie. undergo rearrangement). We could obtain row output (on a suitably wide terminal) as follows: for( k=0 ; k<1000 ; k++ ) { for( j=0 ; j < 50000 ; j++ ) printf( “%10.2lf”, *( pX[k] + j ) ) ; printf( “\n” ) ; } Note that pX[k] points to the beginning of a particular row in the orginal array X, while pX[k]+j points to the j’th element in that row. It is the full expression *( pX[k] + j ) that actually accesses (ie. dereferences) the meaningful double values in X. • Problem: • Sort a 2D array in ascending order on first column: double X[1000][50000] ; • Note that the size of the rows is immense so exchanging rows will take a lot of processing time • Answer: • Use an array of pointers: double * pX[1000], * tptr ; • Initialize each element to point to the beginning of each row for( k=0; k<50000; k++ ) pX[k] = &X[k][0] ; • for( i=0; i<1000; i++ ) for( j = 999-i ; j>0 ; j-- ) if( *(pX[j])<*(pX[j-1]) ) { tptr = pX[j] ; pX[j] = pX[j-1] ; pX[j-1] = tptr ; }
Function arguments: Call by Value/Reference An important point !! In C, the separation of argument passing mechanisms into Call-by-Value versus Call-by-Reference is just an illusion, a conceptual device that may be useful for some programmers. In actuality, C (and all other languages) uses Pass-by-Value (aka Call-by-Value) exclusively and explicitly in all cases of argument passing. Remember that passing a pointer is still passing a value – the programming approach just exploits this kind of address data appropriate to the logic requirements (eg. access to arrays, pointer arithmetic, dereferencing). • There are two mechanisms for passing data to a function through the arguments • Call by Value • When the argument is the name of a variable • intvCube( int x ) { return x*x*x ; } • Note that vCube returns the value computed and leaves it to the programmer to write a statement that accepts the value • Y = vCube( 25 ) ; • Call by Reference • When the argument is a pointer variable • void rCube( int * X ) { *X = *X * *X * *X ; return ; } • For rCube() the result computed is placed back into the storage used to input the original value • rCube( &Y ) ; // result is placed back into Y • This approach can save both unnecessary memory allocations (on the stack) and also memory transfers of data (during marshalling of the function call).
Pointer types of Functions • Functions must have data types declared. • Pointers are just another data type, so functions can be declared as being of type pointer • double * Larger( double X, double Y ) { if( X > Y ) return &X ; else return &Y ; } • Larger() has type double *, hence its return values must be compatible data types (eg. &X, &Y) • Assuming double U=10.34, W=82.754, Z ; one can use this function to access the result Z = *(Larger(U,W)) ; • Be careful to differentiate between return values that are pointers (addresses in RAM) versus data (meaningful values).
The const qualifier • The notion of a constant value is very different than that of a varying value, hence variable. When a program runs, storages can be allocated to handle values that are expected to vary, while protecting those that must not change. • Example: • const float Pi = 3.141592 ; float Area, R=22.4 ; Area = Pi * R * R ; // This is quite acceptable Pi = R ; // Compiler ERROR ! • Now we consider applying const to pointers, which themselves may point to const or non-const references.
The const qualifier • With pointers there are four kinds of patterns to consider. • non-const pointer to non-const data • non-const pointer to const data • const pointer to non-const data • const pointer to const data • Considering that the purpose of const, from a software engineering perspective, is to provide protection (hence security) for data, each of these patterns must be carefully examined and understood • This is true whether a pointer value, or a data value • These will be illustrated through examples in the following slides.
The const qualifier • When both the pointer, and the data to which it points, are intended to be modified • non-constpointer to non-constdata • Example: • Assume: int N = 5, A[5] = {1,2,3,4,5} ; • Function: void Add1 ( int * B, int n ) { for( ; n>0 ; n--, B++ ) (*B)++ ; return ; } • Note that both the pointer B, and the array values (in A) are modified during the call: Add1( A, N ) ;
The const qualifier • When the pointer to data is intended to be modified, but the data to which it points must not be modified • non-constpointer to constdata • Example • Assume: int N; const char A[5] = {‘M’,’e’,’s’,’s’,’a’,’g’,’e’,’\0’} ; • Function:intStrLen ( const char * S ) {int n = 0 ; for( ; *S != ‘\0’ ; S++ ) n++ ; return n ; } • Note that the pointer S is modified, but the values in the string A are not modified in the call: N = StrLen( A ) ; • Any attempt to modify data in A results in a compiler error.
The const qualifier • When the pointer to data must not be modified, but the data to which it points may be modified • constpointer to non-constdata • Example • Assume: float Pi = 3.14159, * constptrPi = &Pi ; • Function: void ChangePi ( float * const P ) {const float Q = 3.14 ; *P = 3.1415 ; P = &Q ; // Compiler error ! return ; } • Note that the value of Pi may change, but the pointer P may not be modified to point at Q in the call: ChangePi( ptrPi ) ; • Also note that all array names represent const pointers.
The const qualifier • When neither the pointer to data, or the data to which it points, may be modified • constpointer to const data • This technique is used to promote maximum protection over data and references to data • Example:int main ( ) {int N = 3, * ptrN = &N ;constint X = 5, * constptrX = &X ; *ptrX = 3 ; // Compiler errorptrX = &N ; // Compiler errorptrX = ptrN; // Compiler error } • Always think about data protection when using const.
Pointers to Functions • Since function codes and data occupy memory during program execution, it makes sense that we could refer to the locations of the code, or data. • When referring to data we may use and possibly modify the data • When referring to code, however, one must never modify the code data (unpredictable side effects) • This leads to the notion of a pointer to a function • Example prototypes: • void Bubble( int A[ ], constint N, int (*compare)( int x, int y ) ) ;or void Bubble( int[ ], constint, int(*)( int, int) ) ;
Pointers to Functions • Example function definitions: • void Bubble( int A[ ], constint N, int (*compare)( int x, int y ) ) {int p, q ; for( p=1 ; p<N ; p++ ) for( q=0 ; q<N-1 ; q++ ) if( (*compare)( A[q], A[q+1] ) ) swap( &A[q], &A[w+1] ) ; return ; } • int ascending( int a, int b ) { return a > b ; }int descending( int a, int b ) { return a < b ; } • Example of usage: • Bubble( Arr1, N1, ascending ) ; Bubble( Arr2, N2, descending ) ; • Study the examples in the textbook.
A Cautionary Note • Pointers open up an entirely new abstraction for programming, but one which requires deeper knowledge and awareness of memory architectures and data structures • Pointers are used more for optimality of performance by reducing the complexity of memory addressing and access to data • Although using pointers can be elegantly expressed, it takes experience to understand how to achieve elegance • A solid understanding of pointers will lead to easier appreciation of abstract data types (ADT) and container based algorithms.
Summary Concept of Pointer as memory address Pointer definitions Usage of pointers Operations on pointers Arrays of pointers Function pointers
Topic Summary • Pointer Concepts & Mechanisms • Address of :: & • Dereferencing :: * • Operations and Arithmetic • Connection to arrays, functions and data structures • Function pointers • Reading – Chapter 7 • Pointers