C/C++ strncpy 用法與範例

本篇 ShengYu 介紹 C/C++ strncpy 用法與範例,strncpy 是用來複製字串的函式,跟 strcpy 函式功能一樣是複製字串,但 strncpy 安全性更高,strncpy 詳細用法範例請繼續往下閱讀。

C 語言要複製 c-style 字串可以使用 strncpy,要使用 strncpy 的話需要引入的標頭檔 <string.h>,如果要使用 C++ 的標頭檔則是引入 <cstring>
strncpy 函式原型為

1
char * strncpy(char * destination, const char * source, size_t num);

strncpy 是將 source 字串的前 num 個字元複製到 destination。如果在複製了 num 個字元之前遇到 source 字串的結尾,則 destination 後續將填充零,直到總共複製了 num 個字元為止。
如果 source 比 num 長,destination 尾部不會加上 \0 結束字元。因此在這種情況下,destination 不應被視為以 \0 結束字元結尾的 C 字串(需注意溢出問題)。
destination 和 source 不得重疊(當重疊時請參考更安全的 memmove 替代方案)。

以下來看看 strncpy 怎麼複製字串,

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

int main() {
char str[] = "Hello world";
char str2[64];
strncpy(str2, str, sizeof(str2)-1);
printf("%s\n", str2);

char str3[64];
strncpy(str3, "This is a string", sizeof(str3)-1);
printf("%s\n", str3);
return 0;
}

結果如下,

1
2
Hello world
This is a string

常見考題

請問下列程式有什麼問題?

1
2
3
const char* src = "abcdefghij";
char dst[10];
strcpy(dst, src);

問題在於 dst 分配了 10 大小的 char 字元陣列 char dst[10]; 總共有 10 個位置,而 src 字串實際佔 11 字元空間(包含結束字元),strcpy 時會造成 buffer overflow,意思就是 abcdefghij 這個字串 需要 11 個字元的位置才夠放,

在實務上通常會需要儘量少用 strcpy, 而改用 strncpy,strncpy 比 strcpy 多了第三個引數,第三個引數是傳入要複製幾個字元的數量,
所以我們這邊複製字串時最大長度不應超過 dst 的大小,

1
2
strncpy(dst, src, sizeof(dst)-1);
dst[sizeof(dst)-1] = '\0';

範例程式如下,

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

int main() {
const char* src = "abcdefghij";
char dst[10];

strncpy(dst, src, sizeof(dst)-1);
dst[sizeof(dst)-1] = '\0';

printf("%s\n", dst);
return 0;
}

輸出結果如下,

1
abcdefghi

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

其它參考
strncpy - C++ Reference
https://www.cplusplus.com/reference/cstring/strncpy/

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

C/C++ Linux pthread_setname_np 設定執行緒名稱用法

本篇 ShengYu 介紹 C/C++ Linux/Unix 執行緒 pthread_setname_np() 用法,pthread_setname_np 是用來設定該執行緒名稱的函式,詳細用法範例請繼續往下閱讀。

用 pthread_setname_np 設定 pthread_create 出來的執行緒名稱

在 C 或 C++ 中使用 pthread 設定執行緒名稱可以透過 pthread_setname_np 函式來達成。這是一個非標準的 POSIX 擴展,因此在不同的系統上可能會有所不同。通常您可以這樣使用它:

cpp-pthread_setname_np.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-pthread_setname_np.cpp -o a.out -pthread
#include <stdio.h>
#include <pthread.h>

void * thread_func(void* arg) {
// 設定執行緒名稱
char name[16] = "MyThread";
pthread_setname_np(pthread_self(), name);

// ...

return NULL;
}

int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);

// 等待執行緒結束
pthread_join(tid, NULL);

return 0;
}

在這個例子中,pthread_setname_np 函式用來設定執行緒名稱為 MyThread。請注意該名稱通常只能設定 15 個字元再加一個結束字元,超過長度可能會設定名稱失敗。

用 pthread_setname_np 設定 C++ std::thread 中執行緒名稱

C++11 的 std::thread 並沒有直接提供設定執行緒名稱的功能,但你還是可以使用 pthread_setname_np 來為 std::thread 設定名稱。以下是一個範例:

