Last Updated:

C++ Operators |  Examples | Program Structure

C++ Operators

Operators control the execution of the program. The C++ statement set contains all the control constructs of structured programming.

The composite operator is limited to curly braces. All other statements end with semicolons.

  • Empty operator – ;

    An empty operator is an operator consisting only of a semicolon. It can appear anywhere in the program where the syntax requires an operator. Executing an empty statement does not change the state of the program.

  • The composite operator is {...}

    The action of a composite operator is to execute the statements it contains sequentially, unless an operator explicitly transfers control elsewhere in the program.

  • Exception
    handling operator try { <operators> } catch (<exclusion declaration>) { <operators> } catch (<exclusion announcement>) { <operators> } ... catch (<exclusion declaration>) { <operators> }
  • conditional statement
    if (<expression>) <operator 1> [else <operator 2>]

  • switch operator (<expression>) { case <constant expression 1>: <operators 1> case <constant expression 2>: <operators 2> ... case <constant expression N>: <operators N> [default: <operators>] } }

    The switch operator is designed to select one of several alternative ways to execute a program. The calculation of the switch operator begins with the evaluation of the expression, after which control is transferred to the operator marked with a constant expression equal to the calculated value of the expression. The output from the switch operator is performed by the break operator. If the value of the expression is not equal to any constant expression, control is passed to the operator marked with the default keyword, if any.

  • A loop operator with a while precondition
    (<expression>) <operator>
  • Loop operator with the postcondition
    do <operator> while <expression>;

    In C++, this operator differs from the classic implementation of a loop with the postcondition that when an expression is true, the loop continues to run, rather than leaving the loop.

  • Step-by-step for statement
    ([<start expression>]; [<word>]; [<expression of increment>]) < operator>

    The body of the for operator is executed until the conditional expression becomes false (equal to 0). The initial expression and the increment expression are typically used to initialize and modify loop parameters and other values. The initial expression is evaluated once before the conditional expression is first validated, and the increment expression is evaluated after each execution of the statement. Any of the three expressions of the loop header, and even all three, can be omitted (don't forget to just leave semicolons). If a conditional expression is omitted, it is considered true, and the cycle becomes infinite.

    The step-by-step loop operator in C++ is a flexible and convenient construct, so the loop operator with the while precondition is rarely used in C++, because in most cases it is more convenient to use the for operator.

  • The loop operator for the
    range for (<gener><massive><operator>

    The loop operator for a range provides a way to iterate through an array (or other structure). Each element of the array is iterated through, and the value of the current element in the array is assigned to the variable declared as an element. To improve performance, the declared element must be of the same type as the elements in the array. You can use automatic type inference. The element can be changed, but it does not affect the array. In order to change the array, the element must be declared as a reference. In the loop, all elements of the array are processed.

  • Break
    break operator
    ;

    The break operator interrupts the execution of the whiledofor, and switch statements. It can only be contained in the body of these operators. Control is passed to the program operator following the interrupted. If a break operator is written inside nested whiledoforswitch statements, it completes only the statement that covers it directly.

  • Continue Statement
    ;

    The continuation statement passes control to the next iteration in the whiledofor loop statements. It can only be contained in the body of these operators. In the do and while operators, the next iteration begins by evaluating the conditional expression. In the for operator, the next iteration begins by evaluating the increment expression, and then the conditional expression is evaluated.

  • Return return
    operator [<expression>];

    The return operator finishes executing the function in which it is contained and returns control to the calling function. Control is passed to the point of the calling function immediately following the call statement. The value of the expression, if specified, is evaluated, cast to the type declared for the function that contains the return operator, and returned to the calling function. If the expression is omitted, the value returned by the function is undefined.

    From a formal point of view, the breakcontinue, and return operators are not structured programming operators. However, their use in limited quantities is justified when they simplify the understanding of the program and allow you to avoid large nested structures. For example, we check the input data for anomalies. If you do not use these operators, then all the processing will have to be invested in a conditional block, which worsens the readability of the program. Instead, you can write a small conditional block that organizes the exit from the function if the source data is incorrect.

I/O is not part of the C++ language, but is performed by functions that are part of the standard library. For more information, see Lecture 4.

2. Program Structure

A C++ program consists of preprocessor directives, compiler instructions, variable and/or constant declarations, declarations, and function definitions.

2.1. Declaring a Variable

Declaring a variable specifies the name and attributes of the variable. The attributes of a variable can be type, number of elements (for arrays), memory class specification, and initializerAn initializer is a constant of the appropriate type that specifies the value that is assigned to a variable when it is created.

Declaring a variable has the following syntax: [<commission class specification>] <type> <name> [= <initializer>] [,<name> [= <initializer>] ...];

Examples of declaring variables

int x;Declaring an Integer Variable Without an Initializer double y = exp(1);A real-type variable is initialized with the number e. int a, b = 0;Declaring two variables of an integer type. The variable is initialized with a value of 0. b

In C++, there is no limit to the number of characters in a name. However, some parts of the implementation (in particular, the linker) are not available to the compiler author, and they sometimes impose such restrictions.

2.1.1. Constants

C++ introduced the concept of user-defined constants to indicate that a value cannot be changed directly. This can be helpful in several ways. For example, many objects do not change after initialization; the use of symbolic constants leads to a more convenient code maintenance than the use of literals directly in the text of the program; Pointers are often used for read-only, but not for writing. most function parameters are readable but not overwritten.

To declare an object as a constant, you must add the const keyword to the declaration. Because a constant cannot be assigned values, it must be initialized.

const int a = 100; const int b[] = {1, 2, 3, 4, 5}; const int c;
a is a constant All b[i] are constants Error – no initializer!

It is typical to use constants as the size of arrays and labels in a case statement.

Note that const modifies the type, i.e. restricts the possible use of the object, but does not specify how the constant object is placed. A simple and typical use of a constant is when the value of the constant is known at compile time and no memory allocation is required for it. An array of constants typically requires memory allocation because, in general, the compiler is unable to determine which element of the array is being accessed in the expression.

2.1.2. Declaring typedef

A declaration that begins with the typedef keyword introduces a new name for the type, not for a variable of that type. The purpose of such an ad is often to assign a short synonym to a commonly used type. For example, if you use unsigned char frequently, you can enter the synonym uchar.

typedef unsigned char uchar; Now uchar is synonymous for unsigned char

Names entered using typedef are synonyms, not new types. Therefore, older types can be used in conjunction with their synonyms. If you need different types with the same semantics or with the same representation, refer to enumerations or classes.

2.2. Declaring and Defining a Function

A function declaration specifies the name of the function, the type of return value, and the number and types of parameters that must be present when the function is called. Specifying void as the return value means that the function returns no value.

A function definition is a function declaration in which the function body is present. A function definition has the following syntax:
<type> <name> (<list of formal parameters>) { [<ad>] [<operators>] }

The types in the function definition and declarations must match. However, parameter names are not part of the type and do not have to match.

All functions in the program exist at the global level and cannot be nested within each other.

Among the functions, one main function stands out, which must be named main. It begins the execution of the program, usually it controls the execution of the program, organizing calls to other functions. In order for a program to be compiled and executed, it must contain at least a definition of the main function.

Examples of function definitions

double Cube(double x); void main() { printf("%lf\n", Cube(5)); } double Cube(double x) { return x * x * x; } Declaration (prototype) of the Main Function of a program that prints 53 A single real argument function that returns a real value equal to an argument cube

When a function is called, memory is allocated for its formal parameters, and each formal parameter is assigned a value of the corresponding actual parameter. The semantics of passing parameters is identical to the semantics of initialization. In particular, the types of formal and actual parameters are checked and, if necessary, either standard or user-defined type conversions are performed. There are special rules for passing arrays as parameters, a means for passing parameters for which type affinity is not checked, and a means for specifying default parameters.

A function should return a value unless it is declared as a void. Conversely, a value cannot be returned from a function if it is declared as a void. Like passing parameters, the semantics of returning a value from a function are identical to initialization semantics.

The return value is specified by the return statement.

int  f1() { } void f2() { } Error – the value // is not returned correctly int f3() { return 1; } void f4() { return 1; } Correct // Error – the value is returned in the void function int  f5() { return; } void f6() { return; } Error - Return value not specified // Correct

A function of type void cannot return a value. However, calling a function of type void does not produce a value, so a function of type void can use a call to a function of type void as an expression in a return statement.

void g() { ... } void h() { return g(); } Correct

This form of return statement is important when writing function templates when the return type is a template parameter.

2.2.1. Embedded Functions

The function can be defined with the inline specifier. These functions are called embedded functions. The inline specifier instructs the compiler that an open substitution of the function body is preferable to a normal implementation of a function call, and that it should attempt to generate code at the point of the call that corresponds to the embedded function each time, rather than generating the function code separately (once) and then calling it through the normal invocation mechanism. The inline specifier has no effect on the meaning of the function call.

inline int max(int x, int y) { return x > y ? x : y; }

Open lookup does not affect the results of a function call, which is different from macro substitution. The embedded function has the usual syntax for describing the function and obeys all rules regarding scope and type control. An open lookup is simply a different implementation of a function call. Instead of generating code that passes control and parameters to a single instance of the function body, a copy of the function body, appropriately modified, is placed in the place of the call. This saves time for handover control.

For all but the simplest functions, function execution time dominates the time spent on call servicing. From this it follows that for all, except the simplest, functions, savings due to open substitution are minimal. The ideal candidate for open lookup is a function that makes something simple, like increasing or returning a value. The existence of such functions is caused by the need to process hidden data.

2.2.2. Default Feature Settings

 

In the C++ language, you can set the so-called default function parameters. If an expression is specified in the declaration of a formal parameter, it is perceived as a default of this parameter. All subsequent parameters should also have omissions. Parameter omissions are substituted into the function call if the last parameters in the list are not in it.

 

int g(int m = 1, int n); Error int h(int m = 1, int n = 2); int h(int m = 1, int n = 2) { ... } int h(int m = 0, int n = 0) { ... } Correct // Error – repeat default parameters // Error – change default parameters int f(int m = 1, int n = 2); int f(int m /* = 1 */, int n /* = 2 */) { ... } f(5, 6); f(5); f();  Correct // Function call with two parameters // Equivalent to call f(5, 2); Equivalent to the call f(1, 2); 

2.2.3. Program Settings

 

The main function, like any other function, can have parameters. These parameters are passed to the program from the command line.

void main(int argc, char *argv[]) { ... } Names and are not a language requirement argcargv

The first parameter contains the number of elements in the array, the second parameter, which is an array of line pointers. Each line stores one parameter passed to the program, and the first parameter (with an index of 0) contains the name of the executable file and always exists. The order in which parameters are declared is significant.

2.2.4. Functions with a variable number of parameters

In the C++ language, it is possible to use functions with a variable number of parameters. To declare such a function, you must specify an ellipsis (,...) at the end of the list of function parameters. No special action is required to call such a function, it is just set as many parameters as necessary.

When interpreting a list of parameters, such a function uses information that is not available to the compiler. Therefore, it is not able to guarantee that the expected parameters are actually present or that they have the correct types. Clearly, if a parameter has not been declared, the compiler does not have the information necessary to perform standard type checking and conversions.

In principle, you can define a function only with undeclared parameters, but it will be difficult to select parameters, because macro definitions for working with undeclared parameters use the name of the last declared formal parameter.

Inside the function, the programmer himself is responsible for selecting additional parameters from the stack. To work with them, you use macro definitions va_argva_start and va_end defined in the stdarg.h file.

For an example of a program with a function with a variable number of parameters, see the end of the lecture.

2.3. Preprocessor

A preprocessor is a program that processes the text of your program before the compiler. Thus, the input of the compiler gets text that may differ from the one you see. The work of the preprocessor is controlled by directives. With the help of a preprocessor, you can perform the following operations:

  • inclusion of texts from specified files in the program;
  • replacing identifiers with sequences of characters;
  • macro substitution, i.e. replacement of the designation with parameterized text generated by the preprocessor taking into account specific arguments;
  • exclusion of individual parts of the text from the program (conditional compilation).

2.3.1. Including Files

The files are included by using the #include directive, which has the following syntax:
#include <path> #include "path"

Angle brackets are an element of syntax here.

The #include directive includes the contents of the file whose path is specified in the compiled file instead of the line with the directive. If the path is enclosed in angle brackets, then the file is searched in standard directories. If the path is enclosed in quotation marks and specified completely, then the file is searched in the specified directory, and if the path is not completely specified - in the current directory. With this directive you can include both standard and your own files in the program text.

For example, you can place named constant definitions and macro definitions that are common to multiple source files in an included file. Included files are also used to store declarations of external variables and abstract data types shared by multiple source files. For more information on using header files, see Lecture 9.

In addition, as mentioned above, in the C++ language, a number of functions, such as I/O functions, dynamic memory allocation, etc., are not an element of the language, but are included in standard libraries. In order to use the functions of standard libraries, it is necessary to include so-called header files in the text of the program (the description of each function indicates which header file is necessary for it). This is also done using the preprocessor directive #include.

directive #include may be attached. This means that it can occur in a file included by another #include directive. The level of nesting of #include directives depends on the compiler implementation.

2.3.2. Macro Substitutions

Macro substitutions are implemented by the #define directive, which has the following syntax:
#define <IDENTER> <Text> #define <IDENTER>> <><Text>

The #define directive replaces all occurrences of the identifier in the source file with the text that follows the identifier in the directive. This process is called macro substitution. The identifier is replaced only if it is a separate token. For example, if the id is part of a string or longer identifier, it is not replaced.

Text is a set of tokens, such as keywords, constants, identifiers, or an expression. One or more space characters must separate the text from the identifier (or from the bracketed parameters). If the text does not fit on the line, then it can be continued on the next line, to do this, you should type the character "reverse slash" at the end of the line and immediately press the "ENTER" key.

The text may be omitted. In this case, all instances of the identifier will be removed from the source code of the program. However, the identifier itself is treated as defined.

The parameter list, if specified, contains one or more identifiers separated by commas and must be enclosed in parentheses. The identifiers in the list must be different. Their scope is limited by the macro definition in which they are defined. The names of the formal parameters in the text mark the positions in which the actual macro call arguments should be substituted.

In a macro call, the identifier is followed by a list of actual arguments that correspond to the formal parameters in the parameter list in parentheses. Lists of actual and formal parameters must contain the same number of elements. Do not confuse the substitution of arguments in a macro definition with the passing of the arguments to the function. The substitution in the preprocessor is purely textual. No calculations or type conversions are performed.

After the macro substitution is performed, the resulting string is re-examined to look for other macro names. When you re-view, the name of the macro substitution that you made earlier is not considered. Therefore, the #define x x directive will not loop the preprocessor.

Examples

#define N 100 #define MULT(a, b) ((a) * (b)) #define MAX(x, y) ((x) > (y)) ? (x) : (y)

The MULT(x + y, z) call will be replaced with ((x + y) * (z))). In the absence of internal brackets, you would get (x + y * z), which is incorrect.

Will the MAX(i, a[i++]) macro call be replaced by ((i) > (a[i++])) ? (i) : (a[i++])). The result of the calculation is unpredictable.

