[C++] Difference between pointer and reference, value passing and reference passing, function pointer, smart pointer

cpp-difference-between-pointer-and-reference
This article can be read in about 19 minutes.

Pointer: Variable that stores an address

  • Pointer = Variable that stores an address (location in memory space)
  • Like variables of basic types such as int and char, pointer variables are also allocated memory for the variable and placed in memory space when the variable is declared. However, immediately after the variable declaration, the pointer is stored with an undefined value, and it is not known where it points.
  • The address of a variable can be obtained by prefixing the name of the variable with the & sign.
  • Dereference (reverse reference): By prefixing a pointer variable with * (indirect operator), the data at the address stored in this pointer can be accessed.

See more at: https://daeudaeu.com/pointer/

pointer variable declaration

C++
int *p;    // ポインタ変数宣言:int型を変数を指すポインタ
// * は変数名側に寄せて付けても良いし、型名側に寄せてつけてもコンパイルは通る
int* p;  //int *p;と同じ

In the following, x is initialized with 'A', so ptr points to the address of x and *ptr (the value ptr points to) is 'A'.

C++
char x = 'A';
char *ptr;
ptr = &x;
printf("*ptr = %cn", *ptr); // *ptr = A"

In the following case, x is not initialized. Therefore, the value stored in x is undefined and the result of *ptr is also undefined.

C++
char x;
char *ptr;
ptr = &x;
printf("*ptr = %cn", *ptr);
C++
// ポインタ変数宣言時点では pNumberには具体的なメモリアドレスが割り当てられていない
int* pNumber;
// error. プログラムはポインタが指す不定の場所に10を書き込もうとする
*pNumber = 10;

When declaring pointer variables, it is recommended that they be assigned a valid memory address or initialized withnullptr or NULL. Direct assignment of concrete address values should be avoided except in special circumstances.

C++

// プログラムのメモリ領域に number という変数を作成し、その変数が 10 という値で初期化
int number = 10;
// number変数のメモリアドレスは、& 演算子を使って取得→これはOK
int* p = &number;
*pNumber = 41;  

// ポインタ変数宣言と初期化
//非推奨:ポインタpにリテラルアドレス 0x0000 を代入しているが、このアドレスが有効である保証はない
int* p = 0x0000;

// NULL に初期化することは有効。これはポインタがどこも指さないことを明示的に示す
int* p = nullptr;  // C++11 以降

Difference between pointer and reference

featurepointerreference
Definition.An operation that accesses the value stored at the address pointed to by a pointer. This function is similar to a shortcut in Windows or a symbolic link in Linux.The reference itself behaves as a dereferenced value.
initializationCan be declared without initialization; can be initialized to nullptr.Must be initialized at the time of declaration.
reassignmentCan be changed to point to different addresses.Once initialized, it cannot be changed to refer to another object.
‘null’ statusIt can point to nullptr.There is no ‘null’ reference.
Access valueAccessed using dereferencing (reverse referencing) ( * ). int x = 5; int* p = &x; // p is a pointer type int y = *p; // access the value by dereferencing (dereferencing with *)Limited operations are offered.
memory addressExplicitly get the address ( & ) and operate.Addresses are not handled directly.
operabilityAddress arithmetic is possible.Limited operations offered.
safetyPointers are dangerous when used incorrectly.References are more secure and have no uninitialized state.
examples showing the use (of a word)Dynamic memory management, array operations, etc.Function arguments, return values, object aliases, etc.

Pass-by-Value and Pass-by-Reference

Pass by value

  • When passing by value, what is passed as an argument to the function is a copy of the value of the variable
  • Therefore, changing the value of an argument within a function does not affect the original variable outside the function, because it is a change to the copied value.
  • If you want changes made in a function to be reflected in a variable outside the function, you must return a value from the function as a return value in the return method and reassign it to the outside variable. However, since only one data can be passed in return, the function can pass only one result to the caller.
C++
void increment(int value) {
    value = value + 1;
}

int x = 5;
increment(x); // この時点でのxの値はまだ5。
C++
int increment(int value) {
    return value + 1; //returnで、関数から値を戻り値として返す
}

int x = 5;
x = increment(x); // この時点でのxの値は6になる。

Pass by reference

  • In the case of pass-by-reference, it is the address (or reference) of the variable that is passed to the function as an argument.
  • A duplicate of the data passed as an argument when the function is executed is created in the same way, but in the case of a pointer, the address stored in the pointer is duplicated. Therefore, even though they are completely different as pointer variables, pointers duplicated by a function call point to the same location pointed to by the original pointer.
  • Therefore, when a value is changed through an argument in a function, the change is made to a value stored at the same address in memory and is directly reflected in a variable outside the function.
  • In this case, there is no need to reassign variables outside the function to update them.
Source: https://daeudaeu.com/pointer/#i-10

There are two ways to achieve reference parsing in C++.

(1) Using pointers as arguments

C++
void increment(int* value) {
    *value = *value + 1;
}

int x = 5;
increment(&x); // x=6

2) Use references

C++
void increment(int& value) {
    value = value + 1;
}

int x = 5;
increment(x); // この時点でのxの値は6になる。

Faster programs by reducing copy time