cpp-pthread_setname_np2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// g++ cpp-pthread_setname_np2.cpp -o a.out -pthread
#include <iostream>
#include <thread>
//#include <pthread.h>?
//#include <cstring>

void thread_func() {
// 設定執行緒名稱為 "MyThread"
pthread_setname_np(pthread_self(), "MyThread");

// ...
}

int main() {
std::thread t(thread_func);

// 等待執行緒結束
t.join();

return 0;
}

在這個例子中是使用 pthread_setname_np 來設定 C++ std::thread 執行緒的名稱為 MyThread

以上就是 C/C++ Linux pthread_setname_np 設定執行緒名稱用法介紹。
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
C/C++ 新手入門教學懶人包
C/C++ Linux/Unix pthread 建立多執行緒用法與範例
C/C++ Linux pthread_attr_setstacksize 設定執行緒堆疊大小用法
C/C++ Linux/Unix 讓執行緒跑在指定 CPU 的方法 sched_setaffinity
C++ std::thread 建立多執行緒用法與範例

C++ std::optional 用法與範例

本篇 ShengYu 介紹 C++ std::optional 用法與範例,std::optional 是 C++17 中引入的一個 template 樣板類別,它代表一個可能存在也可能不存在的值,也就是有值或沒有值。你可以將它視為一個安全的容器,用於存儲可能為空的值。std::optional 它的主要用途是解決傳統的指標和參考可能為空的問題,從而增強了程式碼的安全性和可讀性。

具體來說,std::optional 允許你在一個容器中儲存值,同時可以指示該值是否存在。這對於那些可能回傳空值的函式或者需要可選參數的函式來說非常有用。以下是 std::optional 的一些基本用法。

要使用 std::optional 的話,需要引入的標頭檔: <optional>

C++ 建立 std::optional

這邊介紹 C++ 建立一個空的 std::optional,在建立 std::optional 時,你可以指定它的類型,也可以讓編譯器推導出類型。如果你沒有給出初始值,則建立出來的 std::optional 將是空的。

1
2
std::optional<int> opt;
std::optional<string> opt2;

將值放入 std::optional,將值放入 std::optional 中,這樣它就不再是空的。

1
2
std::optional<int> opt = 123;
std::optional<string> opt2 = "hello";

檢查 std::optional 是否有值

C++ 在使用 std::optional 中的值之前,你可能需要檢查它是否存在,以避免未定義行為。以下是個簡單的檢查 std::optional 是否有值的範例,

std-optional.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++ std-optional.cpp -o a.out -std=c++17
#include <iostream>
#include <string>
#include <optional>

using namespace std;

int main() {
std::optional<int> opt = 123;
if (opt.has_value()) {
cout << "opt has value" << endl;
// 執行當前存在值的程式碼...
} else {
cout << "opt no value" << endl;
// 執行當前不存在值的程式碼...
}

std::optional<std::string> opt2;
if (opt2.has_value()) {
cout << "opt2 has value" << endl;
} else {
cout << "opt2 no value" << endl;
}
return 0;
}

結果輸出如下,

1
2
opt has value
opt2 no value

獲取 std::optional 中的值

在 C++ 中你可以使用 value() 方法從 std::optional 中獲取值,但要小心,如果 std::optional 是空的,這可能會拋出異常。另一個選擇是使用 value_or() 方法,它在 std::optional 為空時回傳預設值。

1
2
3
4
int value = opt.value(); // 如果沒有值,這可能會拋出異常
// 或者
int value = opt.value_or(default_value); // 如果沒有值,回傳預設值
int value = opt.value_or(0); // 如果沒有值,回傳預設值 0

重設 std::optional 中的值

你可以使用 reset() 方法將 std::optional 重設為空。

std-optional2.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++ std-optional2.cpp -o a.out -std=c++17
#include <iostream>
#include <optional>

using namespace std;

int main() {
std::optional<int> opt = 123;
if (opt.has_value()) {
cout << "opt has value" << endl;
} else {
cout << "opt no value" << endl;
}

opt.reset(); // 將 `std::optional` 重設為空

if (opt.has_value()) {
cout << "opt has value" << endl;
} else {
cout << "opt no value" << endl;
}
return 0;
}