In the directive #define, two lexemes can be "glued together". To do this, they need to be combined with ## characters (space characters are allowed on the left and right). The preprocessor combines such tokens into one. For example, the macro definition #define VAR(i, j) i ## j  when the macro call VAR(x, 6) generates an identifier x6.

The # character placed before the macro argument indicates that it must be converted to a character string. In a macro call, the #<formal parameter> construct is replaced by "<faction parameter>".

Replacements in the text can be undone by the #undef directive, which has the following syntax:
#undef <IDENTer>

The #undef directive overrides the current #define definition for the identifier. To cancel a macro definition, simply set its identifier. You do not need to specify a list of parameters. It is not an error to apply the #undef directive to an identifier that has not previously been defined or whose action has already been revoked.

The form of macros adopted in C/C++ is a serious drawback of the language. This form can now be considered obsolete due to the availability of more appropriate language tools, such as templates, namespaces, embeddable functions, and constants. Similarly, the widespread use of type conversions in any language signals poor design. Both macros and castes are frequent sources of error. The fact that you can do without them makes C++ programming much more secure and elegant.

2.3.3. Conditional Compilation

Conditional compilation is provided in the C++ language by a set of instructions that essentially control preprocessing rather than compilation. These directives allow you to exclude any part of the source file from the compilation process by checking the conditions.
#if <manificated constant expression> [<text>] [#elif <determinated constant expression> [<text>]] ... [#else [<text>]] #endif

