Try   HackMD

CPP基礎.Lesson 4

多維陣列

什麼是維???!

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

維度 對應到
零維 int a
一維 int a[10]
二維 int a[10][10]

宣告多維陣列

//型態 名稱 [大小][大小]... (初始化)
int W[5][4];                       //裡面裝垃圾值
int H[2][3] = {{1, 2, 3}, {4, 5}}; //H[1][2] == 0
int O[2][3] = {};                  //全部裝零

//也可以不要寫最前面的大小(如果有初始化)
int X[][2] = {{1, 2}, {3, 4}};     //最前面的大小是2
int I[][2] = {{1, 2}, {3}};        //同上,I[1][1] == 0

int x[][2][2] = {{{1, 2}, {3, 4}}, {{1, 2}, {3, 4}}}
//最前面的大小會是2
int i[][2][2] = {{{1, 1}, {1}}, {{1}}}
//最前面的大小也是2, 其他值為0

輸入陣列的值 (宜搭配for服用)

int winnie[10][20];
for(int i = 0; i < 10; i++)
    for(int j = 0; j < 20; j++)
        cin >> winnie[i][j];

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

注意!

  • 索引值從0開始
  • 索引值只能是整數
  • 也可以用變數當索引
  • 讀到記憶體外就會RE吃到飽

陣列不能互相指定,比較

一維或多維都是

int C[3][3] = {};
int o[3][3];
o = C; //compile error
int V[3][3] = {};
if(C == V) // 不等於
    cout << "impossible" << endl;

修飾子 Qualifier

資料型態

  • 變數的儲存方式
  • 不同型態的變數所占大小不同
  • bit? byte?
    8 bits = 1 byte
    MbpsMB/s 不一樣喔!
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
型別 意義
bool boolean值(真值)
char 字元
int 整數
float 浮點數
double 倍準浮點數

修飾什麼?

宣告額外性質

  • 改變所占記憶體
    • short、long、long long
  • 改變數值範圍
    • unsigned、signed
int main(){
    short int s;      // 短整數
    long int l;       // 長整數
    long long int ll; // 更長的整數
    long double ld;   // 長倍精度浮點(小)數
    unsigned int u;   // 無號(正)整數
}

<climits>, <limits> 可以取得各型態範圍

#include <climits>
#include <limits>
int main(){
    //<climits> 的用法
    int a = INT_MAX; //現在常見的電腦上是 2147483647
    unsigned long b = ULONG_MAX; 
    //<limits> 的用法
    a = std::numeric_limits<int>::min(); //常見為 -2147483648
    bool c = std::numeric_limits<double>::has_infinity; //true
}

空間大小: (sizeof(xxx) 回傳 xxx 所佔據的位元組數)
C 與 C++ 標準皆沒有規範大小,
大多要看編譯時電腦的 CPU 架構與作業系統而定

sizeof(char) <= sizeof(short) 
             <= sizeof(int) 
             <= sizeof(long) 
             <= sizeof(long long)
型別 32-bit Windows 64-bit Windows 64-bit MacOS/linux
short int 16 bits = 2 bytes 16 bits = 2 bytes 16 bits = 2 bytes
int 32 bits = 4 bytes 32 bits = 4 bytes 32 bits = 4 bytes
long int 32 bits = 4 bytes 32 bits = 4 bytes 64 bits = 8 bytes
long long int 64 bits = 8 bytes 64 bits = 8 bytes 64 bits = 8 bytes

const

const: constant(不變的)的簡寫,有翻譯作常量
顧名思義,初始化後就不可再更改
必須在編譯時初始化
目的是要預防寫程式的人不小心寫錯
常用來修飾參數,可以預防函數使壞

const double Pi = 3.1415926;
double SurfaceArea(double r){
    return Pi * r * r;
}
double Volume(double r){
    return Pi * r * r * 3 / 4;
}
int main(){
    Pi = 3.14; // CE: assignment of read-only variable 'Pi'
    double volume = Volume(1);
}

static

static: 靜態的
生命周期(下面會提到)不隨函數消長(整支程式永遠恰有一個)
可以想成:變數範圍不變、生命週期變為整個程式碼

#include <iostream>
int count(){
    static int counter = 0;
    return counter++;
}
int main(){
  for(int i=0; i<5; i++)
      std::cout << count() << " "; // 0 1 2 3 4 
}

另外,static 變數不能被其他 C++ 檔案使用
例如在標頭檔中宣告的 static 全域變數
在引入這個標頭檔的 cpp 檔中是無法使用(看不到)這個變數的