結果輸出如下,

1
2
opt has value
opt no value

檢索 std::optional 中的值,並且同時判斷是否存在

在某些情況下,你可能需要同時檢索 std::optional 中的值並檢查它是否存在。你可以使用 auto 關鍵字以及 if 陳述式的新形式來實現這一目的。

1
2
3
4
5
6
7
8
9
10
11
std::optional<int> getValue() {
std::optional<int> num = 123;
return num;
}

std::optional<int> opt = getValue();
if (auto value = opt; value) {
// 執行當前存在值的程式碼,同時 `value` 中存有值
} else {
// 執行當前不存在值的程式碼
}

上述範例介紹 std::optional 用在函式換回傳值的範例,std::optional 的出現讓 function 回傳值多了一個選擇,有值或是 nullopt,讓人能更清楚表達程式碼用意。

以上是 std::optional 的一些基本用法,讓你可以更安全地處理可能為空的值。以下介紹 std::optional 常見使用情況。

std::optional<int> 的常見用法

以下介紹 std::optional 有設定 int 值跟沒設定 int 值的使用情況,std::optional 沒設定值或者設定成 std::nullopt 都會讓判斷式變成 false,

std-optional3.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
// g++ std-optional3.cpp -o a.out -std=c++17
#include <iostream>
#include <optional>

using namespace std;

void test_optional_int(std::optional<int> num) {
if (num) {
int n = num.value();
cout << "true, " << n << endl;
} else {
cout << "false" << endl;
}

if (num.has_value()) {
auto n = num.value();
cout << "has value, " << n << endl;
} else {
cout << "no value" << endl;
}

if (num == std::nullopt) {
cout << "nullopt" << endl;
} else {
auto n = num.value();
cout << "not nullopt, " << n << endl;
}
}

int main() {
std::optional<int> num;
test_optional_int(num);
cout << "---" << endl;
num = 123;
test_optional_int(num);
cout << "---" << endl;
num = std::nullopt;
test_optional_int(num);
return 0;
}

結果輸出如下,

1
2
3
4
5
6
7
8
9
10
11
false
no value
nullopt
---
true, 123
has value, 123
not nullopt, 123
---
false
no value
nullopt

std::optional<string> 的常見用法

以下介紹 std::optional 有設定 string 值跟沒設定 string 值的使用情況,std::optional 沒設定值或者設定成 std::nullopt 都會讓判斷式變成 false,

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
// g++ std-optional4.cpp -o a.out -std=c++17
#include <iostream>
#include <string>
#include <optional>

using namespace std;

void test_optional_int(std::optional<std::string> str) {
if (str) {
std::string s = str.value();
cout << "true, " << s << endl;
} else {
cout << "false" << endl;
}

if (str.has_value()) {
auto s = str.value();
cout << "has value, " << s << endl;
} else {
cout << "no value" << endl;
}

if (str == std::nullopt) {
cout << "nullopt" << endl;
} else {
auto s = str.value();
cout << "not nullopt, " << s << endl;
cout << "not nullopt, " << (*str) << endl; // 其他寫法
printf("not nullopt, %s\n", (*str).c_str()); // 其他寫法
}
}

int main() {
std::optional<std::string> str;
test_optional_int(str);
cout << "---" << endl;
str = "hello";
test_optional_int(str);
cout << "---" << endl;
str = std::nullopt;
test_optional_int(str);
return 0;
}

結果輸出如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
false
no value
nullopt
---
true, hello
has value, hello
not nullopt, hello
not nullopt, hello
not nullopt, hello
---
false
no value
nullopt

使用 std::optional 後確實讓 code 看起來更清楚,更能清楚表達程式碼與原作者的意涵。

std::optional 包 struct/class 的用法

這邊介紹 std::optional 包 struct 或是 class 的用法,std::optional 也可以用 -> 來直接存取 member 或 member function,範例如下,

std-optional5.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
// g++ std-optional5.cpp -o a.out -std=c++17
#include <iostream>
#include <string>
#include <optional>

using namespace std;

struct Student {
uint32_t id;
std::string name;
};