Each directive #if in the same source file must correspond to its final directive #endif. Between #if and #endif directives, an arbitrary number of directives #elif and no more than one #else directive is allowed. If the directive is #else present, there should be no other directives #elif between it and the #endif directive at a given nesting level.

The preprocessor selects a section of text to process based on the calculation of the constant expression that follows each #if directive and #elif. The text that follows the constant expression with the value "true" is selected. If no bounded constant expression is true, the preprocessor selects the text that follows the #else directive. If there is no directive #else, no text is selected.

A constant expression can contain a defined(<IDent> preprocessor operation). This operation returns a true value if the specified identifier is currently defined, otherwise the expression is false.

#if (sizeof(void *) == 2) #define SDATA #else #define LDATA #endif #if defined(CREDIT) credit(); #elif defined(DEBIT) debit(); #else printerror(); #endif

3. Example

3.1. The program for finding the root of the equation f() = 0 on the segment [; ] with a given accuracy by dividing the segment in half xab

The first option is the usual way


#include <cstdio> // Include header files,
                                                            // containing I/O function prototypes
#include <math.h> // and math functions (for fabs)

void main()
 { double a, b, e, x, c, fa, fc; // Variable declarations
   intn;
   
   printf("Enter the bounds of the segment and the precision: "); // Prompt for the user
   scanf("%lf%lf%lf", &a, &b, &e); // Input of initial data
   for (n = 0; fabs(a - b) > e; n++) // Include the initialization of the variable n in the header of the for loop,
                                                            // increase it by 1, because it is definitely fulfilled
                                                            // at each step of the loop, and checking the loop condition
    { c = (a + b) / 2; // Because there must be more than one statement in the loop body,
      fa = f(a); // and according to the syntax, only one is possible,
      fc = f(c); // statements that make up the loop body,
      if (fa * fc < 0) // combined into one using operator brackets {...}
       b=c;
      else
       a = c;
     }
   x = (a + b) / 2;
   printf("The root of the equation = %lf\nNumber of iterations = %d\n", x, n);
  }

