CPPPPDL:C++ Primer Plus Developer's Library : Stephen Prata
[1] Functions:
A sub program that does a defined task.
[2] Flow (For user defined functions):
[3] Execution:
[4] A function may or may not have argument(s).
Skeleton of declaration of a function:
typeName functionName (argumentList...){
statements;
return values of type typeName; //nothing returned if typeName is void
}
[5] What you can use functions to return?
int,float,double,long,structs,objects,arrays that are a part of a struct or an object....
What you cannot use functions to return?
For eg: Arrays
[6] What happens at the compiler level after returning values?
[7] "ANSI C borrowed prototyping from C++..." [CPPPPDL]
Prototypes:
[8] Function arguments and passing by value technic:
Take for eg:
// function prototype(s)
double volumeCube(double);
int main(int argc, char **argv)
{
double side = 5.64;
// Function arguments and passing by value technic:
cout << "The area of the cube of side "<< side <<" m is "<< endl;
cout << volumeCube(side) << " m³"<< endl; // function call
return 0;
}
// function definition
double volumeCube(double x)
{
return x*x*x;
}
Pass by value:
Source here
Advantages of passing by value:
Arguments passed by value can be variables (eg. x), literals (eg. 6), or expressions (eg. x+1).
Arguments are never changed by the function being called, which prevents side effects.
Disadvantages of passing by value:
Copying large structs or classes can take a lot of time to copy, and this can cause a performance penalty.
In most cases, pass by value is the best way to pass arguments to functions — it is flexible and safe.
[9] Name of the array is the pointer pointing to the first element of the array. (or)
Name of the array is the address of the first element of the array.
C++ treats array names as if it were a pointer. Look at [Aha!] in the code.
[10] The approach is not a pass by reference, but pass by value. Here though a pointer
is passed, the address to the first element is passed as a value.
Check the code [bingo] section to check for yourself. But, if one computes the sizeof
arr in function definition, it will return as 4 bytes, because it returns the size of
pointer variable.
[11] When passing by value, the functions use the copy of the variable. However, in
case of arrays, they use the original array.
[12] In the function definition of sumOfArrayElements, an empty pair of square
brackets following the array name indicates that the function can accommodate arrays of any size.
[13] With respect to addresses (&) and pointers:
arrayName[i] is equivalent to
*(arrayName+i) - fetches the value of ith element in the array. &arrayName[i] is equivalent to arrayName+i - fetches the address of the ith element in the array.
[14] To find the range of values in an array: Approach - pass two pointers,one to indicate the start and another to indicate the end of an array.
[15] Pointing a const variable to a non const pointer is an invalid operation.
If this were allowed, then, changing the value of the non const pointer would
modify the value of the const variable.
const int* -> a (variable) pointer to constant integer data,
int* const -> a constant pointer to integer data.
The trick is reading from right to left.
[17] const T is equivalent to T const, where T is a datatype.
[18] Key is to read it backwards
int* - pointer to int
int const * - pointer to const int
int * const - const pointer to int
int const * const - const pointer to const int
Now the first const can be on either side of the type so:
const int * == int const *
const int * const == int const * const
int ** - pointer to pointer to int
int ** const - a const pointer to a pointer to an int
int * const * - a pointer to a const pointer to an int
int const ** - a pointer to a pointer to a const int
int * const * const - a const pointer to a const pointer to an int
[19] Passing 2D Arrays as arguments to the function:
The representation of a 2D array is as follows:
array[m][n] = {{1,2,...,n elements},{1,2,...,n elements},{1,2,...,n
elements},......,{1,2,...,n elements}m of these elements as rows}
every i in m contains n elements. The whole array point to n int's,
instead of every j in n pointing to int. Hence data[m][n] could be
passed as
*data[n] or (*data)[n] or data[][n] inside
as function argument. the function definition could work with any number of
rows, but column size needs to be fixed.
[19a] 2D Array == 1D Array inside an 1D Array.For eg: data [4][5] - data is an
array of four ints, with each int element containing 5 ints. So data [3][2]
fetches an int value from the 3rd int element. Then further, from the 3rd int
element, 2nd int element is fetched. Since the array is the name of the
pointer, to fetch mth element, it has to be dereferenced once, and in mth
element, nth element has to be fetched, so yet again dereferencing has to be
done. So, all in all, dereferencing it twice in total.Hence -
((data+m)+n) does the operation described in the above text. For convenience
we represent
*(*(data+m)+n) == data[m][n].
Examine the following lines carefully,
data points to the address of the first element in the array.
data+m pointer to row m.
*(data+m) accesses the value in mth row
*(data+m)+n pointer to mth row, nth column, equivalent to data[m]+n
*(*(data+m)+n) accesses the nth value of mth row equivalent to data[m][n].
[20] Pass the row information in the function argument list, not the column.
In the function definition, use the pointer to an array. The pointer to an
array could be used as if it were to be the name of an array.
[21] Following is a single pointer to array of four ints
int (*ar2)[4]
[22] Following is the declaration for four pointers to int type
int *ar2[4]
Functions with C-Style String Arguments
[23] Difference between C-style strings and regular array: Strings have a
built-in terminating character.
[24] About char array: char array containing characters, but no null character
is just an array, not a string.
[25] Algorithmic aspect: Functions don't require to be provided with the size
of the string. Instead, use a looping statement, and check until a terminating
character has reached.
[26] Functions cannot return strings, solution: return the address of the
string instead. Algorithmic aspect: Return a pointer.
[27] Create a new array of char:
char* ptr = new char [size];
[28] The disadvantage of using the design in [27]: The programmer is
responsible for freeing the resource held by a pointer. If overlooked, can
cause memory leaks. However, 'delete' can be used to free the resource held by
a pointer.
[29] Algorithmic Aspect: To traverse through a C-string:
while(*ptrToArray!=nullChar)
// do something...
// increment the pointer by 1 byte essentially
ptrToArray++;
[30] Algorithmic Aspect: To count the number of chars in a C-string:
Have a counter variable of type int, then..
while(*ptrToArray!=nullChar)
// do something...
// increase the counter until the array encounters
// the null char '\0'
counter++;
// increment the pointer by 1 byte essentially
ptrToArray++;
[31] Algorithmic Aspect: To check if a string is a palindrome:
Loop through both the strings, and compare char wise.
while(L -- > 0)
// do something...
// check if...
R[L] == S[L]?return true:return false;
Functions with structs
[31] Functions that return or accept structs, accommodate returning arrays and
strings. Where as the functions in general, cannot accept or return strings
and arrays.
[32] When considering writing functions for structs, the varaibles are treated as single valued variables
structs packs the data into a single entity, this is a construct that is contrary to arrays.
[33] structs can be passed to functions by value. A copy of the original struct is used.
[34] Recall that the name of the array is the address of the first element of
the array (array equivalent to &array[0]). In structs the name of the struct
is merely treated to be the name of the struct, and the address is fetched
using the & operator.
[35] Disadvantage of design [33] : If the struct is large, this would hinder
the performance of the system. (PS: Design [33] is an acceptable practice.
However, it is an inefficient one.)
Solution : Use the address. Pass the address of the struct in the
function. Then use a pointer to access the contents of the struct.
[36] In C++, one can code functions using the pass by reference technique.
[37] Define a struct:
struct validStructName{
T variableNameA;
T variableNameB;
// T is a datatype
};
[38] Declare a struct:
validStructName structInstance;
[39] Access the members of the struct:
structInstance.variableNameA;
Passing structs address as an argument
Bearing these in mind:
[40]
Functions with string class objects
[41] C++ treats string as any other fundamental datatype, such as int or
float.
Functions and array objects
[42] The objects can be passed as values to functions, but, the function
definition works on the copy of the object. Also, one can pass the pointer to
the object. In which case, the function definition acts on the original copy
of the object.
[43] Thumb rule: (a) To display the contents of the array object, pass the value
of the object. (b) To modify the contents of the array object, pass the address
of the object.
void showValues(array<double,4> anArray); //[43](a) anArray is an object.
double scaleValues(array<double,4> *ptrObject) //[43](b) pointer to an object.
[44] C++ does not allow to pass an array as an argument to a custom function.
BUT...One can pass a pointer to an array by array's name alone.One can pass an 1D array as an argument in a function, for which one has to declare function formal parameter in one of following three ways
Passing pointers as a formal parameter:
T foo(int *param)
{
.
.
.
}
Passing arrays with known size as a formal parameter:
T foo(int param[10])
{
.
.
.
}
Pass arrays with unknown size as a formal parameter:
T foo(int param[])
{
.
.
.
}
[45] More on container classes https://isocpp.org/wiki/faq/containers
More on std::array container http://en.cppreference.com/w/cpp/container/array
More on C++11 Standards http://www.stroustrup.com/C++11FAQ.html#when-compilers
Like built-in arrays in C++, the container array class have certain
constraints. The arrays can be passed by value to a function. However, the
function acts on the copy of the original object which is an inefficient
design.
Pointer to functions
[46] Functions, like data, has address. A function's address is usually a
memory address where the machine language code of the function begins.
Why get the address of the function?
Solution: It may prove useful if a function wants to run another function. By
passing the address, the first function finds the second function, and runs
it.
[47] Basics:
In order to implement the plan of passing pointers to functions, in order to
facilitate functions to accept functions as arguments.
Step 1. Get the address of the function. How?
Solution: Use the function name by without using the trailing parentheses -
this provides the address of the function.
Eg: foo() passes the return value of the function. foo returns the address of
the function.
Quiz: Difference between first(second) and first(second()).....
Solution: first(second): The first() function invokes the second() function from
within the first() function.
first(second()): The first() function first invokes the second() function,
then the return value of second() is passed to the first() function.
Step 2. Declare a pointer to a function. How?
[48] A pointer to a function has to specify the same information as the function
prototype conveys the compiler - declaration has to comply with the function
return value and function's signature.
Eg:
T foo(T argList); // Prototype
// Declaration of pointer to function foo
T (*ptrfoo) (T argList);
/*
ptrfoo is a pointer to the function that takes
arguments argList of type T, and returns value of type
T
*/
[49] The reason behind having a pair of parentheses around *ptrfoo: i. Without
parentheses, the function would return a pointer to type T.
T (*ptrfoo) (T argList); // Pointer to a function
T *ptrfoo (T argList); // Returns a pointer of type T
ii. Parentheses have higher precedence than *.
[50] A function's prototype that accepts the address of the function would look
like the following.
T first (U); // Prototype of the first function.
T (*pf) (U); // pointer to function first.
// Prototype of a function that invokes the first function.
// Uses the pointer to function.
V second (S arg, T (*pf) (U));
/*
Now for the calling part.
*/
// Assign pointer to address of the matching function
pf=first; // pf now points to first() function.
// Pass the first's address to the second function.
second('argument',pf); // function call
NOTE: (*pf) plays the same role as first. So, first(arg) is equivalent to
(*pf)(arg).[bingo!]
Step 3. Pointer to a function to invoke the function.
// NOTE: (*pf) plays the same role as first. So, first(arg) is equivalent to
// (*pf)(arg).
// --------------------------------------------------------------------------
first(arg); // pf is a pointer to the function, (*pf)() is the function.
(*pf)(arg); // both return the same result.
T first (U); // Prototype of the first function.
T (*pf) (U); // pointer to function first.
pf=first; // pf now points to first() function.
T varA=first(arg);
T varB=(*pf)(arg); // varA and varB both output the same result.
Variations on the Theme of Function Pointers
[recall]If you want a pointer pointing to a function, then replace the functionName
with (*ptrTo_functionName).
[51] In the function signature (argument list), T arg[] can be reduced to T [], and
can be reduced to T *.
// Prototypes, all of them perform the same function - return a
// pointer to type T.
T* fun_1(T arr[],int n); // is...
// equivalent to...
T* fun_2(T [],int); // is...
// equivalent to...
T* fun_3(T*,int):
[52] Now, to point a pointer to the functions above, see [recall].
T* (*ptrToFunction)(T*,int);
[53] Or you could use,autohttp://msdn.microsoft.com/en-IN/library/dd293667.aspx
herehttp://en.cppreference.com/w/cpp/language/auto or
herehttp://herbsutter.com/2013/06/07/gotw-92-solution-auto-variables-part-1/
auto ptrToFunction=fun_1;
/* Recall, the function name minus the
parentheses returns the address of the function.
So, ptrToFunction stores the address, hence becomes
a pointer to the function fun_1..*/
[54] In C++11, auto is a type 'deducer'. It sends directives to the compiler to deduce the
variable type from an initialisation expression. I believe in the previous
standards, auto was a storage class specifier[?].
T* pd;
auto x=pd; // x is now T*
auto* y=pd // y: T*
int g();
auto pf=g; // pf: pointer to function g
const auto& rf=g(); // rf:const int&
[55] auto cannot deduce array types.
T someArray[2];
auto anotherArray[2]=someArray; // Returns an error.
/*
someArray evaluates to a pointer, which does not match the array
type.
*/
[56] auto cannot be used in function parameters.
int someFunction(auto x=4.00);
// parameter cannot be declared 'auto'
[57] When you’re new to auto, the key thing to remember is that you really are declaring your own new local variable.
i.e., What is on the left is my new variable, and what is on the right is just its initial value:
[Source:http://herbsutter.com/2013/06/07/gotw-92-solution-auto-variables-part-1/]
auto my_new_variable = its_initial_value;
Another way to look at it is that whenever you see the keyword auto, it
means that you take the type on the RHS, but strip of top-level
const/volatile.
int val=10;
auto a=val; // a: int
auto& b=val;// b: int&
const auto c=val; // const int
const auto& d=val; // const int&
// -----------------------------
// Remember to remove the top level const or & or && to obtain
// the basic type.
int& ir=val;
auto e=ir; // e: int, ir is just another name for val.
int* ip=&val; // ip is a pointer to int.
auto f=ip; // f is a int*.
const int ci=val;
auto g=ci; // g is int, and not const int. g is a separate variable.
const int& cir=val;
auto h=cir; // h is int.
const int* cip= &val;
auto i=val; // i is const int*
// Above is not a top level const.
// cip is not const, rather *cip is const. (In other words, it is a
// pointer to const int.)
int* const ipc=&val; // const pointer to int
auto j=ipc; // j:int*, and not const.
const int* const cipc=&val; // const pointer to const int.
auto k=cipc; // k:const int*