int main() {

Student student;
student.id = 100;
student.name = "Alice";
std::optional<Student> optStudent1;
std::optional<Student> optStudent2;
optStudent2 = student;

if (optStudent1) {
printf("optStudent1 id = %d\n", optStudent1->id);
printf("optStudent1 name = %s\n", optStudent1->name.c_str()); // 存取 member function
printf("optStudent1 name size = %zu\n", optStudent1->name.size()); // 存取 member function
}

if (optStudent2) {
printf("optStudent2 id = %d\n", optStudent2->id);
printf("optStudent2 name = %s\n", optStudent2->name.c_str()); // 存取 member function
printf("optStudent2 name size = %zu\n", optStudent2->name.size()); // 存取 member function
}

return 0;
}

結果輸出如下,

1
2
3
optStudent2 id = 100
optStudent2 name = Alice
optStudent2 name size = 5

C++ std::optional 的主要用途

以下是一些 std::optional 的主要用途:

  1. 代替可能為空的指標或參考:通常情況下,我們可能會用指標或參考來表示一個可能為空的值。但是指標可能為空,並且需要額外的解參考操作來獲取實際的值,這會增加程式碼的覆雜度和潛在的錯誤。使用 std::optional 可以更加直觀地表達這種情況。
  2. 函式回傳值可能為空:有些函式可能無法始終返回有效的結果,而是可能返回空。使用 std::optional 可以清晰地表示函式的回傳值可能為空,並且無需使用特殊的回傳值表示空。
  3. 避免使用異常處理空值:在早期有時會使用異常來表示一個函式無法返回有效結果的情況。但是異常處理可能會帶來額外的開銷,並且可能使程式碼更難以理解。std::optional 提供了一種更加顯式的方式來處理可能為空的值,而無需使用異常。
  4. 提高程式碼的可讀性和安全性:通過使用 std::optional,程式碼的意圖更加明確,可以減少錯誤的發生,並且使程式碼更容易理解和維護。

總之,std::optional 是 C++ 中一種非常有用的工具,可以有效地處理可能為空的值,提高程式碼的安全性和可讀性。

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

其它相關文章推薦
C/C++ 新手入門教學懶人包
std::string 用法與範例
std::vector 用法與範例
std::sort 用法與範例
std::map 用法與範例
std::set 用法與範例

C++ std::front_inserter 用法與範例

本篇 ShengYu 介紹 C++ std::back_inserter 用法與範例,在 C++ 中,標準庫提供了許多功能豐富的工具,其中之一就是 std::front_inserter。這個小小的插入迭代器可能容易被忽視,但它卻有著強大的功能,特別是在處理容器時。本文將深入探討 std::front_inserter 的使用方式,並通過一些例子來展示它的神奇之處。

什麼是 std::front_inserter?

在開始之前,讓我們先瞭解一下 std::front_inserter 的基本概念。它是一個插入迭代器,用於將元素插入到容器的開頭位置,而不是尾部。這意味著,當你使用 std::copy 或其他演算法來將元素從一個序列插入到另一個序列時,你可以選擇將它們插入到目標容器的開頭。

如何使用 std::front_inserter?

現在讓我們來看看如何使用 std::front_inserter。以下是一個簡單的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
#include <iterator>

int main() {
std::vector<int> vec = {2, 3, 4};
std::deque<int> deque = {1, 5};

std::copy(vec.begin(), vec.end(), std::front_inserter(deque));

for (const auto& d : deque) {
std::cout << d << " ";
}
std::cout << std::endl;

return 0;
}

結果輸出如下,

1
4 3 2 1 5

在這個例子中,我們分別有 std::vector<int>vecstd::deque<int>deque。我們想要將 vec 中的元素插入到 deque 的前端。我們使用 std::copy 函數來實現這一目的,並將 std::front_inserter(deque) 作為目標插入迭代器。

那可以將元素插入到 std::vector 的前端嗎?答案是不行,會編譯失敗,因為 std::vector 容器沒有 push_front 成員函式可使用,所以基本上要注意該容器有沒有頭部的操作成員函式,例如:std::deque。

std::front_inserter 更多用法示例

