Pointer Review and Enhancement

Pointer Usage - Parameter Passing
·  The address is always passed for arrays. The parameter can be declared with [] or as a pointer.
·  For single char and the numeric types, values are passed by default. / // Example 1-1
// Function to replace all occurrences of a character in a string
void replaceAll(char szString[], char cMatch, char cValue)
{
int i;
for (i = 0; i < length(szString); i++)
{
if (szString[i] == cMatch)
szString[i] = cValue;
}
}
// Example 1-2
// Same function using pointers instead
void replaceAll(char *pszString, char cMatch, char cValue)
{
char *pCh;
for (pCh = pszString; *pCh != '\0'; pCh++)
{
if (*pCh == cMatch)
*pCh = cValue;
}
}
Pointer Usage - Parameter Passing continued
·  Numeric variables and single char variables by default are passed by value. To be able to change the value of the variable, the address must be passed using & in the caller
·  The called function will have pointer variables for the corresponding parameters.
·  To reference the value or change the value of the calling function's argument, we dereference it using an * / // Example 2-1
// Passing Addresses so that Values can be returned
double dExamM[100];
double dExamMin;
double dExamMax;
int iExamCnt;

// Pass the array of exams, count of those exams and
// the address for dExamIn and dExamMax.
determineMinMax(dExamM, iExamCnt, &dExamMin, &dExamMax)

