C++ 記憶體

電腦

電腦是由一堆晶體管(Transistor)組成,可以把transistor想像成開關,因為開關只有兩種狀態,開或是關,對應到0或是1,因此電腦裡面所有東西都是由0和1來表示

電腦是用2進位(binary)

一切都是數字

電腦的世界裡,一切都是數字...

8 bit = 1 byte

Bit

Binary Values and Range

3-bit binary number可以代表多少數字?

N-bit binary number可以代表

2^n

個數字

000->0 ~ 111->7

1 byte可以存多少數字?

4 byte 呢 ?

1byte用16進位來表示會需要幾位?

1:\ 2^8\quad 2:\ (2^8)^4 = 2^{32}\quad 3:\ 2
2^{10} \approx 1000

Different variable type

sizeof

int x;
cout << sizeof(x) << " bytes" << endl;
cout << sizeof(int) << " bytes"<< endl;

syntax: sizeof(variable), sizeof(type)

取得一個型別或變數在記憶體中佔用的位元組數(bytes)

儲存範圍

int 能儲存 4byte 的資料

範圍是: -2^31~2^31 - 1

也就是 −2,147,483,648 ~ 2,147,483,647

long long 能儲存 8byte 的資料

範圍是: -2^63 ~ 2^63 - 1

儲存範圍

如果數字 >\ 10^9\ 要用\ long\ long\ (8\ byte)\ 不然會overflow

overflow: 數值超過表示範圍,變成不可預期的值

#include <iostream>
using namespace std;

int main() {
    int x = 42;
    x = 1e10;
    cout << x <<"\n";
    return 0;
}

Remark

#define ll long long
ll a = 5;
long long a = 6;
unsigned long long a = 3;

unsigned: 沒有正負號

會把加在int 前面會讓表示範圍變成

0 ~ 2^32 - 1

加在long long前面呢 ?

電腦組成

位址(address)

變數是存放在計算機的記憶體裡面

要存取記憶體上的資料,我們會需要資料在記憶體的位址

因此一個變數會有一個相對應的記憶體位址

取位址運算子(&)

#include <iostream>
using namespace std;

int main() {
    int x = 42;
    cout << "x 的值是: " << x << endl;
    cout << "x 的記憶體位址是: ";
    cout << &x << endl;
    return 0;
}

記憶體位址通常使用16進位來表示

0x開頭表示後面是16進位

Translating binary to hexadecimal

電腦眼中的變數,就是變數儲存的位址

如何儲存addr

有了位址,我們就可以更輕鬆的操作變數

指標(pointers)

Pointers

用來儲存另外一個variable的位址

Pointers

指標可以想像成一個變數指向另外一個變數

一個變數如何可以指向另外一個變數?

透過將目標變數的記憶體位址存起來,我們就可以與該變數建立連結

myvar = 25;
foo = &myvar;
bar = myvar;

Remark:

用&可以拿到變數在記憶體的位址

dereference operator(*)

暫時叫他取值運算子

baz = *foo;

可以存取foo儲存的變數

其實就是去memory把

對應位址的資料取出

Declaring pointers

Syntax: type *name;

int *number;
char *character;
double *decimals;

Remark:

  1. address需要用指標存
  2. 宣告的*和取值的*不要搞混
  3. 在變數名稱前面加上*就可以代表該type的指標

自己驗證一下,指標size都一樣嗎?

null pointers

int *p = 0;
int *q = nullptr;

int *r = NULL;

通常指標剛開始宣告時不會只向任何記憶體(變數)

需要代表我們的指標現在沒有指向任何東西

Remark: 初始化最好都指向nullptr

#include <iostream>
using namespace std;

int main ()
{
  int firstvalue = 8, secondvalue = 9;
  int *mypointer;

  mypointer = &firstvalue;
  *mypointer = 10;
  mypointer = &secondvalue;
  *mypointer = 20;
  cout<<"firstvalue is "<<firstvalue<<'\n';
  cout<<"secondvalue is "<<secondvalue<<'\n';
  return 0;
}

Remark

int myvar;
int *myptr;
myptr = &myvar;
int myvar;
int *myptr;
*myptr = &myvar;
int myvar;
int *myptr = &myvar;