除了與 std::copy 一起使用之外,std::front_inserter 還可以與其他演算法和插入迭代器一起使用。以下是一些常見的用法:

  1. 使用 std::transform:將一個序列的元素轉換後插入到另一個容器的前端。

    1
    std::transform(src.begin(), src.end(), std::front_inserter(dst), someFunction);
  2. 使用 std::generate:使用生成器函數將元素插入到容器的前端。

    1
    std::generate_n(std::front_inserter(deque), count, someGeneratorFunction);
  3. 直接插入元素:直接將元素插入到容器的前端。

    1
    2
    std::deque<int> deque = {2, 3, 4};
    std::front_inserter(deque) = 1; // 將1插入到deque的前端
  4. 使用 std::copy_if:根據某個條件將符合條件的元素插入到容器的前端。

    1
    std::copy_if(src.begin(), src.end(), std::front_inserter(dst), somePredicate);

總結

std::front_inserter 是 C++ 中一個強大且實用的工具,特別是在處理容器時。它允許我們將元素插入到容器的開頭位置,這為我們提供了更靈活的方式來操作序列。無論是將一個序列插入到另一個序列的開始處,還是根據特定條件插入元素,std::front_inserter 都能幫助我們輕鬆實現。下次在你的 C++ 程式中,記得考慮使用這個強大的工具!

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

其它相關文章推薦
C/C++ 新手入門教學懶人包
std::copy 用法與範例
std::string 用法與範例
std::vector 用法與範例
std::sort 用法與範例
std::map 用法與範例

C++ std::back_inserter 用法與範例

本篇 ShengYu 介紹 C++ std::back_inserter 用法與範例,std::back_inserter是一個標準庫中的函式模板,可以讓你輕鬆地將元素插入到容器的尾部,而不需要事先知道容器的大小。它通常與標準庫中的演算法一起使用,例如std::copy,用於將範圍內的元素複製到容器中。你可以將std::back_inserter傳遞給這些演算法,以便自動將元素插入到容器的尾部。本文將帶你深入探索std::back_inserter的用法,並展示如何與不同的標準庫演算法結合使用。

什麼是 std::back_inserter?

std::back_inserter是C++標準庫中的一個函式模板,它允許將元素插入到容器的尾部,而不需要提前分配內存。這使得在不知道容器大小的情況下,動態地向容器新增元素。

C++ std::back_inserter 基本用法

讓我們從一個簡單的 C++ std::back_inserter 示例開始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> vec1 = {1, 2, 3, 4};
std::vector<int> vec2;

std::copy(vec1.begin(), vec1.end(), std::back_inserter(vec2));

// 現在vec2包含與vec1相同的元素
for (int num : vec2) {
std::cout << num << " ";
}

return 0;
}

在這個示例中,std::copyvec1的元素複製到vec2中,而std::back_inserter(vec2)則表示將元素插入到vec2的尾部。結果輸出如下,

1
1 2 3 4

C++ std::back_inserter 其他常見用法

除了與std::copy一起使用之外,std::back_inserter還可以與其他標準庫演算法和容器一起使用。以下是幾種常見的用法:

  1. 使用std::copy_if將符合條件的元素從一個範圍複製到另一個容器中:
    1
    2
    3
    std::vector<int> vec1 = {1, 2, 3, 4, 5};
    std::vector<int> vec2;
    std::copy_if(vec1.begin(), vec1.end(), std::back_inserter(vec2), [](int n) { return n % 2 == 0; });

結果輸出如下,

1
2 4

  1. 使用std::transform對範圍內的元素進行轉換,並將結果插入到另一個容器中:
    1
    2
    3
    std::vector<int> vec1 = {1, 2, 3, 4, 5};
    std::vector<int> vec2;
    std::transform(vec1.begin(), vec1.end(), std::back_inserter(vec2), [](int n) { return n * 2; });

結果輸出如下,

1
2 4 6 8 10

  1. 使用std::generate_n生成特定數量的元素,並將其插入到容器的尾部:
    1
    2
    std::vector<int> vec;
    std::generate_n(std::back_inserter(vec), 5, []() { return rand() % 100; });

結果輸出如下,

1
7 49 73 58 30

這些示例展示了std::back_inserter與不同演算法的結合使用,它使得在不需要事先知道目標容器大小的情況下,能夠方便地將元素插入到容器的尾部。

結語