The second option is like on Pascal


#include <cstdio>
#include <math.h>

void main()
  { double a, b, e, x, c, fa, fc;
    intn;

    printf("Enter the bounds of the segment and the precision: ");
    scanf("%lf%lf%lf", &a, &b, &e);
    n = 0;
    while (fabs(a - b) > e)
     { c = (a + b) / 2;
       fa = f(a);
       fc = f(c);
       if(fa*fc < 0)
        b=c;
       else
        a = c;
       n++;
      }
    x = (a + b) / 2;
    printf("The root of the equation = %lf\nNumber of iterations = %d\n", x, n);
   }

The third option is that the entire algorithm is placed in the for loop header.

#include <cstdio>
#include <math.h>

void main()
  { double a, b, e, x, c, fa, fc;
    intn;

    printf("Enter the bounds of the segment and the precision: ");
    scanf("%lf%lf%lf", &a, &b, &e);
    for(n=0;
         fabs(a - b) > e;
         c = (a + b) / 2, fa = f(a), fc = f(c), fa * fc < 0 ? b = c : a = c, n++) ; // To combine multiple statements
                                                                                       // in the increment expression of the for loop
                                                                                       // used
                                                                                       // sequential calculation operation
    x = (a + b) / 2;
    printf("The root of the equation = %lf\nNumber of iterations = %d\n", x, n);
   }

3.2. A variable number of parameter functions similar to the printf function

 



#include <cstdio>
#include <stdarg.h>

void print(char *format, ...);

void main()
  { int a = 45, b = 87;
    double f = 2.75;

    print("dfd", a, f, b);
   }

void print(char * format, ...)
  {va_list list; // Variable for working with the list of arguments
    int n, i;
    double f;
   
    va_start(list, format); // Initialize pointer to argument list
    for (i = 0; format[i]; i++)
    switch(format[i])
     {case 'd':
         n = va_arg(list, int); // Select the next parameter
         printf("%d\n", n);
         break;
       case 'f':
         f = va_arg(list, double); // Select the next parameter
         printf("%lf\n", f);
         break;
      }
    va_end(list); // Reset pointer to argument list to NULL
   }