Parameters - itzjac/cpplearning GitHub Wiki

Parameters

Given a function swap, that performs a swap operation between two variables (doesn't matter the type of the variable, here is implemented with strings).

#include  void swap(std::string a, std::string b)
{
    std::string c = a;
    a = b;
    b = c;
}

int main()
{
    std::string a = "first";
    std::string b = "second";
    swap(a, b);
    std::cout<< a << std::endl << b << std::endl;
    return 0;
}

Regardless the swap operation performed on the variables, the output of this program is:

first
second

The reason why variables don't change their values as expected lies on the mechanism in which the function is called, this is parameters passed by value.

When the parameters by value are used, the variables passed to the function are not the real variables but a copy of them. The variable's copy exist only inside the swap function, i.e. the arguments of the function, all changes made to that copy will be local to the function. When the function terminates, all it's local variables go out of scope and are destroyed. The variables printed after the function call, still preserve their initialized values: no changes made to the original variables were performed.

Parameters by reference

With the previous swap function, the question is: how can we change the function mechanism so that real variables are permanently modified? The mechanism which enables this behavior is when calling function parameters by reference, it will be necessary to change the signature using the

address-of operator &

With the addition of the operator on functions parameters

void swap(std::string& a, std::string& b)
{
    std::string c = a;
    a = b;
    b = c;
}

This change will produce the expected output$ second first Means that no more copies of variables are used in the scope of the function, but the references to the original variables.

Arrays as parameters

Understanding the two different mechanism for function calls will be important once we start introducing pointers, but it is important to stress the

difference when calling a function which parameter is an array.

#include const unsigned MaxSize = 5;
void changeArray(int const a[], int size)
{
    for (int i = 0; i < size ; ++i)
    {
        a[i] = 0;
    }
}

void printArray(int const a[], int length)
{	
for (int i = 0; i < size ; ++i)
{
	std::cout<<a[i]<<" ";
    }
}

int main()
{
int a[MaxSize] = {2, 1, 4, 6, 7};

changeArray(a, MaxSize);
    printArray(a, MaxSize);
return 0;
} 

An array of size 5 is declared in main with the values {2, 1, 4, 6, 7}, the function changeArray receives an array and the size of the array as its parameters.

Then it changes all the values to 0. The out put oft his program

$0 0 0 0 0

For the function changeArray, we are not specifying a parameter using the & operator, but because the parameter is of an array type, the contents of the original

will be changed. We can think of it now, that a function parameter of the kind of an array will by default use a reference to the original array, allowing

to make permanent changes.

Depending on the context of the problem, it can be necessary or not to allow a function to change the content fo the array. Let's assume that we want to preserve the original array a, copy its content to a local array b and perform the necessary changes on b, without modifying the original array.

This will simply be achieved by appending to the parameter function the const keyword, the a array passed into the function will be read-only.

#include <iostream>

const unsigned MaxSize = 5;

void copyArray(int const a[])
{
int b[size];

for (int i = 0; i < MaxSize; ++i)
{
	b[i] = a[i];
}
    // do whatever you like to the local array b
}

void printArray(int const a[], int length)
{	
for (int i = 0; i < size ; ++i)
{
	std::cout<< a[i] << " ";
    }
    std::cout<<std::endl;
}

int main()
{
    int a[MaxSize] = {2, 1, 4, 6, 7};
    printArray(a);
    copyArray(a);
    std::cout<<"After calling copyArray"<<std::endl;
    printArray(a, size);
return 0;
} 

Output:

$2 1 4 6 7
After calling changeArray2
$2 1 4 6 7

Which demonstrates how the original array is preserved.

EXERCISES:

Using the above program and using arrays of integer, declare and implement a function called sort.

  • Declare and implement a function called sort

  • The sort function, will sort the elements on an ascending order .

  • Inside the changeArray function call the sort using the b array.

    void changeArray(int const a[]) { int b[size];

    for (int i = 0; i < size ; ++i) { b[i] = a[i]; } // TODO: HW call a function sort and print the sorted array }

Variable Scope

We mentioned frequently the word scope, to refer to the lifetime of a variable after its being declared.

We can categorize two type variable scopes:

  • Local
  • Global

The local type refers to a variable declared inside a function, including main. Remember that local variables without initialization contain undefined data.

A local variable can only be accessed within the function, for the rest of the function the variable is out of scope.

int f()
{
    int a;
    return a*a;
}
int g()
{
    int a;
    return a + a;
}
int main()
{
    f();
    g();
    return 0;
}

Function f & g, contain a local variable called a, because the variable is only local to each function there is no name conflict: each a is a different variable inside

the scope of the function. This program will print undefined values, because local variables are never initialized.

Global variables refer to a variables declared outside of any function, they are commonly created next to the headers. Let's

declare a global variable for the last code

int globalA;
int f()
{
    int a;
    return a*a;
}
int g()
{
    int a;
    return a + a;
}
int main()
{
    f();
    g();
    return 0;
}

Global variables behave different than the local ones.

  • Global variables are default initialized by the compiler
  • They are accessible every where in the program

Global variables can be very convenient to declare, but be aware that deliberately creating global variables tends can

potentially produce spaghetti code (at least super hard to debug), breaks modularity and pollutes the global space.

Whenever you find yourself declaring a global variable, step back and think of there is another way to solve the problem.

Notice how we can easily pollute the global space by changing the name of the global variable to a

int a;
int f()
{
    int a;
    return a*a;
}
int g()
{
    int a;
    return a + a;
}
int main()
{
    f();
    g();
    return 0;
}

The compiler is not very explicit but the program is more complicated to read, it is not clear which version of a is used.

But even more dangerous, if you later write another module which uses another global variable called a, then the compiler

will complain, good luck trying to figure out the error!

This kind of error can be easily avoiding by promoting modularity on your programs.

Changing scope

For the previous example, it is still possible to explicitly specify which version of the a variable is meant to be used.

The program above contains only three scopes: local scope of f, local scope of g and the global scope.

To change the scope you use the scope operator ::. Let's change the f() function to use only the global a.

int f()
{
    int a;
    return ::a*::a;
}

The appended :: operator now forces to use the global variable instead of the local a, this function will return 0 (remember

that global variables are default initialized).

Main parameters

So far the main function signature has been declared in one of its simplest forms:

int main()
{
    return 0;
}

main can be expressed in many different ways depending on the compiler.

The most complete version of the main function is one that provides a way to communicate running options for our program.

For instance, the program tail reads a file from the end and prints the number of lines specified in the command line.

To print the last two lines of the file outfile.txt

$tail -2 outfile.txt

he program tail, then receives two parameters: the first that specifies the number of lines to be printed from the end and the second which is the

name of the file to be used.

Any of the programs we have written so far, enable this capability. To do so we must declare the main in a more complete version, which

by the way, is portable across any OS

int main(int argc, char *argv[])
{
    return 0;
}

The names of the parameters are of no importance but is a recommended practice to use those name to refer to

  • arguments counter
  • arguments values

By simply executing the program, the parameters will always contain a value which refers to the name of the program.

So by default execution, argc = 1 and argv[0] = "name of the program".

Consider this program is called mainparams

#include <iostream>

int main(int argc, char *argv[])
{
    std::cout<<"Default execution contains an argument count: \n"<< argc << "One argument value: " << argv[0]<< std::endl;
    return 0;
}

Output:

$./mainparams
Default execution contains an argument count: 1
One argument value: ./mainparams

Generally with this version of main, you will want to execute the program with more than 1 argument count, let's create a program that displays as many arguments as are passed in command line.

#include <iostream>

int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; i++)
    {
        std::cout<<"Argument: "<< i << " -> with value: " << argv[i]<< std::endl;
    }
    return 0;
}