std::back_inserter是C++中一個極為實用的工具,它為動態插入元素提供了便利的方法。通過與標準庫中的演算法結合使用,你可以更加靈活地處理容器中的數據,並實現各種功能。希望本文能幫助你更好地理解和應用std::back_inserter,並提高你的C++寫程式技能。

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

其它相關文章推薦
C/C++ 新手入門教學懶人包
std::copy 用法與範例
std::string 用法與範例
std::vector 用法與範例
std::sort 用法與範例
std::map 用法與範例

C++ std::copy 用法與範例

本篇 ShengYu 介紹 C++ std::copy 用法與範例,在 C++ 中,標準庫提供了許多強大的工具和函數,其中之一就是 std::copy。這個函數讓我們能夠輕鬆地將一個範圍的元素從一個位置複製到另一個位置。在這篇文章中,我們將探索 std::copy 的基本用法以及一些更進階的技巧。

C++ std::copy 除了基本用法外,它還可以接受不同的迭代器類型,從而可以在不同類型的容器之間進行複製。它還支援複製部分範圍以及將元素插入到容器的特定位置。std::copy 是 C++ 中進行元素複製操作的一個通用且強大的工具。

C++ std::copy 基本用法

首先,讓我們看一下 std::copy 的基本用法。這個函數定義在 <algorithm> 標頭檔案中。它是標準庫中的一個演算法,使用起來非常簡單:

1
2
3
#include <algorithm>

std::copy(起始位置, 結束位置, 目標位置);

其中,起始位置結束位置 定義了源範圍的區間,而 目標位置 定義了目標範圍的起始位置。例如,如果你有兩個向量 sourcedestination,你可以將 source 的一部分元素複製到 destination,讓我們通過一個簡單的示例來理解這個概念:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination(5); // 預先分配大小

std::copy(source.begin(), source.end(), destination.begin());

for (const auto& num : destination) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

在這個示例中,我們將 source 中的元素複製到了 destination 中,最終輸出如下,

1
1 2 3 4 5

除了基本用法外,std::copy 還有一些進階的用法,讓我們一起繼續往下閱讀看看。

std::copy 使用 std::back_inserter 插入元素到容器末尾

在這個示例中,我們創建了一個空的 destination 向量,然後使用 std::copy 將 source 向量中的元素複製到 destination 中。不同的是,我們將 std::back_inserter(destination) 傳遞給 std::copy 作為目標位置。std::back_inserter 是一個插入迭代器,當它被用於 std::copy 時,它會自動插入元素到容器 destination 的末尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination;

std::copy(source.begin(), source.end(), std::back_inserter(destination));

for (const auto& num : destination) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

std::copy 使用 std::ostream_iterator 將元素複製到輸出流

這個示例中,我們使用 std::copy 將 source 向量中的元素複製到 std::ostream_iterator<int>,這是一個迭代器,它將元素寫入到輸出流 std::cout 中。std::ostream_iterator<int>(std::cout, " ") 中的 " " 空格是指定元素之間的分隔符號,你也可以改用 , 逗號作為分隔符號。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {
std::vector<int> source = {1, 2, 3, 4, 5};

std::copy(source.begin(), source.end(), std::ostream_iterator<int>(std::cout, " "));
//std::copy(source.begin(), source.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << std::endl;

return 0;
}

std::copy 複製部分範圍到不同位置

在這個示例中,我們使用 std::copy 將 source 向量中索引從 1 到 3 的元素(不包括索引為 4 的元素)複製到 destination 向量的開始位置。這顯示了 std::copy 可以用於複製範圍的子集到另一個容器的開始位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination(3);

std::copy(source.begin()+1, source.begin()+4, destination.begin());

for (const auto& num : destination) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

最終輸出如下,

1
2 3 4

以上這些示例展示了 std::copy 的不同用法,從將元素複製到容器中,到將元素複製到輸出流中,以及從一個範圍複製子集到另一個容器。這些是 std::copy 的一些常見用法示例,它提供了許多靈活的選項,讓我們能夠輕鬆地處理各種複製需求。從基本的複製到不同容器,到複製到輸出流,再到複製部分範圍,std::copy 讓我們的代碼更加優雅和易於閱讀。根據具體的需求,你可以選擇適合的方法來使用它。

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

