Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Parameter Passing: by Train vs. by Truck

3.09/5 (11 votes)
19 Jul 2010CPOL4 min read 54.2K  
An article highlighting the advantages of using a composite datatype for passing parameters to functions.

Introduction

This article describes the advantages and disadvantages of the form or format that is used for passing parameters to functions. The useful technique described in this article can be used in languages like C, C++, as well as in any language which supports pointers and composite data-types like struct.

Ways of parameter passing to a function

Conceptually, parameters can be passed to a function in two ways, including the combination of these two:

  1. by State: This way of parameter passing specifies whether the modifications made to parameters by the called function will be available to the caller of the function. Thus, this manner of parameter passing focuses on the state\ value of parameters, before and after the function call.
  2. Under this, there are different actual techniques\ methods to pass parameters to a function:

    • by Value: Modification made to parameters by the called function cannot be seen by the caller of the function.
    • C++
      //------ called function ------//
      int func(int P1, char P2)
      {
          P1 = 20;  //...tried to modify
          P2 = 'b'; //...tried to modify
          
          return 1;
      }
      //------ called function ------//
      
      
      //-------- caller code --------//
      //original data
      int Data_P1 = 10;
      char Data_P2 = 'a';
      
      func(Data_P1, Data_P2);
      
      //Data_P1 = 10 ...not modified, since passed by Value
      cout << "Data_P1 = " << Data_P1 << endl;
      //Data_P2 = a  ...not modified, since passed by Value
      cout << "Data_P2 = " << Data_P2 << endl;
      //-------- caller code --------//
    • by Reference: Modification made to the parameters by the called function can be seen by the caller of the function.
    • C++
      //------ called function ------//
      int func(int &P1, char *P2)
      {
          P1 = 20;   //...tried to modify
          *P2 = 'b'; //...tried to modify
          
          return 1;
      }
      //------ called function ------//
      
      //-------- caller code --------//
      //original data
      int Data_P1 = 10;
      char Data_P2 = 'a';
      
      func(Data_P1, &Data_P2);
      
      //Data_P1 = 20 ...modified, since passed by Reference
      cout << "Data_P1 = " << Data_P1 << endl;
      //Data_P2 = b  ...modified, since passed by Reference
      cout << "Data_P2 = " << Data_P2 << endl;
      //-------- caller code --------//
    • There are other techniques as well like by Name etc., the description of which can be easily found on the Internet.
  3. by Format: This way of passing parameters, by Format, is not something new. In fact, it is used in routine development. This way of parameter passing specifies whether the parameters are passed to a function individually (horizontally) or collectively (vertically).
  4. Therefore, the word Format in the current discussion can be horizontally or vertically.

    Under this, there are different actual techniques\methods to pass parameters to a function:

    • by Train: Passing parameters individually (horizontally) or in horizontal format can be imagined as if parameters are passed in the form of a train, since a train carries its load (coaches\ bogies) one after the other in horizontal format.
    • by Truck: Passing parameters collectively (vertically) or in vertical format can be imagined as if parameters are passed in the form of a truck, since a truck carries its load by keeping things one above the other in vertical format.

Let's see this "by Format" parameters passing method in detail with its pros and cons.

Parameter passing "by train"

parapass_1.png

C++
int func(int P1, float P2, abc P3, char P4, xyz P5);

As can be seen from the image, there are a number of parameters that are passed to the function func one after the other, horizontally in the form of a Train, and hence the name "parameter passing by train".

Disadvantages

  1. When a parameter in the function parameter list gets added, deleted, or modified, the function signature gets modified.
    1. If parameter P1's data-type gets modified from int to float, the function signature gets modified.
    2. C++
      int func(/*int*/ float P1, float P2, abc P3, char P4, xyz P5);
      //...signature is modified
    3. If parameter P2 gets deleted, the function signature gets modified.
    4. C++
      int func(int P1, /*float P2,*/ abc P3, char P4, xyz P5);
      //...signature is modified
    5. If a new parameter P6 gets added to the parameter list, the function signature gets modified
    6. C++
      int func(int P1, float P2, abc P3, char P4, xyz P5, pqr P6);
      //...signature is modified

