Aller au contenu

Variables

Type of variables

In C++, a program declares variables that are strongly typed, as in Java. However, type conversions tend to be more permissive and programmers should be very careful when manipulating variables of different types.

In the recent version of C++, the programmer can also use type inference, leaving the job of determining the type of the variables to the compiler, by using the auto keyword. In any case, types are known at compilation time and each variable declared in the program has a type that belongs to one of the built-in types or to classes defined in libraries or by the programmer.

More information about auto will be given in a following chapter.

example

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {7, 5, 16, 8};
    // Iterate over elements of the vector
    for (int n : v) {
        std::cout << n << " ";
    }
    std::cout << std::endl;
    // The auto keyword is used to interfere the type of n
    for (auto n : v) {
        std::cout << n << " ";
    }
    return 0;
}

Local Variables

In C++, local variables can be declared and used in functions or methods, as in C or Java.

In Java, local variables are allocated on the stack for primitive types (e.g. int or float) and on the heap for reference types (e.g. object of any class). This is illustrated in the example below:

// !!! JAVA code

// declares an initialized reference to a Point object on the heap
Point p1; 
// creates a new Point object and stores a reference to this object in p2
Point p2 = new Point(); 
...
// p3 stores another reference to the object referenced by p2,
// thus p3 and p2 reference the same object
Point p3 = p2;

This is a major difference as compared to C++, in which objects can be allocated both on the stack (without the new keyword) and on the heap (with the new keyword).

Allocation on the Stack or on the Heap

The stack is a special area of the memory which stores temporary variables. In the stack, variables are declared, stored and initialized during runtime. When the computing task is complete, the memory of the variable is automatically erased.

The heap is a pre-reserved area of computer memory. A program process can allocate a variable amount of memory from the heap and this memory has to be differentiated from the stack, data and bss section. On embedded systems, the heap is very limited in size. It is thus particularly important to monitor memory allocation on the heap so that NO out of memory error may ever occur. Whenever possible, it is even better NOT to use the heap and dynamically allocated memory.

In C++, no garbage collector exists since objects may own raw pointers to the memory heap. It is thus the programmer’s responsibility to free unneeded heap space and to call delete for each call to new. It is a good practice to reset the pointer value of a deallocated object to nullptr.

A memory leak is created when memory allocations are managed in such a way that memory which is NO longer needed is NOT released.

example

#include <iostream>
#include <string>

int main() {
    // Object created on the stack with default constructor
    std::string s1;
    std::cout << "String s1 : " << s1 << std::endl;
    // Object created on the stack with specific constructor
    std::string s2("Hello");
    std::cout << "String s2 : " << s2 << std::endl;
    // Object created on the heap with specific constructor
    std::string* s3 = new std::string("World");
    std::cout << "String s3 : " << s3[0] << std::endl;

    // Copy an object on the stack means that the assignment operator of the
    // class will be applied (usually it involves a deep copy of the object)
    std::string s4 = s2;
    std::cout << "String s4 : " << s4 << std::endl;
    s4[0] = '#';
    std::cout << "String s4 : " << s4 << std::endl;
    std::cout << "String s2 : " << s2 << std::endl;

    // Copying a pointer to an object simply copies the address (reference)
    // In C++, there is no built-in reference counting mechanism and copying
    // a pointer only means copying an address
    std::string* s5 = s3;
    s5->at(0) = '@';
    std::cout << "String s3 : " << s3[0] << std::endl;
    std::cout << "String s5 : " << s5[0] << std::endl;

    // Delete all variables on the heap
    delete s3; s3 = nullptr;
    delete s5; s5 = nullptr;

    return 0;
}

Global Variables

In C++, as in C, it is possible to allocate global variables. These variables are declared outside any function or method scope and are accessible in any function or method. It is possible to restrict the scope of a global variable to the .c/.cpp file in which it is declared by adding the static keyword to the variable declaration such as in static int counter. Global variables are allocated for the entire lifetime of the program in which it is declared. They are located in a global memory space.

It is a good practice to limit the use of global variables as much as possible. If feasible, prevent their use and limit their scope with the use of the static keyword.

example

#include <iostream>

// Global variable
int globalVariable = 9;

// This variable scope is limited to this file
static int restrictedGlobalVariable = 10;

// Function using global variable
void f() {
    globalVariable += 1;
    restrictedGlobalVariable += 1;
}

int main() {
    std::cout << globalVariable << " " << restrictedGlobalVariable << std::endl;
    globalVariable++;
    restrictedGlobalVariable++;
    std::cout << globalVariable << " " << restrictedGlobalVariable << std::endl;
    f();
    std::cout << globalVariable << " " << restrictedGlobalVariable << std::endl;
    return 0;
}

Constants

In C, constants are often defined using the #define primitive. With this primitive, the pre-processor first textually replaces all defined symbols in the code, before the compiler does perform its job. Using such primitives has many drawbacks and with C++, constants should be defined using the const or constexpr keyword.

Both keywords are often combined with the static keyword. Constants can be defined globally or within a class scope as demonstrated by the class_with_constant.hpp file.

example

class_with_constant.hpp

#ifndef EMBEDDED_C__CLASS_WITH_CONSTANT_HPP
#define EMBEDDED_C__CLASS_WITH_CONSTANT_HPP

#include <cstdint>
#include <cstdlib>

// Global constants
static constexpr uint32_t kGlobalConstantExpr = 1;
static const uint32_t kGlobalConstant = rand();

class ClassWithConstant {
public:
    static constexpr uint32_t kConstantExpr = 2;
    static const uint32_t kConstant;
};
// Initialization of kConstant
const uint32_t ClassWithConstant::kConstant = kGlobalConstant * 2;

#endif //EMBEDDED_C__CLASS_WITH_CONSTANT_HPP

main.cpp

#include <iostream>
#include "class_with_constant.hpp"

int main() {
    std::cout << "Global const expr: " << kGlobalConstantExpr << std::endl;
    std::cout << "Global random const: " << kGlobalConstant << std::endl;

    std::cout << "Const expr: " << ClassWithConstant::kConstantExpr << std::endl;
    std::cout << "Random const: " << ClassWithConstant::kConstant << std::endl;

    return 0;
}