其它相關文章推薦
C/C++ 新手入門教學懶人包
std::back_inserter 用法與範例
std::front_inserter 用法與範例
std::string 用法與範例
std::vector 用法與範例
std::sort 用法與範例

C++17 的新特性:讓寫程式更簡潔、更高效

本篇 ShengYu 紀錄 C++17 的幾個新特性,隨著計算機科學的不斷發展,程式語言也在不斷演進,以滿足開發人員日益增長的需求。C++17 引入了許多令人振奮的新功能和特性,旨在使寫程式變得更加簡潔、更加高效。在本文中,我們將探討 C++17 中的一些重要特性,並通過簡單易懂的示例演示其用法和優勢。

C++17 新增了那些功能跟特色?

C++17 引入了許多新功能和特性,其中一些包括:

  1. 結構化綁定:允許從元組或其他結構中解構數據成員,使代碼更清晰易讀。
  2. constexpr if:在編譯時根據條件進行模板的分支選擇,增強了模板元編程的能力。
  3. 並行演算法函式庫:引入了一些並行執行的標準演算法,提高了性能。
  4. 新的屬性和屬性修飾符:包括 [[nodiscard]]、[[fallthrough]] 等,用於更好地控制編譯器行為。
  5. 巢狀命名空間:允許在命名空間內部巢狀其他命名空間,提高了代碼的模塊化程度。
  6. 修正了一些語言和庫的缺陷和不一致性,提高了語言的一致性和易用性。

根據以上幾個項目,讓我們更詳細地討論每個項目,並提供一些示例:

1. 結構化綁定:清晰解構數據

C++17 引入了結構化綁定,讓我們可以輕松地從元組或其他結構中解構數據成員,從而使代碼更加清晰易讀。

例如,在迭代器循環中使用結構化綁定,可以這樣做:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <map>

int main() {
std::map<int, std::string> myMap{{1, "One"}, {2, "Two"}, {3, "Three"}};

for (const auto& [key, value] : myMap) {
std::cout << "Key: " << key << ", Value: " << value << std::endl;
}

return 0;
}

這種結構化的方式使得我們可以直觀地訪問鍵值對的內容,讓代碼更加易於理解和維護。

例如,在標準庫類型中使用結構化綁定,可以這樣做:

1
2
3
4
5
6
7
8
9
10
11
12
#include <tuple>
#include <iostream>

int main() {
std::tuple<int, double, std::string> data{42, 3.14, "Hello"};

auto [num, value, text] = data;

std::cout << "num: " << num << ", value: " << value << ", text: " << text << std::endl;

return 0;
}

在函數返回值中使用結構化綁定,可以這樣做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <tuple>

std::tuple<int, double, std::string> getTuple() {
return std::make_tuple(42, 3.14, "Hello");
}

int main() {
auto [num, value, text] = getTuple();

std::cout << "num: " << num << ", value: " << value << ", text: " << text << std::endl;

return 0;
}

當涉及到結構化綁定時,你都可以在迭代器循環、函數返回值以及標準庫類型中使用。

2. constexpr if:編譯時條件分支

在 C++17 中引入了 constexpr if,它允許在編譯時根據條件進行模板的分支選擇,從而增強了模板元編程的能力。下面是一個簡單的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

template <typename T>
void process(T value) {
if constexpr(std::is_integral<T>::value) {
std::cout << "Integral value: " << value << std::endl;
} else {
std::cout << "Non-integral value: " << value << std::endl;
}
}

int main() {
process(42); // 輸出:Integral value: 42
process(3.14); // 輸出:Non-integral value: 3.14

return 0;
}

這種在編譯時進行條件分支選擇的能力使得我們可以更加靈活地控制代碼的行為,提高了代碼的效率和可維護性。

3. 並行演算法函式庫:提升性能

C++17 還引入了一些標準演算法的並行版本,可以利用多核處理器提高性能。例如,我們可以使用並行排序演算法來加速排序過程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>
#include <algorithm>
#include <execution>