If called with six program arguments:

$./mainparams first second third 1 2 3
Argument: 0 -> with value: ./mainparams
Argument: 1 -> with value: first
Argument: 2 -> with value: second
Argument: 3 -> with value: third
Argument: 4 -> with value: 1
Argument: 5 -> with value: 2
Argument: 6 -> with value: 3

There is no limit on the number of values that can be passed to the program, compare with the g++ compiler options and how many arguments you have used so far!

EXERCISE

  1. Create a program that prints the following patterns
  • Executing the program without arguments will print the size of the triangles as above
  • The program can accept one argument that represents the size of the triangle (consider only unsigned positive integer as valid inputs).
  1. Implement the reverse Polish notation calculator as in Day 4, use command line arguments to pass the operands

Files

Reading one word at a time from a text file

#include <iostream>
#include <string>

int main(int argc, char *argv[])
{
    // opening a text file
std::string filename = "sample.txt";
    std::ifstream file(filename, std::ifstream::in);
    
if (!file.is_open())
{
        std::cerr<<"The file [" << filename.c_str()<< "] couldn't be opened"<> word;
	std::cout<
    }
    return 0;
}

Reading a text file one line at a time

#include
#include
#include
int main(int argc, char *argv[])
{
    // opening a text file
    std::string filename = "sample.txt";
    std::ifstream file(filename, std::ifstream::in);
    if (!file.is_open())
    {
        std::cerr<<"The file [" << filename.c_str()<< "] couldn't be opened"<

Write a file

For each of the two previous exercises, reading one word and one line at a time, create a new file and copy the contents of the read only file (sample.txt) into it.

Read/Write using binary mode

EXERCISES

#include 
#include 

const unsigned MaxPlayers = 10;
const unsigned BuffSize = 20;
const unsigned LineBuffSize = 40;

struct Player
{
  char name[BuffSize];
  int score;
};

bool LoadDataFromFile(const char *filename, struct Player *p, int max_players, int &num_players);
bool WriteDataToFile(const char *filename, struct Player *p, int num_players);
void BubbleSort(struct Player *p, int max_players);

int main()
{
    struct Player players[MaxPlayers];
    int num_players = 0;
    LoadDataFromFile("datos.txt", players, MaxPlayers, num_players);
    std::cout << "Demo for players" << std::endl;
    std::cout<<"Total number of players: "score = atoi(buff);
    {
        p++;
        num_players++;
    }

    fileStr.close();
    return true;
}

void BubbleSort(struct Player *p, int max_players)
{
    for(int i = 0; i < max_players-1; i++)
        for(int j = 0; j < max_players-1; j++)
        {
            if(p[j].score > p[j+1].score)
            {
                struct Player p1;
                p1 = p[j+1];
                p[j+1] = p[j];
                p[j] = p1;
            }
        }
}

bool WriteDataToFile(const char *filename, struct Player *p, int num_players)
{
    // TODO:
}

Macro processor

  • #ifdef #else #endif

  • #ifndef

  • #define

  • #undef

  • FILE

  • LINE

  • __cplusplus

  • ASSEMBLER

  • #error A c++ compiler is required!

  • Selective compilation: NDEBUG, WIN32, GCC
  • Avoiding multiple file inclusion
  • Define as a constant
  • Write to file file and line

BMP example, download here.

Pointers

Binky and pointers

Sorting

Bubble sort

Selection sort

Insert sort

http://cs.smith.edu/~thiebaut/java/sort/demo.html

Big O, Omega table

EXERCISES

  1. Given the following code, implement the function WriteDataToFile to store the sorted scores. Use as the input file data.txt
⚠️ **GitHub.com Fallback** ⚠️