In the case of value parsing

  • The data size is determined by the argument type. For example, an int argument is usually 4 bytes and a char argument is usually 1 byte.

Pass-by-reference (pass-by-pointer)

  • Passed to the function is a pointer, the size of which is platform-dependent: 4 bytes on 32-bit architectures and 8 bytes on 64-bit architectures.

In the case of pass-by-reference, functions can be called efficiently even when dealing with large data structures or when the size of the data depends on the execution environment, thus improving the execution speed of the resulting program.

function pointer

  • A pointer pointing to a function. Functions, like variables, etc., are expanded in memory during program execution and reside in memory.
  • Variable declaration of function pointer. Declare variables according to the function return value and argument types: return type (*variable name)(argument 1 type, argument 2 type);.
  • Address storage in function pointer: Simply assign the function name to the function pointer. However, use the variable name of the function pointer for the function name.
  • Function pointers are especially useful when flexible programming is needed, such as in callback functions or when selecting functions at runtime.
C++
int functionA(int a, char b, int *c){
    /* 処理 */
}

// 関数ポインタの変数宣言
// 戻り値の型 (*変数名)(引数1の型, 引数2の型);
int (*funcPtr)(int, char, int*);

int x = 10;
char y = 'y';
int ret;

// 関数ポインタへのアドレス格納
funcPtr = functionA;

// 関数名には関数ポインタの変数名を使用する
ret = funcPtr(x, y, &x);

Using function pointers

  • The function pointer corresponding to the operator is stored in operation, and the pointer is used to execute the selected operation. This allows for easy function switching.
C++
// 関数ポインタを使った場合
#include 

// 加算関数
int add(int a, int b) {
    return a + b;
}

// 減算関数
int subtract(int a, int b) {
    return a - b;
}

int main() {
    // 関数ポインタの宣言
    int (*operation)(int, int);

    int num1, num2;
    char operator;

    printf("Enter two numbers: ");
    scanf("%d %d", &num1, &num2);
    printf("Enter operator (+, -): ");
    scanf(" %c", &operator);

    // 選択された演算に応じて関数ポインタに対応する関数を格納
    switch (operator) {
        case '+':
            operation = add;
            break;
        case '-':
            operation = subtract;
            break;
        default:
            printf("Invalid operatorn");
            return 1;
    }

    // 関数ポインタを使用して計算を実行し、結果を表示
    int result = operation(num1, num2);
    printf("Result: %dn", result);

    return 0;
}

If function pointers are not used

  • Need to call the function directly with a conditional branch for each operator
C++
#include 

// 加算関数と減算関数のコードは同じ

int main() {
    int num1, num2;
    char operator;

    printf("Enter two numbers: ");
    scanf("%d %d", &num1, &num2);
    printf("Enter operator (+, -): ");
    scanf(" %c", &operator);

    // 演算子ごとに関数を呼び出す
    int result;
    switch (operator) {
        case '+':
            result = add(num1, num2);
            break;
        case '-':
            result = subtract(num1, num2);
            break;
        default:
            printf("Invalid operatorn");
            return 1;
    }

    // 結果を表示
    printf("Result: %dn", result);

    return 0;
}

Situations to use function pointers

callback function

  • When you pass the address of a function and have it executed at the destination you pass it to. In multi-threaded or parallel programming, multiple processes are going on at the same time, and a situation may arise where you want to execute another process when one process is finished, in which case the callback function is useful.

The addNumbers function takes two integers as normal arguments and a function pointer (callback), and calls the callback function through the pointer under certain conditions.

C++
#include 

// コールバック関数の型を定義
typedef void (*Callback)(int);

// 加算を行い、結果が10以上ならコールバックを呼び出す関数
void addNumbers(int a, int b, Callback callback) {
    int result = a + b;
    std::cout << "加算結果: " << result <= 10) {
        // コールバック関数の呼び出し
        callback(result);
    }
}

// コールバックとして使用される関数
void onResultReachedTen(int result) {
    std::cout << "結果が10以上です: " << result << std::endl;
}

int main() {
    int x = 5;
    int y = 7;

    // addNumbersに数値とコールバック関数を渡す
    addNumbers(x, y, onResultReachedTen);

    return 0;
}

Memory Management: Smart Pointer

smart pointer

  • Smart Pointer is a tool for smarter use of pointers
  • The latest C++ recommends wrapping the raw pointer with a smart pointer instead of using a raw pointer.
  • Automatically release memory when the destructor is called. (i.e., when the code goes out of scope of the smart pointer).

https://learn.microsoft.com/ja-jp/cpp/cpp/smart-pointers-modern-cpp?view=msvc-170

When using a raw pointer:

C++
int* pNumber = new int; // 新しい整数のメモリを借りる
*pNumber = 10;          // そのメモリに10を格納する
delete pNumber;         // もうそのメモリは必要ないので返却する

If you use a smart pointer (here std::unique_ptr )

C++
#include  // スマートポインターを使うために必要なライブラリ

std::unique_ptr pNumber = std::make_unique(); // 新しい整数のメモリを賢く借りる
*pNumber = 10; // そのメモリに10を格納する
// ここでdeleteを呼び出す必要はない。スマートポインターが自動でメモリを返却してくれる

コメント

Copied title and URL