int main() {
std::vector<int> data{3, 1, 4, 1, 5, 9, 2, 6, 5};

// 並行排序
std::sort(std::execution::par, data.begin(), data.end());

for (int num : data) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

通過利用並行演算法函式庫,我們可以更充分地利用硬體資源,加速程式的執行。

4. 新的屬性和屬性修飾符:更好的控制編譯器行為

C++17 引入了一些新的屬性和屬性修飾符,例如[[nodiscard]]、[[fallthrough]]等,可以更好地控制編譯器的行為。例如,我們可以使用[[nodiscard]]屬性來告訴編譯器在忽略函數返回值時發出警告:

1
2
3
4
5
6
7
8
[[nodiscard]] int add(int a, int b) {
return a + b;
}

int main() {
add(3, 4); // 如果不使用返回值,編譯器會發出警告
return 0;
}

這些新的屬性和屬性修飾符使得我們可以更加精確地指導編譯器對代碼進行優化,提高了代碼的健壯性和可靠性。

5. 巢狀命名空間

C++17 允許在命名空間內部巢狀其他命名空間,提高了代碼的模塊化程度。例如:

1
2
3
4
5
6
7
8
9
10
11
12
namespace outer {
namespace inner {
void hello() {
std::cout << "Hello from inner namespace!" << std::endl;
}
}
}

int main() {
outer::inner::hello(); // 輸出:Hello from inner namespace!
return 0;
}

以上這些例子展示了 C++17 中新增功能的一些用法和優勢。

C++17 新增函式庫特點

C++17 新增函式庫包含了以下這些特點,

  • std::variant
  • std::optional
  • std::any
  • std::string_view
  • std::invoke
  • std::apply
  • std::filesystem
  • std::byte
  • splicing for maps and sets
  • parallel algorithms
  • std::sample
  • std::clamp
  • std::reduce
  • prefix sum algorithms
  • gcd and lcm
  • std::not_fn
  • string conversion to/from numbers

總結

C++17 帶來了許多令人激動的新特性,從結構化綁定到constexpr if,從並行演算法函式庫庫到新的屬性和屬性修飾符,每一個都為我們提供了更多的工具和技術來編寫更好的代碼。通過利用這些新特性,我們可以使代碼更加簡潔、高效,從而提高開發效率和程式性能。隨著更多項目逐漸遷移到 C++17 平台,相信它將成為未來 C++ 開發的重要基礎。

其他參考
https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP17.md

以上就是 C++17 的新特性介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
C/C++ 新手入門教學懶人包
std::optional 用法與範例
std::string 用法與範例
std::vector 用法與範例
std::sort 用法與範例
std::map 用法與範例

VS Code 使用 Code Runner 更改預設執行的編譯參數的方法

本篇記錄 Visual Studio Code (VS Code) 使用 Code Runner 時,更改預設執行的編譯參數的方法,在左下角齒輪按鈕按下選擇 Settings > 搜尋欄輸入 code-runner.executorMap > 點選 Edit in settings.json

settings.json 裡進行修改,這個修改是全範圍的影響喲,

1
2
3
4
5
"code-runner.executorMap": {
"javascript": "node",
"java": "cd $dir && javac $fileName && java $fileNameWithoutExt",
"c": "cd $dir && gcc $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
"cpp": "cd $dir && g++ -std=c++14 -pthread $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",

如果是 C++ 程式的話就再 "cpp": 這一欄後面加上編譯參數。存檔後就生效囉!

其它相關文章推薦
Visual Studio Code 常用快捷鍵
Visual Studio Code 找對應大括號的快捷鍵
C/C++ clang-format 對程式碼風格排版格式化
Qt Creator 常用快捷鍵
vim 常用快捷鍵

2024 台股 ETF 股利通知申請電子帳單方法

本篇介紹台股 ETF 股利通知申請電子帳單方法,每年都會收到鼓利通知書或ETF的收益分配通知書,配息越多次收到的通知書越多,長期累積起來的鼓利通知書也很驚人。

近幾年來開始不想收到紙本,因為一大堆紙本保存不易,日後也很難回頭檢視,最後下場都是丟進垃圾桶。現在只要動動手指就可以線上完成申請電子帳單,輕鬆做環保,不必再費心處理紙本個人資料,保存也更方便,線上申請幾分鐘即可搞定,之後便可第一時間掌握股利消息,同時降低紙本通知書個資外洩的風險。

Read More