C++ 讀取二進制檔用法與範例

本篇 ShengYu 介紹 C++ 讀取二進制檔的用法與範例,

以下 C++ 讀取二進制檔的用法介紹將分為這幾部份,

  • C++ std::ifstream 讀取二進制檔資料
  • C++ 讀取二進制檔到 C-Style 陣列裡
  • C++ 一次讀取全部二進制到 vector<char>
  • C++ 一次讀取全部二進制到 string 裡
  • C++ 讀取二進制檔資料到 struct 裡

那我們開始吧!

C++ std::ifstream 讀取二進制檔資料

這邊示範 C++ std::ifstream 讀取二進制檔資料(binary),ifstream 要讀取二進制檔案資料的話,開檔模式要用 std::ios::binary 表示二進制模式,假如只讀取單純一筆 int 的資料的話,範例如下,

cpp-binary-read.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// g++ cpp-binary-read.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

int main() {
std::ifstream ifs;
int num;

ifs.open("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ifs.read((char *)&num, sizeof(int));
cout << num;
ifs.close();
return 0;
}

結果輸出如下,

1
3

C++ 讀取二進制檔到 C-Style 陣列裡

這邊介紹 C++ std::ifstream 讀取二進制檔到 C-Style 陣列裡,建立完 ifstream 後使用 ifstream::open() 來開檔並指定 std::ios::binary 二進制模式,開檔跟上個範例不同的是這次我們在 ifstream 建構時一併開檔,如果開檔成功的話,就讀出內容並且印出來,

cpp-binary-read2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-binary-read2.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

int main() {
char buffer[256] = {0};

std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ifs.read(buffer, sizeof(buffer));
cout << buffer;
ifs.close();
return 0;
}

假設我們的輸入的二進制內容是 abcde,結果輸出如下,

1
abcde

要用 ofstream 將資料寫入二進制檔的範例可以看這篇

C++ 一次讀取全部二進制到 vector<char>

這邊示範 C++ 一次讀取全部二進制到 vector<char> 裡,

cpp-binary-read3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// g++ cpp-binary-read3.cpp -o a.out
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

int main() {
std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

std::vector<char> buffer;
buffer.assign(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());

for (int i = 0; i < buffer.size(); i++) {
cout << buffer[i] << " ";
}
ifs.close();
return 0;
}

結果輸出如下,

1
a b c d e

C++ 一次讀取全部二進制到 string 裡

這邊示範 C++ 一次讀取全部二進制到 string 裡,

cpp-binary-read4.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// g++ cpp-binary-read4.cpp -o a.out
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

string str;
str.assign(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
// or
//ifs >> str;

for (int i = 0; i < str.size(); i++) {
cout << str[i] << " ";
//cout << (int)str[i] << " ";
}
ifs.close();
return 0;
}

結果輸出如下,

1
a b c d e

C++ 讀取二進制檔資料到 struct 裡

這邊示範 C++ 用 ifstream 將二進制檔的資料讀取到 struct 裡,先用 ifstream::read() 讀一個 int 知道有幾筆 struct 的資料後,之後再用 for 迴圈搭配 ifstream::read() 讀取 count 次 struct 的資料,

cpp-binary-read5.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// g++ cpp-binary-read5.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

struct Student {
int id;
char name[4];
int age;
};

int main() {
struct Student st[10] = {0};
int count;

std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ifs.read((char *)&count, sizeof(int)); // 讀取筆數
cout << "count: " << count << "\n";

for (int i = 0; i < count; i++) {
ifs.read((char *)&st[i], sizeof(struct Student));
cout << "id: " << st[i].id
<< ", name: " << st[i].name
<< ", age: " << st[i].age << "\n";
}
ifs.close();
return 0;
}

結果輸出如下,

1
2
3
4
count: 3
id: 1, name: ab, age: 18
id: 2, name: cd, age: 19
id: 3, name: ef, age: 20

更好的寫法是讀完筆數後,去 new 動態記憶體配置對應的 struct 筆數,如下所示,跟上例不同的地方還有這次我把 ifstream::read() 一次讀取 count 筆 struct 資料,

cpp-binary-read6.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// g++ cpp-binary-read6.cpp -o a.out
#include <iostream>
#include <fstream>
using namespace std;

struct Student {
int id;
char name[4];
int age;
};

int main() {
int count;

std::ifstream ifs("input.bin", std::ios::in | std::ios::binary);
if (!ifs.is_open()) {
cout << "Failed to open file.\n";
return 1; // EXIT_FAILURE
}

ifs.read((char *)&count, sizeof(int)); // 讀取筆數
cout << "count: " << count << "\n";

struct Student *st = new Student[count];
ifs.read((char *)st, sizeof(struct Student) * count);

for (int i = 0; i < count; i++) {
cout << "id: " << st[i].id
<< ", name: " << st[i].name
<< ", age: " << st[i].age << "\n";
}
ifs.close();
return 0;
}

結果輸出同上。要如何用 ofstream 將 struct 寫入二進制檔的範例可以看這篇

以上就是 C++ 讀取二進制檔的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C++ 寫入二進制檔用法與範例
C++ 讀取文字檔用法與範例
C++ 寫入文字檔用法與範例

macOS 安裝 zsh-completions 輸入指令自動完成

macOS 使用 zsh 要自動完成的話要安裝 zsh-completions,macOS Mojave (10.14) 之前的版本預設 shell 是 bash。從 macOS Catalina (10.15.x) 開始使用 zsh 做為預設登入 shell,想要知道你現在用的 shell 是哪種的話可以透過 echo $SHELL 指令來了解,以我的電腦 10.15.7 為例的話

1
2
$ echo $SHELL
/bin/zsh

這表示目前使用的 shell 是 zsh
如何變更預設 shell 可以看這篇 Apple 官方的教學

但是預設 zsh 下輸入指令按 tab 時沒有自動完成的話會很不好使用,所以以下介紹怎麼安裝 zsh 自動完成,
brew 安裝 zsh-completions 指令如下,

1
brew install zsh-completions

安裝最後的畫面如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
==> Pouring zsh-completions--0.33.0.all.bottle.tar.gz
==> Caveats
To activate these completions, add the following to your .zshrc:

autoload -Uz compinit
compinit

You may also need to force rebuild `zcompdump`:

rm -f ~/.zcompdump; compinit

Additionally, if you receive "zsh compinit: insecure directories" warnings when attempting
to load these completions, you may need to run this:

chmod -R go-w '/usr/local/share/zsh'

zsh completions have been installed to:
/usr/local/share/zsh/site-functions
==> Summary
🍺 /usr/local/Cellar/zsh-completions/0.33.0: 147 files, 1.2MB

在 ~/.zshrc 添加以下內容

1
2
autoload -Uz compinit
compinit

之後開新的終端機,遇到下面這樣的錯誤訊息需要執行 chmod -R go-w '/usr/local/share/zsh' 可以解決,

1
2
zsh compinit: insecure directories, run compaudit for list.
Ignore insecure directories and continue [y] or abort compinit [n]?

這樣就搞定囉!開新的終端機試試效果吧!

git format-patch 製作 patch 與匯入 patch

本篇介紹 git-format-patch 製作 patch 與匯入 patch,git 提供了兩種 patch 方案。 一種是用 git diff 產生標準 patch (.diff 檔案),另一種是 git format-patch 生成的 git 專用 patch (.patch 檔案),以下兩種都會介紹。

以下 git 製作 patch 用法介紹將分為這幾部份,

  • git format-patch 製作 patch
  • git 匯入 patch
  • git 製作標準 diff patch
  • git 匯入標準 diff patch

那我們開始吧!

git format-patch 製作 patch

git format-patch 製作最新1筆 patch,

1
git format-patch -1

git format-patch 製作最新3筆 patch,

1
git format-patch -3

git format-patch 製作某個 commit 到最新 commit 的 patch,

1
git format-patch sha1111111111111111111111111111111111111

git format-patch 製作某個 commit 以前 1 筆的 patch,

1
git format-patch -1 sha1111111111111111111111111111111111111

git format-patch 製作某個 commit 以前 3 筆的 patch,

1
git format-patch -3 sha1111111111111111111111111111111111111

git format-patch 製作從 root 到最新 commit 的 patch,

1
git format-patch --root

git format-patch 製作從指定 commit 到結束 commit 的 patch,

1
git format-patch abcd123..abcd456

git format-patch 加上 -o 選項可以指定輸出的目錄,

1
git format-patch abcd123..abcd456 -o /home/shengyu/Download/

git 匯入 patch

這邊介紹 git 匯入 patch,

1
2
3
4
git am --abort # 假如之前匯入失敗的話 可以使用 git am --abort 取消之前的匯入
git am 0001-xxx.patch # 匯入 patch 檔案
# or
git am -s 0001-xxx.patch # 匯入 patch 檔案並加上 sign off 簽字

git 匯入指定目錄下的 patch,

1
git am /home/shengyu/Download/*

在匯入 patch 遇到衝突時,可以執行 git am --skip 跳過此次衝突,也可以執行 git am --abort 取消匯入 patch 的動作,還原到操作前的狀態。

除了用 git am 也可以用 git apply,以下是用 git apply 檢查 patch,確保 patch 檔案沒有問題,

1
git apply --stat 0001-xxx.patch

git apply 檢查 patch 套用後是否會有問題,

1
git apply --check 0001-xxx.patch

git apply 匯入 patch 改動但不 commit,

1
git apply 0001-xxx.patch

git 製作標準 diff patch

這邊介紹 git diff 製作標準 diff patch,也就是 diff 檔,將 track 但 not stage 的改動製作 diff,

1
git diff ./ > xxx-patch.diff

git diff 製作從指定 commit 到結束 commit 的 diff patch,

1
git diff abcd123 abcd456 > xxx-patch.diff

git 匯入標準 diff patch

這邊介紹 git 匯入標準 diff patch(沒有包含 commit),

1
2
3
patch -p1 < xxx-patch.diff
# or
git apply xxx-patch.diff

以上就是 git format-patch 製作 patch 與匯入 patch 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

Windows 10 硬碟使用率 100% 電腦緩慢的解決方法

本篇 ShengYu 介紹 Windows 10 硬碟使用率 100% 電腦緩慢的解決方法,這個現象特別容易在傳統硬碟上發現,SSD 基本上使用很快,所以很難發現,但傳統硬碟 Hard Disk 因為比 SSD 慢,所以更容易發現這件事。

取消勾選 允許為這個磁碟機中的檔案內容建立索引

到檔案總管,在 C 槽上按滑鼠右鍵,選擇內容,結果會看到 允許為這個磁碟機中的檔案內容建立索引 呈現勾選狀態,

取消勾選 允許為這個磁碟機中的檔案內容建立索引,並按下套用

選擇 將變更套用到磁碟C:、子資料夾及檔案,並按下確定

D 槽重複上述動作,其它槽也比照辦理。

停用 Windows 搜索

有兩種方式停用 Windows Search,分別是暫時停用 Windows Search永久停用 Windows Search

暫時停用 Windows Search
暫時停用 Windows Search 的方法如下,
右鍵點擊開始按鈕,選擇 命令提示字元(系統管理員),輸入 net.exe stop "Windows search"

cmd 要以系統管理員身份執行,否則會出現存取被拒的錯誤訊息

永久停用 Windows Search

同時按住鍵盤上Windows鍵+R,並輸入 services.msc

找到 Windows Search 選項,雙擊或右鍵進入內容

啟動類型改成已停用,點擊套用確定

其他文章參考
[教學]解決 windows 讀取滿載 硬碟100% 速度慢問題 - Angus電科技
https://wuangus.cc/windows8_hdd/
解決磁碟100%導致電腦緩慢運行的方法 不看你會後悔! @ 電腦維修初學者
https://dahufoodtravel.pixnet.net/blog/post/221625435
[教學] 如何解決 Windows 10 硬碟使用率 100% 的問題 @ 傻瓜狐狸的雜碎物品
https://www.fox-saying.com/blog/post/47491467

C/C++ exit 用法與範例

本篇 ShengYu 介紹 C/C++ exit 用法與範例,exit 函式是用來結束程式,並將 exit 的引數回傳給作業系統,以下介紹如何使用 exit 函式。

要使用 exit 的話需要引入的標頭檔 <stdlib.h>,如果要使用 C++ 的標頭檔則是引入 <cstdlib>
exit 函式原型為

1
void exit(int status);

使用 exit 函式會結束程式,並將 exit 的引數回傳給作業系統,可以使用 EXIT_SUCCESS 和 EXIT_FAILURE 預定義常數來當作 exit 的引數,來表示程式執行的成功或失敗。EXIT_SUCCESS 是 0 而 EXIT_FAILURE 是 1 。

有時候也會忘記 exit 到底要帶入 0 還是 1 哪個才是代表正常結束,容易忘記的話就可以使用 EXIT_SUCCESS 跟 EXIT_FAILURE 這兩個預定義的常數。

1
2
exit(EXIT_SUCCESS); // or exit(0);
exit(EXIT_FAILURE); // or exit(1);

EXIT_SUCCESS 與 EXIT_FAILURE 的預定義常數可以在 stdlib.h 或 cstdlib 標頭檔裡找到,

stdlib.h
1
2
#define	EXIT_FAILURE	1
#define EXIT_SUCCESS 0

以下示範 C/C++ exit 的使用範例,

cpp-exit.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
// g++ cpp-exit.cpp -o a.out
#include <stdio.h>
#include <stdlib.h>

int main() {
printf("main start\n");

printf("main exit\n");
exit(EXIT_FAILURE); // or exit(1);

printf("main end\n");
return 0;
}

程式輸出如下,

1
2
main start
main exit

以上就是 C/C++ exit 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其他參考
exit - C++ Reference
https://cplusplus.com/reference/cstdlib/exit/
C++程式終止 | Microsoft Learn
https://learn.microsoft.com/zh-tw/cpp/cpp/program-termination?view=msvc-170

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包

C/C++ sizeof 用法與範例

本篇 ShengYu 介紹 C/C++ sizeof 用法與範例,sizeof 是用來計算資料類型在該平台記憶體中所佔用 bytes 數。

以下 C/C++ sizeof 的用法介紹將分為這幾部份,

  • sizeof 基本類型
  • sizeof 自定義類型 struct / class
  • sizeof 資料結構對齊問題

那我們開始吧!

sizeof 基本類型

sizeof 回傳的是 size_t,要 print size_t 的話要用 %zu,才不會遇到型別不對的編譯警告,

cpp-sizeof.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// g++ cpp-sizeof.cpp -o a.out
#include <stdio.h>

int main() {
printf("sizeof(char) %zu\n", sizeof(char));
printf("sizeof(unsigned char) %zu\n", sizeof(unsigned char));
printf("sizeof(short) %zu\n", sizeof(short));
printf("sizeof(unsigned) %zu\n", sizeof(unsigned));
printf("sizeof(int) %zu\n", sizeof(int));
printf("sizeof(unsigned int) %zu\n", sizeof(unsigned int));
printf("sizeof(long) %zu\n", sizeof(long));
printf("sizeof(float) %zu\n", sizeof(float));
printf("sizeof(double) %zu\n", sizeof(double));

return 0;
}

我的平台輸出如下,基本上資料類型有無 unsigned 都是同一個大小,char 的大小是 1 byte,int 的大小是 4 bytes,

1
2
3
4
5
6
7
8
9
sizeof(char)           1
sizeof(unsigned char) 1
sizeof(short) 2
sizeof(unsigned) 4
sizeof(int) 4
sizeof(unsigned int) 4
sizeof(long) 8
sizeof(float) 4
sizeof(double) 8

如果是用 sizeof(變數) 的話,就會計算該變數的資料類型的大小,範例如下,

cpp-sizeof2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// g++ cpp-sizeof2.cpp -o a.out
#include <iostream>
using namespace std;

int main() {
char c = 'a';
short s = 123;
int i = 456;
long l = 789;
float f = 3.5;
double d = 3.14;
cout << "sizeof(char) " << sizeof(c) << '\n';
cout << "sizeof(short) " << sizeof(s) << '\n';
cout << "sizeof(int) " << sizeof(i) << '\n';
cout << "sizeof(long) " << sizeof(l) << '\n';
cout << "sizeof(float) " << sizeof(f) << '\n';
cout << "sizeof(double) " << sizeof(d) << '\n';

return 0;
}

我的平台輸出如下,

1
2
3
4
5
6
sizeof(char)          1
sizeof(short) 2
sizeof(int) 4
sizeof(long) 8
sizeof(float) 4
sizeof(double) 8

如果是用 sizeof 陣列的話,範例如下,

cpp-sizeof3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
// g++ cpp-sizeof3.cpp -o a.out
#include <iostream>
using namespace std;

int main() {
int num[10];
cout << "sizeof(int[10]) " << sizeof(int[10]) << '\n';
cout << "sizeof num " << sizeof(num) << '\n';
cout << "length of num " << sizeof(num) / sizeof(num[0]) << '\n';

return 0;
}

我的平台輸出如下,

1
2
3
sizeof(int[10])  40
sizeof num 40
length of num 10

sizeof 自定義類型 struct / class

sizeof 自定義類型 struct 或 class 的範例如下,

cpp-sizeof4.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// g++ cpp-sizeof4.cpp -o a.out
#include <iostream>
using namespace std;

struct EmptyStruct {};
class EmptyClass {};
struct CharStruct { char c; };
class CharClass { char c; };
class Base {
public:
int a;
};
struct Derived : Base {
public:
int b;
};

int main() {
EmptyClass *p;
Derived d;
Base &b = d;
cout << "sizeof empty struct " << sizeof(struct EmptyStruct) << '\n';
cout << "sizeof empty class " << sizeof(EmptyClass) << '\n';
cout << "sizeof Char struct " << sizeof(struct CharStruct) << '\n';
cout << "sizeof Char class " << sizeof(CharClass) << '\n';
cout << "sizeof pointer " << sizeof(p) << '\n';
cout << "sizeof the Base class " << sizeof(Base) << '\n';
cout << "sizeof the Derived class " << sizeof(Derived) << '\n';
cout << "sizeof the Derived through Base " << sizeof(b) << '\n';

return 0;
}

我的平台輸出如下,不管是空的 struct 或者空的 class 基本開銷就是 1,即使裡面放了一個 char 變數也還是 1,

1
2
3
4
5
6
7
8
sizeof empty struct              1
sizeof empty class 1
sizeof Char struct 1
sizeof Char class 1
sizeof pointer 8
sizeof the Base class 4
sizeof the Derived class 8
sizeof the Derived through Base 4

sizeof 資料結構對齊問題

現代的編譯器幾乎都會進行最佳化,所以有時候某資料結構佔用的 bytes 數可能不是你想像的,原因是因為編譯器會為了運行效能進行最佳化,如下列範例所示,

cpp-sizeof5.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-sizeof5.cpp -o a.out
#include <iostream>
using namespace std;

struct CharChar { char c; char c2; };
struct CharCharInt { char c; char c2; int i; };
struct IntCharChar { int i; char c; char c2; };
struct CharIntChar { char c; int i; char c2; };
struct CharShortChar { char c; short s; char c2; };

int main() {
cout << "sizeof(CharChar) " << sizeof(CharChar) << '\n';
cout << "sizeof(CharCharInt) " << sizeof(CharCharInt) << '\n';
cout << "sizeof(IntCharChar) " << sizeof(IntCharChar) << '\n';
cout << "sizeof(CharIntChar) " << sizeof(CharIntChar) << '\n';
cout << "sizeof(CharShortChar) " << sizeof(CharShortChar) << '\n';

return 0;
}

我的平台輸出如下,值得注意的是 CharCharInt 手算的話應該為 6,但是編譯器進行最佳化時會變成 8,
而 CharIntChar 會因為順序關係而造成最佳化結果不同變成 12,
CharShortChar 手算的話應該為 4,但是編譯器進行最佳化時會變成 6,

1
2
3
4
5
sizeof(CharChar)       2
sizeof(CharCharInt) 8
sizeof(IntCharChar) 8
sizeof(CharIntChar) 12
sizeof(CharShortChar) 6

通常在跨平台或資料結構的網路通訊中會需要資料對齊,
那麼如果要一定讓它強制對齊的話呢?Windows 與 Linux / macOS 作法不同,
在 Linux / macOS 下是這樣做,要加上 __attribute__((__packed__)),加在前或後都可以,適用於 GCC 編譯器,

cpp-sizeof6.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// g++ cpp-sizeof6.cpp -o a.out
#include <iostream>
using namespace std;

struct __attribute__((__packed__)) CharChar {
char c;
char c2;
};

struct __attribute__((__packed__)) CharCharInt {
char c;
char c2;
int i;
};

struct IntCharChar {
int i;
char c;
char c2;
} __attribute__((__packed__));

struct CharIntChar {
char c;
int i;
char c2;
} __attribute__((__packed__)) charintchar;

typedef struct CharShortChar {
char c;
short s;
char c2;
} __attribute__((__packed__)) CharShortChar_t;

int main() {
charintchar.i = 123;
CharShortChar_t charshortchar;
cout << "sizeof(CharChar) " << sizeof(CharChar) << '\n';
cout << "sizeof(CharCharInt) " << sizeof(CharCharInt) << '\n';
cout << "sizeof(IntCharChar) " << sizeof(IntCharChar) << '\n';
cout << "sizeof(CharIntChar) " << sizeof(CharIntChar) << '\n';
cout << "sizeof(CharShortChar) " << sizeof(CharShortChar) << '\n';

return 0;
}

我的平台輸出如下,

1
2
3
4
5
sizeof(CharChar)       2
sizeof(CharCharInt) 6
sizeof(IntCharChar) 6
sizeof(CharIntChar) 6
sizeof(CharShortChar) 4

在 Windows 下是這樣做,#pragma pack 這寫法原本只適用於 Visual C++ 編譯器,但後來 GCC 為了相容性也加入了這個語法的支援,

cpp-sizeof7.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// g++ cpp-sizeof7.cpp -o a.out
#include <iostream>
using namespace std;

//#pragma pack(push)
//#pragma pack(1)
#pragma pack(push, 1)
struct CharChar { char c; char c2; };
struct CharCharInt { char c; char c2; int i; };
struct IntCharChar { int i; char c; char c2; };
struct CharIntChar { char c; int i; char c2; };
struct CharShortChar { char c; short s; char c2; };
#pragma pack(pop)

int main() {
cout << "sizeof(CharChar) " << sizeof(CharChar) << '\n';
cout << "sizeof(CharCharInt) " << sizeof(CharCharInt) << '\n';
cout << "sizeof(IntCharChar) " << sizeof(IntCharChar) << '\n';
cout << "sizeof(CharIntChar) " << sizeof(CharIntChar) << '\n';
cout << "sizeof(CharShortChar) " << sizeof(CharShortChar) << '\n';

return 0;
}

我的平台輸出如下,同上,

1
2
3
4
5
sizeof(CharChar)       2
sizeof(CharCharInt) 6
sizeof(IntCharChar) 6
sizeof(CharIntChar) 6
sizeof(CharShortChar) 4

有關資料結構對齊問題的詳細分享無法在本篇完整解釋以後再另文說明。

以上就是 C/C++ sizeof 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

sizeof operator - cppreference.com
https://en.cppreference.com/w/cpp/language/sizeof
c++ - what is the difference between __attribute__((__packed__)); and #pragma pack(1) - Stack Overflow
https://stackoverflow.com/questions/32208805/what-is-the-difference-between-attribute-packed-and-pragma-pack1

其它相關文章推薦
如果你想學習 C/C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ typedef 用法與範例
C/C++ define 用法與範例

C/C++ define 用法與範例

本篇 ShengYu 介紹 C/C++ define 用法與範例。

以下 C/C++ define 用法介紹將分為這幾部份,

  • C/C++ define 基本用法
  • C/C++ define 單行巨集的範例
  • C/C++ define 多行巨集的範例
  • C/C++ 取消巨集
  • C/C++ define 來簡化函式
  • C/C++ 預定義巨集

那我們開始吧!

C/C++ define 基本用法

以下為 C/C++ define 語法,define 前面加上 #,define 最後面不需加上;分號,define 的順序跟 typedef 是不一樣的,容易搞混,需要特別注意一下,

1
#define <NEW_NAME> <EXIST_NAME>

例如,define PI 為 3.1415,define HELLO_WORLD_STR 為 “Hello World”,

1
2
#define PI 3.1415
#define HELLO_WORLD_STR "Hello World"

C/C++ define 單行巨集的範例

以下示範 C/C++ define 單行巨集,

cpp-define.cpp
1
2
3
4
5
6
7
8
9
10
11
// g++ cpp-define.cpp -o a.out
#include <stdio.h>

#define PI 3.1415
#define HELLO_WORLD_STR "Hello World"

int main() {
printf("PI: %f\n", PI);
printf("%s\n", HELLO_WORLD_STR);
return 0;
}

輸出如下,

1
2
PI: 3.141500
Hello World

C/C++ define 多行巨集的範例

以下示範 C/C++ define 多行巨集,多行可以用反斜線換行,

1
2
3
4
#define MACRO(arg1, arg2) do { \
stmt1; \
stmt2; \
} while(0)

例如:兩整數交換的 SWAP 可以這樣寫,

1
2
3
4
#define SWAP(x, y) \
int tmp = x; \
x = y; \
y = tmp;

實際的範例如下,

cpp-define2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// g++ cpp-define2.cpp -o a.out
#include <stdio.h>

#define SWAP(x, y) \
int tmp = x; \
x = y; \
y = tmp;

int main() {
int a = 3;
int b = 5;
printf("a: %d, b: %d\n", a, b);
SWAP(a, b);
printf("a: %d, b: %d\n", a, b);
return 0;
}

輸出如下,

1
2
a: 3, b: 5
a: 5, b: 3

C/C++ 取消巨集

C/C++ 取消巨集語法如下,使用 #undef 後面接上巨集名稱,

1
#undef <macro name>

undef 取消巨集範例如下,

cpp-define3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// g++ cpp-define3.cpp -o a.out
#include <stdio.h>

#define DEBUG
#undef DEBUG

int main() {
#ifdef DEBUG
printf("DEBUG\n");
#else
printf("NO DEBUG\n");
#endif
return 0;
}

輸出如下,

1
NO DEBUG

C/C++ define 來簡化函式

define 巨集可以用來簡化函式,例如我們 define 一個 DEBUG_PRINT 巨集用來將資料輸出到錯誤輸出上,

1
2
3
4
5
6
#define DEBUG_PRINT(fmt, ...) do { fprintf(stderr, fmt, __VA_ARGS__); } while (0)

// 使用範例
DEBUG_PRINT("error message");
// 使用範例
DEBUG_PRINT("error code: %d", errorcode);

或 Android 常見的 log 輸出函式,

1
2
3
4
5
6
7
8
#include <android/log.h>

#define TAG "MYDEBUG"
#ifdef DEBUG
#define ALOGD(...) do { __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__); } while (0)
#else
#define ALOGD(...) do {} while (0)
#endif

C/C++ 常用巨集

1
2
3
4
5
#define MIN(a, b) (a < b ? a : b)
#define MAX(a, b) (a > b ? a : b)
#define ADD(x, y) (x + y)
#define SUB(x, y) (x - y)
#define SWAP(x, y) int tmp = x; x = y; y = tmp;

寫 macro 時有時會遇到一些陷阱,例如乘法 MULT 這個巨集如下,

1
#define MULT(x, y) x * y

上面這種寫法乍看沒什麼問題,實際上會因為 MULT(1+1,2+2); 這樣使用後被展開成 1+1*2+2 = 5 而造成結果不對。
所以會改成下這樣寫法,加上括號,

1
#define MULT(x, y) (x) * (y)

這樣寫的話展開成 (1+1)*(2+2) = 2*4 = 8 結果就正確了。
最保險的方式是把每個變數都用括號把它包起來,這也就是為什麼常看到別人寫的巨集都多了好多括號的原因。

C/C++ 預定義巨集

以下是 C/C++ 常用的預定義巨集,

1
2
3
4
5
__FILE__,原始碼檔案路徑  
__LINE__,原始碼行數
__FUNCTION__,函式名稱
__func__,函式名稱
__VA_ARGS__,可變引數的巨集

以上就是 C/C++ define 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其他參考
c preprocessor - #define macro for debug printing in C? - Stack Overflow
https://stackoverflow.com/questions/1644868/define-macro-for-debug-printing-in-c
(Android系统)android log机制浅析_威猛の小脑斧的博客-CSDN博客___android_log_write
https://blog.csdn.net/lele_cheny/article/details/34858503
用macro的技巧 | 技術筆記
http://twmht.github.io/blog/posts/cc/macro.html

其它相關文章推薦
如果你想學習 C/C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ typedef 用法與範例
C/C++ sizeof 用法與範例

C/C++ typedef 用法與範例

本篇 ShengYu 介紹 C/C++ typedef 用法與範例,typedef 是將一個資料類型取一個別名,之後就可以用新的別名來宣告變數,以便簡化宣告語法。

以下 C/C++ typedef 的用法介紹將分為這幾部份,

  • C/C++ typedef 基本用法
  • C/C++ typedef struct 取別名

那我們開始吧!

C/C++ typedef 基本用法

C/C++ 中 typedef 是關鍵字,typedef 是將一個資料類型取一個別名,之後就可以用新的別名來宣告變數,通常是為了簡化宣告語法,讓程式碼更容易閱讀,以下為 C/C++ typedef 基本用法,

typedef 最後面需要加上 ; 分號,語法如下,

1
typedef <EXIST_TYPE> <NEW_TYPE>;

例如,將 char 取 int8_t 別名,將 int 取 int32_t 別名,

1
2
typedef char int8_t;
typedef int int32_t;

C/C++ typedef struct 取別名

這邊介紹 C/C++ 使用 typedef 把某個 struct 取別名,以下示範用 typedef 將 struct point 取一個 point_t 別名,之後宣告時就可以使用新的 point_t 別名,就可以省去加上 struct,藉此達到簡化宣告語法,

cpp-typedef.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// g++ cpp-typedef.cpp -o a.out
#include <stdio.h>
#include <string.h>

typedef struct point {
int x;
int y;
} point_t;

int main() {
point_t p1;
p1.x = 3;
p1.y = 5;
printf("x: %d\n", p1.x);
printf("y: %d\n", p1.y);

return 0;
}

輸出如下,

1
2
x: 3
y: 5

另外還可以把 struct 的定義跟 typedef 分開寫,像這樣寫,

1
2
3
4
5
6
struct point {
int x;
int y;
};

typedef struct point point_t;

以上就是 C/C++ typedef 的用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
如果你想學習 C/C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ struct 用法與範例
C/C++ enum 用法與範例
C/C++ union 用法與範例

Android adb install INSTALL_FAILED_UPDATE_INCOMPATIBLE 解決方法

本篇介紹 Android adb install apk 出現 INSTALL_FAILED_UPDATE_INCOMPATIBLE 解決方法。

adb 指令的安裝方式可以參考這篇

adb install apk INSTALL_FAILED_UPDATE_INCOMPATIBLE 錯誤

用 adb install abc.apk 時遇到 INSTALL_FAILED_UPDATE_INCOMPATIBLE 錯誤訊息時怎麼辦,

解決方法:
簡單的方式是移除原本的套件 package,用 adb uninstall 移除該套件名 package name,如果移除套件 com.xxx.xxx 就這樣下,

1
adb uninstall com.xxx.xxx

參考
android - INSTALL_FAILED_UPDATE_INCOMPATIBLE when I try to install compiled .apk on device - Stack Overflow
https://stackoverflow.com/questions/11891848/install-failed-update-incompatible-when-i-try-to-install-compiled-apk-on-device

相關主題
Android adb 基本用法教學
Android adb logcat 基本用法教學
Android adb logcat 出現 read unexpected eof 解決方法
Android adb 同步時間/設定時間
Android adb shell input 事件用法
Android adb forward 通訊埠轉發用法教學

3種免費遠端桌面軟體推薦

本篇 ShengYu 介紹 Windows/macOS/Ubuntu 跨平台免費遠端桌面軟體。

以下3種免費遠端桌面軟體推薦分為

  • TeamViewer
  • Chrome Remote Desktop
  • AnyDesk

TeamViewer

TeamViewer 是一款用於遠端存取以及遠端控制和遠端維護電腦和其他終端裝置的軟體,發布於 2005 年。功能性被逐步擴大。TeamViewer 不需要註冊,非商業領域免費使用,這有助於軟體的廣泛傳播。

多平台支援性與特色
Server伺服端(被控制)支援平台:Windows / macOS / Linux / Chrome OS / Raspberry Pi
Client客戶端(控制)支援手機行動裝置:Yes
需要外網IP:不需要
需要帳號:不需要註冊帳號

下載安裝
電腦版(Windows/macOS/Ubuntu):Chrome 遠端桌面
Chrome 擴充:Chrome Remote Desktop
Android:TeamViewer遠端控制版本
iOS:TeamViewer Remote Control

官網連結:https://www.teamviewer.com/

Chrome Remote Desktop

Chrome 遠端桌面是 Google 開發的遠端桌面軟體工具,允許使用者通過 Google 開發的專有協議(內部稱為Chromoting)遠端控制其他電腦的桌面。特色為快速且完全免費。需要 Chrome 瀏覽器跟 google 帳號。

多平台支援性與特色
Server伺服端(被控制)支援平台:Windows / macOS / Linux
Client客戶端(控制)支援手機行動裝置:Yes
需要外網IP:不需要
需要帳號:需要註冊 Gmail 帳號

下載安裝
電腦網頁版:Chrome 遠端桌面
Chrome 擴充:Chrome Remote Desktop
Android:Chrome 遠端桌面
iOS:Chrome 遠端桌面

AnyDesk

AnyDesk 是一款由德國公司 AnyDesk Software GmbH 推出的遠端桌面軟體。使用者可以通過該軟體遠端控制電腦,同時還能與被控制的電腦之間進行文件傳輸。是近幾年的新興免費遠端桌面軟體。

多平台支援性與特色
Server伺服端(被控制)支援平台:Windows / macOS / Linux / FreeBSD / Raspberry Pi / Chrome OS
Client客戶端(控制)支援手機行動裝置:Yes
需要外網IP:不需要
需要帳號:不需要註冊帳號

下載安裝
Windows:AnyDesk 遠端桌面軟體
macOS:AnyDesk 遠端桌面軟體
Android:AnyDesk 遠端桌面軟體
iOS:AnyDesk 遠端桌面軟體

官網連結:https://anydesk.com

以上就是3種免費遠端桌面軟體的介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它參考
【遠端桌面】4款2022年最常用免費遠端控制軟體分享!TeamViewer、Chrome、AnyDesk、Wayk Now
https://kikinote.net/157800