// In the called function, those addresses are received into
// pointers.
void determineMinMax(double dExamM[], int iExamCnt
, double *pdExamMin, double *pdExamMax)
{
int i;
*pdExamMax = 0; // dereference to initialize to low value
if (iExamCnt == 0)
{
*pdExamMin = 0;
return;
}
*pdExamMin = 200; // arbitrary high value
for (i = 0; i < iExamCnt; i++)
{
if (dExamM[i] < *pdExamMin)
*pdExamMin = dExamM[i];
if (dExamM[i] > *pdExamMax)
*pdExamMax = dExamM[i];
}
}
Pointer Usage - Parameter Passing continued
·  struct variables by default are passed by value. To be able to change the value of anything in the struct, the address must be passed using & in the caller.
o  The caller passes the address of the struct
o  Within the called function, the parameter must be defined as a pointer
o  To reference the attributes, use pointer notation. / // Example 3-1
typedef struct
{
char szSSN[10];
char szFullName[40];
double dHourlyRate;
} Employee;
Employee employee;
// caller passes the address of employee
calculateRaise (employee);
// called function declares a pointer to an Employee data tpe
void calculateRaise (Employee *pEmployee)
{
// To reference the attributes, we must use pointer notation
pEmployee->dHourlyRate = pEmployee->dHourlyRate * 1.01;
}
Pointer Usage - Referencing Dynamic Memory
Suppose we have the following typedef:
typedef struct Node
{
int iInfo; // contents of a node
struct Node *pNext;
} Node; / // Example 4-1
// declare a pointer to a Node
Node *pNew;
// To dynamically allocate memory for a Node, we use malloc
// passing the size of a Node. We then typecast the return value
// to tell the compiler to what malloc is returning a pointer
pNew = (Node *) malloc(sizeof(Node));
if (pNew == NULL)
exitError("Memory allocation error", "");
Pointer Usage - Parameter Passing continued
·  To change the value of an argument that is a pointer, we must pass the address of that pointer.
o  The caller passes the address of the pointer variable.
o  Within the called function, the parameter must be defined as a ** pointer. This means it is receiving the address of a pointer variable.
o  To reference what the pointer variable is referencing, we must dereference the parameter. / // Example 5-1
Node *insertLL(Node **ppHead, int iNewInfo)
{
Node *pNew;
Node *pPrecedes;
Node *pFind;
// see if it already exists
pFind = searchLL(*ppHead, iNewInfo, &pPrecedes);
if (pFind != NULL)
return(pFind); // already exists
pNew = allocateNode(iNewInfo);
if (pPrecedes == NULL) // at front of list
{
pNew->pNext = *ppHead;
*ppHead = pNew;
}
else
{ // after a node
pNew->pNext = pPrecedes->pNext;
pPrecedes->pNext = pNew;
}
return pNew;
}
Some Operators that Involve Pointers
variable - reference the address of that variable (i.e., it returns a pointer)
array[subscript] - reference the address of that element of the array.
*pointer - dereference
o  When used in an expression, we are referencing the value that is at the location the pointer is referencing (i.e., follow one pointer link)
o  When used in the target of an assignment, we are replacing the value at the location referenced by the pointer (i.e., follow one pointer link)
pointer++ - post-increment
increments the variable, but returns the original value of the variable when used in an expression
++pointer - pre-increment
increments the variable and then returns the new value of the variable / // Example 6-1 - pointer to an integer variable
int arrayM[5] = {100, 200, 300, 400, 500};
int *piValue;
piValue = &arrayM[2]; // piValue contains the address of arrayM[2]
// which contains 300
arrayM[0] = *piValue + 10; // arrayM[0] is now 310
*piValue = 600; // arrayM[2] is now 600
piValue++; // piValue contians the address of arrayM[3];
*piValue = 700;
printf("6-1 %d %d %d %d %d\n"
, arrayM[0], arrayM[1]
, arrayM[2], arrayM[3]
, arrayM[4]);
Output:
6-1 310 200 600 700 500
// Example 6-2
char szName[12] = "Joe King";
char *pszStr;
pszStr = &szName[4]; // pszStr contains the address of szName[4]
printf("6-2 pszStr is '%s'\n", pszStr);
printf("6-2 *pszStr is '%c'\n", *pszStr);
pszStr++;
printf("6-2 pszStr is now '%s'\n", pszStr);
Output:
6-2 pszStr is 'King'
6-2 *pszStr is 'K'
6-2 pszStr is now 'ing'
// Example 6-3
int arrayM[5] = {100, 200, 300, 400, 500};
int *piValue;
piValue = &arrayM[0];
which of these would be true (executing independently) (A, B or neither):
A.  if (*piValue++ == 200) false
...
B.  if (*++piValue == 200) true
...
Pointer Arithmetic
C provides pointer arithmetic which is different from integer arithmetic. The unit measure in pointer arithmetic depends on the datatype defined for the pointer. The sizeof that datatype determines the unit measure. / char *pcMyChar; // unit measure is 1 byte
long *plMyLong; // unit measure is 4 bytes (8 bytes on 64-bit)
double *pdMyScore; // unit measure is 8 bytes
Employee *pemployee; // unit measure is sizeof(Employee)
When the ++ operator is used with a pointer, the increment isn't necessarily by 1. It increments by the unit measure. / pcMyChar++; // increments by 1
plMyLong++; // increments by 4 (4 on 64-bit)
pdMyScore++; // increments by 8
pemployee++; // increments by sizeof(Employee)
what is pdMyScore+2? // 16 more than the address of pdMyScore
Operator Precedence (highest to lowest)
Precedence / Operator / Description / Associativity
1 Highest / ++
--
.
-> / post-increment
post -decrement
attribute selection
selection via pointer reference / left-to-right
2 / ++
--
*
(typecast)
Unary - / pre-increment
pre -decrement
dereference
address of
type cast
Unary - / right-to-left
3 / *
/
% / multiplication
division
modulo (remainder) / left-to-right
4 / +
- / addition
subtraction / left-to-right
5 / <=
>= / less than
greater than
less than or equal to
greater than or equal to / left-to-right
6 / ==
!= / equal to
not equal to / left-to-right
7 / logical AND / left-to-right
8 / || / logical OR / left-to-right
9 Lowest / = / assignment / right-to-left
Expressions with multiple Operators (using precedence) / // Example 7-1
char szName[12] = "Lee King";
char *pStr = &szName[4]; // pStr contains the address of szName[4]
char ch;
ch = *pStr++; // temp save of old pStr, increment pStr
// dereference the temp value
printf("7-1 pszStr is '%s', ch is '%c'\n", pStr, ch);
Output:
7-1 pszStr is 'ing', ch is 'K'
// Example 7-2
char szName[12] = "Lee King";
char *pStr = &szName[4]; // pStr contains the address of szName[4]
char ch;
ch = *++pStr; // increment pStr and then dereference
printf("7-2 pszStr is '%s', ch is '%c'\n", pStr, ch);
Output:
7-2 pszStr is 'ing', ch is 'i'
Exercise #1
Trace the following code using the variables and corresponding addresses. Show the output and show the final value of the variables.
int iX = 2, iY = 3, iZ = 4;
int arrayM[5] = { 6, 5, 4, 3, 2 };
int *pi1;
int **ppi2;
pi1 = &iX;
*pi1 += 2;
printf("1. iX is %d\n", iX);
printf("2. pi1 is %p %d %p\n", pi1, *pi1, &pi1);
ppi2 = &pi1;
**ppi2 = 3;
printf("3. pi1 is %p %d %p\n", pi1, *pi1, &pi1);
printf("4. ppi2 is %p %d %p %p\n", ppi2, **ppi2
, *ppi2, &ppi2);
pi1 = &arrayM[1];
*pi1 = 10;
printf("5. pi1 is %p %d %p\n", pi1, *pi1, &pi1);
pi1 += 1;
*pi1 = 20;
printf("6. pi1 is %p %d %p\n", pi1, *pi1, &pi1);
pi1 = &arrayM[4];
arrayM[*pi1 + 1] = 30;
printf("7. pi1 is %p %d %p\n", pi1, *pi1, &pi1); / Assume Memory is arranged as follows
Variable Name / Address / Value
iX / 6000 / 2 4 3
iY / 6004 / 3
iZ / 6008 / 4
arrayM[0] / 6012 / 6
arrayM[1] / 6016 / 5 10
arrayM[2] / 6020 / 4 20
arrayM[3] / 6024 / 3 30
arrayM[4] / 6028 / 2
pi1 / 6032 / 6000 6016 6020 6028
ppi2 / 6036 / 6032
Output:
1. iX is 4
2. pi1 is 6000 4 6032
3. pi1 is 6000 3 6032
4. ppi2 is 6032 3 6000 6036
5. pi1 is 6016 10 6032
6. pi1 is 6020 20 6032
7. pi1 is 6028 2 6032
Exercise #2
Trace the following code using the variables and corresponding addresses. Show the output and show the final value of the variables.
int iX = 20, iY = 30, iZ = 40;
int arrayM[5] = { 6, 5, 4, 3, 2 };
int *pi1;
int *pi2;
pi1 = arrayM;
*pi1++ = 10;
printf("1. pi1 is %p %d %p\n", pi1, *pi1, &pi1);
pi1 = &arrayM[2];
*++pi1 = 20;
printf("2. pi1 is %p %d %p\n", pi1, *pi1, &pi1);
pi2 = &iX;
printf("3. pi2 is %d %d %d\n"
, *pi2, *pi2 + 1, *(pi2 + 1)); / Assume Memory is arranged as follows
Variable Name / Address / Value
iX / 6000 / 20
iY / 6004 / 30
iZ / 6008 / 40
arrayM[0] / 6012 / 6 10
arrayM[1] / 6016 / 5
arrayM[2] / 6020 / 4
arrayM[3] / 6024 / 3 20
arrayM[4] / 6028 / 2
pi1 / 6032 / 6012 6016 6020 6024
pi2 / 6036 / 6000
Output:
1. pi1 is 6016 5 6032
2. pi1 is 6024 20 6032
3. pi2 is 20 21 30
Exercise #3
Trace the following code using the variables and corresponding addresses. Show the output and show the final value of the variables.
int iX = 20, iY = 30, iZ = 40;
int arrayM[5] = { 6, 5, 4, 3, 2 };
int *pi1;
int **ppi2;
int ***pppi3;
pi1 = arrayM;
ppi2 = &pi1;
pppi3 = &ppi2;
***pppi3 = 100;
printf("1. pppi3 is %p %p %p %d\n"
, pppi3, *pppi3, **pppi3, ***pppi3);
pi1 = &iX;
***pppi3 = 200;
printf("2. pppi3 is %p %p %p %d\n"
, pppi3, *pppi3, **pppi3, ***pppi3); / Assume Memory is arranged as follows
Variable Name / Address / Value
iX / 6000 / 20 200
iY / 6004 / 30
iZ / 6008 / 40
arrayM[0] / 6012 / 6 100
arrayM[1] / 6016 / 5
arrayM[2] / 6020 / 4
arrayM[3] / 6024 / 3
arrayM[4] / 6028 / 2
pi1 / 6032 / 6012 6000
ppi2 / 6036 / 6032
pppi3 / 6040 / 6036
Output:
1. pppi3 is 6036 6032 6012 100
2. pppi3 is 6036 6032 6000 200