int myvar;
int *foo = &myvar;
int *bar = foo;

Remark

哪裡錯 ?

int *a;
*a = 5;

a沒有指向任何記憶體,因此不能進行存取

Swap

如何用pointer實作swap ?

Swap

code要補充...

Recall

void swap_by_ref(int &a, int &b) {
    int tmp = a;
    a = b;
    b = tmp;
}
swap_by_ref(a, b);

pass-by-value v.s. pass-by-reference

項目 reference(參考) pointer(指標)
寫法 int &a int *a
呼叫方式 直接傳變數 傳變數位址
是否可為空 不可為空(必須綁定) 可為 nullptr
存取語法 直接用變數名 需 * 解參考
與 swap 關係 都能改到呼叫端變數 都能改到呼叫端變數

pointer vs array

array其實也是儲存位址,只是大小以及位置無法改變

int myarray[20];
int *mypointer;
mypointer = myarray;

myarray = mypointer;

陣列:固定位置的指標

Pointer arithmetics

  1. 當我們在加或減一個指標時,他會跳到下一個變數的位址
  2. 不同type的變數會有不同byte,所以加的長度其實一次是加sizeof(type)
int *p = (int *)1;
cout << p << "\n";
p += 1;//p += 5;
cout << p <<"\n";
long long *q = (long long *)1;
cout << q << "\n";
q += 1;
cout << q <<"\n";

Example

Remark:

(new_type)expression可以轉換類型

e.g.  char c; cout <<(int)c;

Remark

指標的加減是以指到的元素大小為單位

int main() {
    int *x = (int*)1;
    int *y = (int*)5;
    cout << y - x << endl;
    long long *z = (long long*)17;
    cout << z - (long long*)1 << endl;
    return 0;
}

Array

int arr[] = {1, 2, 3, 4, 5};
int i = 0;
cout << *(arr + i);

a[4] = 0;

*(a + 4) = 0;

上面兩個其實等價

Array

int arr[5];
int *p = arr;
for(int i = 0; i < 5; i++)
{
	*p = i;
    p++;
}
for(int *i = arr; i < arr + 5; i++)
{
	cout << *i <<"\n";
}

Array in function

void f(int arr[], int n)
{
	for(int i = 0; i < n; i++)
    {
    	arr[i] += 2;
    }
    return;
}
  1. 參數是陣列要加上[ ]
  2. array如果將其作為參數傳入function,會是原來的array

練習

寫一個function,接受參數是array和一個pointer,要求回傳pointer所指向的元素

是array裡面的第幾個元素?

int main() {
    int arr[5] = {1, 2, 6, 7, 8};
    int *ptr = (arr + 3);
    cout << f(arr, ptr) <<" ";
    cout << arr[f(arr, ptr)]<<"\n";
    cout << *ptr <<"\n";
    return 0;
}

Char

以前 C 是沒有string的,那時候要輸出字串必須要定義字元的陣列

char f1[10];
f1[0] = 'h', f1[1] = 'i', f1[2] = '\0';
cout << f1 <<"\n";

Char

char *foo = "hello";
cout << foo <<"\n";
cout << *(foo+4) << " " 
<< foo[4] <<"\n";

Pointers to pointers

char a;
char *b;
char **c;
a = 'z';
b = &a;
c = &b;

如何透過c拿到a變數的value ?

Pointers to functions

pointers也可以代表函數

用( )把函數名稱括起來並加上*以及接受的參數

int f(int x, int y)
{
    return x + y;
}
int main() {
    int (*g)(int, int) = f;
    cout << g(2, 3) << "\n";
    return 0;
}

Pointers to functions

pointers可以讓函數作為一種參數,使其可以傳到另外一個參數

int addition (int a, int b)
{ return (a+b); }

int subtraction (int a, int b)
{ return (a-b); }

Pointers to functions

pointers可以讓函數作為一種參數,使其可以傳到另外一個參數

int operation (int x, int y, int (*func)(int,int))
{
  int g;
  g = (*func)(x,y);
  return (g);
}
int m;
m = operation (7, 5, addition);

C++ 記憶體

By ernestii26