// circles.cpp
static double Pi = 3.14159; // 這行用了 static
double SurfaceArea(double r){
    return Pi * r * r;
}
// main.cpp
double SurfaceArea(double r);
int main(){
    extern double Pi; // 如果沒有 static,這行可以拿到 Pi
    double P = 2 * Pi * 5;
}
//CE: undefined reference to `Pi'

其他修飾子

  • C++: extern, inline, volatile
  • C: register, restrict, _Atomic

總結

  • short, long, unsigned 可以修飾資料型態
  • const 可以避免意外修改
  • static 可以讓變數一直活著
  • 參考資料很雜,但放 cppreference 準沒錯

Struct

假設…你是個車輛經銷商

  • 儲存車輛的資料
    • 價格
    • 年份
    • 長、寬、高(體積)
    • 油耗量
    • 最高時數
    • ….

solution 1

開好幾條陣列

char carName[30][50];
int year[30];
int price[30];
float speed[30];
float oil[30];
  • 過於繁瑣
  • 資料不好整理

solution 2

開一條二維陣列

int Car[30][5];
//Car[n][0] for Car’s name
//Car[n][1] for year
//Car[n][2] for Price
//Car[n][3] for speed
//Car[n][4] for oil
  • 不夠直觀,無法直接從Index中判斷資料類型
  • 不能同時把不同類型的東西綁一起

使用struct

struct Car{
    char carName[50];
    int year;
    int price;
    float speed;
    float oil;
};
  • 簡單明瞭
  • 存取方便
  • 符合物件導向設計思維

宣告

struct name{
    datatype data1;
	datatype data2;};
struct Car{
    char carName[50];
    int year;
    int price;
    float speed;
    float oil;
};

int main(){
    Car Benz;
    cin >> Benz.carName >> Benz.year
        >> Benz.price >> Benz.speed
        >> Benz.oil;
    //用name.dataname來存取

    Car Armed[20];
    //也可以宣告struct 陣列
    Armed[5] = Benz;
    //可用'='來賦予一個struct的所有值給另一個struct
    cout << "Name: " << Armed[5].carName << endl;
    cout << "Year: " << Armed[5].year << endl;
    cout << "Price: " << Armed[5].price << endl;
    cout << "Speed: " << Armed[5].speed << endl;
    cout << "Oil: " << Armed[5].oil << endl;
    
}

CONSTRUCTOR 建構子
方便初始化變數

struct Car{
    char carName[50];
    int year;
    int price;
    float speed;
    float oil;
    
    Car(int _price){
        price = _price;
    }
};

Car Mazda(980000);

Scope

變數的生命週期

變數活著的時候我們可以使用
死後無人知曉無影無蹤

Local Variables

活在block裡的變數
在block裡宣告 (出生)
出block就死亡

Block

block: 被大括號包起來的區域

{
	//This is a block
}
int foo(int n) {
	//連同函式主體一起組成的block
}
for (int i = 0; i < 5; i++) {
	//連同for一起組成的block
}
{
    {
        int a = 10; //a 出生
        cout << a; //print a
    }
    
    cout << a; //編譯失敗 a 已經死掉
    //error: 'a' was not declared in this scope
}
int main() {   
    
    for (int i = 0; i < 5; i++) {
        //可以使用i
    }
    
    cout << i; //編譯失敗 i 已經死掉
    //error: 'i' was not declared in this scope
}

Global Variables

可以在程式的各個角落使用他

通常宣告在程式的頂部
在block, function 外

#include <iostream>
using namespace std;
int global = 10;
void func1() {
    global = global + 1;
}
void func2() {
    global = global + 2;
}
void func3() {
    global = global + 3;
}
void func4() {
    global = global + 4;
}
int main()
{
    func1();
    func2();
    func3();
    func4();
    cout << global;
}

全域變數每個地方都可以改

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
出bug機率大

如果區域變數的名稱跟全域變數相同

  1. 編譯會過? 會
  2. 那用誰的? 優先使用最後命名的
#include<iostream> 
using namespace std; 

int x = 5; //global x
  
int main() 
{    
    int x = 2; //local x
    cout << x << endl; //print 2
} 

namespace

如果今天你要和一堆人一起寫project
為了避免撞名,該怎麼辦?

int chen_max(int x, int y);
int lee_max(int x, int y);
int wang_max(int x, int y);
#include <iostream>
namespace chen{
    int max(int x, int y){
        return x > y ? x : y;
    };
}

int main(){
	//std 也是一個namespace
	std::cout << chen::max(3, 5);
}

using namespace [namespace]

所有在[namespace]的名稱都可以使用

#include <iostream>
using namespace std;
int main() {
	cout << "123";
}

回家作業

d626: 小畫家真好用