Advantages

  1. It is easy to pass some parameters by Value and some parameters by Reference.
  2. C++
    //...passing P1 by Value
    //...passing P2 by Reference
    //...passing P3 by Value
    //...passing P4 by Reference
    //...passing P5 by Value
    
    int func(int P1, float &P2, abc P3, char *P4, xyz P5);

Parameter passing "by truck"

parapass_2.png

C++
int func(PARAMETERTRUCK stParam);

//where PARAMETERTRUCK is defined as :
struct PARAMETERTRUCK
{
    int P1;
    float P2;
    abc P3;
    char P4;
    xyz P5;
};

As can be seen from the image, only one (composite\ container-like) parameter is passed to the function func. This passed parameter can be considered as a Truck which loads all the parameters in it, hence the name "parameter passing by truck".

Advantages

  1. Even if a parameter in the function parameter list gets added, deleted, or modified, the function signature does not get modified.
    1. The parameter P1's data-type gets modified from int to float, but the function signature does not get modified.
    2. C++
      struct PARAMETERTRUCK
      {
          /*int*/ float P1;
          float P2;
          abc P3;
          char P4;
          xyz P5;
      };
      
      int func(PARAMETERTRUCK stParam); //...signature not modified
    3. The parameter P2 gets deleted, but the function signature does not get modified.
    4. C++
      struct PARAMETERTRUCK
      {
          int P1;
          /*float P2;*/
          abc P3;
          char P4;
          xyz P5;
      };
      
      int func(PARAMETERTRUCK stParam); //...signature not modified
    5. A new parameter P6 gets added to the parameter list, but the function signature does not get modified.
    6. C++
      struct PARAMETERTRUCK
      {
          int P1;
          float P2;
          abc P3;
          char P4;
          xyz P5;
          pqr P6;
      };
      
      int func(PARAMETERTRUCK stParam); //...signature not modified

Disadvantages

  1. All of the parameters can be passed either by Value or by Reference.
  2. C++
    int func(PARAMETERTRUCK stParam);  //by Value
    int func(PARAMETERTRUCK &stParam); //by Reference
    int func(PARAMETERTRUCK *stParam); //by Reference

We can overcome this disadvantage and allow the mixture of passing some parameters by Value and some parameters by Reference.

Let's see how:

C++
//------ called function ------//
//Define the Parameter Truck as follows :
struct PARAMETERTRUCK
{
    int   P1;  //...parameter that needs to be passed by value
    char *P2;  //...use pointer for parameter
               //   that needs to be passed by Reference
};

//... always passing Parameter Truck "by Value".
//... still preventing the function signature from getting modified.
int func(PARAMETERTRUCK stParam) 
{
    stParam.P1 = 20;   //...tried to modify
    *stParam.P2 = 'b'; //...tried to modify
    
    return 1;
}
//------ called function ------//


//-------- caller code --------//
//original data
int Data_P1 = 10;
char Data_P2 = 'a';

PARAMETERTRUCK stData;
stData.P1 = Data_P1;    //...pass by Value
stData.P2 = &(Data_P2); //...pass by Reference

func(stData);

//continue using original data some of which 
//are modified in function func as per requirement

//Data_P1 = 10 ...not modified, since passed by Value
cout << "Data_P1 = " << Data_P1 << endl;
//Data_P2 = b  ...modified, since passed by Reference
cout << "Data_P2 = " << Data_P2 << endl;
//-------- caller code --------//

Conclusion

By using the technique of passing parameters by truck, we can prevent the signature of a function from getting modified when its parameter list gets modified. At the same time, we can pass some parameters by Value and some parameters by Reference through the use of pointers.

Therefore, use a horizontal list of parameters the least, and pass parameters using a struct, i.e., "Truck".

There is one strong reason to support the above line. Since, good programming practices (Single Responsibility Principle) suggest that an entity ("function" in our case) should be responsible for perform only one task\ cohesive functionality, all the parameters that will be received by a function should participate or contribute towards the goal\ functionality which the function is trying to provide. Therefore, it is better to pass all the parameters under one name\ goal.

History

  • Version 1.0: Initial uploading of article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)