Parameters & Arguments Passing arguments to void-functions is no different than passing information to explicit-functions. The arguments passed to void-functions may be variables, constants, pointers (to addresses in memory), and/or expressions. DisplayAdditionProblem DisplayAdditionProblem DisplayAdditionProblem DisplayAdditionProblem DisplayAdditionProblem (3, 22); (Add1, Add2); (2+3, 12-X+Y); (2, Add2); (*Ptr1, *Ptr2); /* /* /* /* /* Constant Variable Expression Constant & Variable Pointers-Section 4.5 */ */ */ */ */ void DisplayAdditionProblem ( int FirstNumber, int SecondNumber) { /* Code Necessary To Complete The Task */ } When the first line of this function is executed, the memory manager will allocate two bytes of contiguous memory for integer value parameter FirstNumber and initialize it with the integer value of the first argument. The memory manager will also allocate two bytes of contiguous memory for integer value parameter SecondNumber and initialized with it with the integer value of the second argument. Program PrtNum.c will provide an opportunity to examine the scope of variables in the selective memory maps embedded within the code. PROGRAM PrtNum.c # include <stdio.h> void PrintNum (int N1, int N2 ,float N3); main () { int Num1, Num2; float Num3 ; Num1 = 5; Num2 = 8; Num3 = Num2 / Num1; /* Note Integer Division */ PrintNum (Num1, Num2, Num3); /* prototype */ } /************************************************/ void PrintNum (int N1, int N2 ,float N3) { printf ("N1 = %3d\n", N1); printf ("N2 = %3d\n", N2); printf ("N3 = %6.2f\n", N3); N1 = N1 * N2; } The output from PrtNum.c is as follows: N1 = N2 = N3 = 5 8 1.00 The formal parameters of PrintNum are N1, N2, and N3. Subprogram PrintNum is called from main; Num1, Num2, and Num3 are arguments (actual parameters) in this call statement. Let us now examine the relationship between the parameter list in the header void PrintNum (int N1, int N2 ,float N3) and the call PrintNum (Num1, Num2, Num3); In this case, Num1 corresponds to N1, Num2 corresponds to N2, and Num3 corresponds to N3. Notice that both the number and data type of variables in the parameter list correspond with the number and type of variables listed in the call. As previously explained, N1 (&2655332) is a copy of Num1 (&2477876); changes made to N1 within PrintNum have no effect on Num1 (out-of-scope). Changing N1 to 40 had no effect on Num1! See memory map PrtNum-5. Pointer Declaration and Assignment Some high-level languages allow two-way passing of variables called passing by reference. Pascal is one such language. When a variable is passed by reference, changes made in a subprogram actually change the original variable. Although C has no mechanism for passing by reference, we will learn to change original variables by passing pointers. The concept of pointers is very important in most programming languages. Knowledge of pointers in C is critical; pointers shall be used with arrays, strings, files, and the passing of parameters. In C we can display addresses associated with symbolic variables in several ways. In review, we have already printed out the address associated with symbolic variable N1 with printf statements like the following: int N1; printf("The Hex Address Associated with N1 = %X\n",&N1); printf("The Address Associated with N1 = %lu\n",&N1); We have already allowed the scanf function to fill the address associated with N1 with a statement like: scanf("%d",&N1); Let us declare the following local variables in function main: int Age = 18, *PtrToAge; The first variable, Age, is an integer; the memory manager allocates two bytes of contiguous memory for Age and the compiler initializes Age to 18. The second variable is a pointer to an integer; PtrToAge shall be used to hold the address of some integer variable; the content of this variable is unknown (Memory Map A). The memory manager will allocate the number of bytes necessary to store a pointer variable. Since the contents of a pointer is an address in memory, pointers need be only large enough to store the largest address possible for the specific computer. The number of bytes allocated to a pointer will vary because (1) all computers do not have the same memory capacity and (2) all computers do not use the same addressing. The following line of code will allow you to determine the size of a pointer on your computer. printf("Pointers Are Allocated %ld bytes\n",sizeof(PtrToAge)); Pointers Are Allocated 4 bytes Four-byte pointers are common; the thirty-two bits would allow the computer memory to be expanded to 232 bytes (4,294,967,296). We shall assume a four-byte pointer size throughout this text. PtrToAge = &Age; Pointers may be assigned to an address (of matching data type) or another pointer (of matching data type). The pointer assignment statement above places the address, associated with symbolic integer variable Age, into variable PtrToAge. Examine Memory Map B. Age is stored at address 1196538 (abbreviated &1196538). A compilation error will be generated if PtrToAge were assigned to the address of a float variable or the address of a char variable; the data types must match. The arrow, on memory map above, is used to illustrate the relationship between the pointer and the variable to which it points. The following code segment could be used to display the critical components of the memory map above. printf ("%14s%20s\n","Age[int]","PtrToAge[int *]"); printf (" ---------------------------\n"); printf (" |%6d | |%10p |\n",Age,PtrToAge); printf(" ---------------------------\n"); printf ("%11p-%d%17p-%d\n",&Age,sizeof(Age), &PtrToAge,sizeof(PtrToAge)); Output: Age[int] -------------| 18 | -------------1196538-2 PtrToAge[int *] -------------| 1196538 | -------------1196534-4 Let us now create Memory Map Ptr-1 for the following declarations: float PayPerHour = 7.50, *PayPtr; char MiddleInitial = 'E', *InitialPtr; Without looking at Memory Map Ptr-2, attempt to show the memory map as it would be after the following pointer assignment statements: InitialPtr = & MiddleInitial; PayPtr = & PayPerHour; Now check your work! Let us now create Memory Map Quantity-1 for the following declarations and assignments: unsigned long int QuantityInStock= 200000; float PayPerHour = 7.50; unsigned long int *QuantityPtr1, *QuantityPtr2; QuantityPtr1 = &QuantityInStock; QuantityPtr2 = QuantityPtr1; The address in one pointer may be assigned to the address in another pointer (of matching data types). In the illustration above, both QuantityPtr1 and QuantityPtr2 point to QuantityInStock. De-referencing Pointers Thus far we have printed the addresses of pointers and the addresses contained within pointers. We have also assigned pointers to the address of a variable (of appropriate data type). We have transferred the address contained in one pointer to another pointer (of like data type). It is now time to use the pointer to change the contents of the original memory location; we shall do this by dereferencing the pointer. As previously mentioned, all parameters passed to C subprograms are passed by value. Pointers are a valid data type and may be included within the parameter list. In program Switch.c, the main function has two local integer variables X, and Y. Let us examine a program which uses a void–function, SwitchIntegers, to switch the contents of variables X and Y. PROGRAM Switch1.c # include <stdio.h> void SwitchIntegers (int *FirstPtr, int *SecondPtr); main () { int X=5, Y=8; printf ("%d - %d\n",X,Y); SwitchIntegers (&X,&Y); printf ("%d - %d\n",X,Y); } void SwitchIntegers (int *FirstPtr, int *SecondPtr) { int Temp; Temp = *FirstPtr; *FirstPtr = *SecondPtr; *SecondPtr = Temp; } Output from Switch1.c is as follows: 5 - 8 8 - 5 The addresses of X and Y are arguments in the call to function SwitchIntegers. The argument (&X) is matched with the parameter pointer (FirstPtr) and the argument (&Y) is matched with parameter pointer (SecondPtr). It is by dereferencing these pointers that the contents X and Y are exchanged. Program Switch2.c provides a memory dump that might help you to better understand what has happened in program Switch1.c. This type of memory dump will be quite helpful in debugging errors related to the transfer of information between subprograms. PROGRAM Switch2.c # include <stdio.h> void SwitchIntegers (int *FirstPtr, int *SecondPtr); main () { int X=5, Y=8; puts("------------------ Beginning of main ---------------------\n"); printf ("%14s%16s\n","X[int]","Y[int]"); printf (" ---------------------------\n"); printf (" |%6d | |%6d |\n",X,Y); printf(" ---------------------------\n"); printf ("%11p-%d%17p-%d\n\n",&X,sizeof(X),&Y,sizeof(Y)); SwitchIntegers (&X,&Y); puts("-------------------- End of main ------------------------\n"); printf ("%14s%16s\n","X[int]","Y[int]"); printf (" ---------------------------\n"); printf (" |%6d | |%6d |\n",X,Y); printf(" ---------------------------\n"); printf ("%11p-%d%17p-%d\n\n",&X,sizeof(X),&Y,sizeof(Y)); } void SwitchIntegers (int *FirstPtr, int *SecondPtr) { int Temp = 0; /* Initialized to 0 for memory map output only - not efficient */ puts("------------- Beginning of SwitchIntegers ---------------\n"); printf ("%16s%18s%16s\n","FirstPtr[int]","SecondPtr[int]","Temp[int]"); printf (" -----------------------------------------\n"); printf (" |%10p | |%10p | |%6d |\n",FirstPtr,SecondPtr,Temp); printf (" -----------------------------------------\n"); printf (" |->%4d | |->%4d |\n",*FirstPtr,*SecondPtr); printf(" ---------------------------\n"); printf ("%11p-%d%17p-%d%17p-%d\n\n",&FirstPtr,sizeof(FirstPtr), &SecondPtr,sizeof(SecondPtr),&Temp,sizeof(Temp)); Temp = *FirstPtr; *FirstPtr = *SecondPtr; *SecondPtr = Temp; puts("---------------- End of SwitchIntegers -----------------\n"); printf ("%16s%18s%16s\n","FirstPtr[int]","SecondPtr[int]","Temp[int]"); printf (" -----------------------------------------\n"); printf (" |%10p | |%10p | |%6d |\n", FirstPtr,SecondPtr,Temp); printf (" -----------------------------------------\n"); printf (" |->%4d | |->%4d |\n",*FirstPtr,*SecondPtr); printf(" ---------------------------\n"); printf ("%11p-%d%17p-%d%17p-%d\n\n",&FirstPtr,sizeof(FirstPtr), &SecondPtr,sizeof(SecondPtr),&Temp,sizeof(Temp)); } Output from Switch2.c is as follows: ------------------ Beginning of main --------------------X[int] -------------| 5 | -------------1349514-2 Y[int] -------------| 8 | -------------1349512-2 ------------- Beginning of SwitchIntegers --------------FirstPtr[int] -------------| 1349514 | -------------|-> 5 | -------------1349504-4 SecondPtr[int] -------------| 1349512 | -------------|-> 8 | -------------1349508-4 Temp[int] --------------| 0 | --------------- 1349494-2 ---------------- End of SwitchIntegers ----------------FirstPtr[int] -------------| 1349514 | -------------|-> 8 | -------------1349504-4 SecondPtr[int] -------------| 1349512 | -------------|-> 5 | -------------1349508-4 Temp[int] --------------| 5 | --------------- 1349494-2 -------------------- End of main -----------------------X[int] -------------| 8 | -------------1349514-2 Y[int] -------------| 5 | -------------1349512-2 Variable Temp was initialized to 0 only for display purposes. The printf statements that provide the memory dumps could be better accomplished using one or more subprograms; this will be left to the student. The dereferencing of pointers is the same for void-functions and explicit-functions. Memory maps will prove to be helpful in debugging until you are comfortable with the concept.