简介

  • C/C++ 编程语言理论基础

C++ 默认参数

在 C++ 中,函数的默认参数是一种允许您为函数的参数提供默认值的机制。这意味着您可以在定义函数时指定某个参数的默认值,如果调用函数时未提供该参数的值,函数将使用默认值。这对于使函数更灵活且易于使用非常有用。

以下是一个简单的示例,演示了如何在 C++ 函数中使用默认参数:

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

// 函数定义时指定默认参数
void greet(std::string name = "Guest") {
    std::cout << "Hello, " << name << "!" << std::endl;
}

int main() {
    // 调用函数时未提供参数值,将使用默认值
    greet(); // 输出 "Hello, Guest!"

    // 也可以提供参数值,覆盖默认值
    greet("Alice"); // 输出 "Hello, Alice!"

    return 0;
}

在上述示例中,greet 函数接受一个名为 name 的参数,但它在函数定义中指定了默认值 "Guest"。因此,在调用函数时,如果不提供 name 参数的值,函数将使用默认值。但如果提供了参数值,它将覆盖默认值。

请注意以下几点关于默认参数的注意事项:

  1. 默认参数必须从右向左添加。也就是说,如果您在函数参数列表中为某个参数指定默认值,那么该参数右侧的所有参数都必须有默认值。例如,void func(int a, int b = 0) 是有效的,但 void func(int a = 0, int b) 是无效的。

  2. 一旦为某个参数指定了默认值,后续的参数都必须有默认值,无法再定义没有默认值的参数。这是因为在函数调用时,如果您省略了参数,编译器无法确定省略的参数是哪个。

  3. 默认参数的值可以是常量、全局变量、静态变量或者常量表达式。

  4. 在函数声明和函数定义中都可以指定默认参数。通常,将默认参数放在函数声明中,而不是函数定义中,以便其他文件中的代码可以使用相同的默认参数。函数定义中通常省略默认参数的值,因为它已经在声明中定义过了。

默认参数是一种提高函数灵活性和可用性的重要工具,它允许您为不同的情况提供合适的默认行为,同时允许调用者在需要时覆盖默认值。

C++ std::set 详解

std::set 是 C++ 标准库中的一个关联容器,它实现了有序不重复元素的集合。在 std::set 中,元素按照升序排序,并且不允许重复元素的存在。std::set 是基于红黑树(Red-Black Tree)数据结构实现的,因此插入、删除和查找操作都具有对数时间复杂度(O(log n))。

以下是关于 std::set 的详解:

包含头文件

要使用 std::set,你需要包含 <set> 头文件:

1
#include <set>

创建和初始化 std::set

你可以使用多种方式来创建和初始化 std::set

1
std::set<int> mySet; // 创建一个空的 set,存储整数

你还可以在创建时指定初始元素:

1
std::set<int> mySet = {10, 20, 30}; // 创建并初始化 set

插入元素

可以使用 insert() 方法来向 std::set 中插入元素。插入操作会自动维护集合的有序性和不重复性:

1
mySet.insert(40); // 向 set 中插入元素 40

删除元素

可以使用 erase() 方法来删除 std::set 中的元素。你可以指定要删除的元素值或迭代器:

1
mySet.erase(20); // 删除值为 20 的元素

查找元素

你可以使用 find() 方法来查找 std::set 中的元素。如果元素存在,它会返回元素的迭代器;否则,返回 end()

1
2
3
4
5
6
std::set<int>::iterator it = mySet.find(30);
if (it != mySet.end()) {
    // 元素 30 存在
} else {
    // 元素 30 不存在
}

遍历元素

你可以使用迭代器来遍历 std::set 中的元素:

1
2
3
4
for (std::set<int>::iterator it = mySet.begin(); it != mySet.end(); ++it) {
    int value = *it;
    // 处理元素 value
}

其他常用操作

std::set 还支持其他一些常用操作,例如:

  • size():获取集合中元素的数量。
  • empty():检查集合是否为空。
  • clear():清空集合中的所有元素。
  • lower_bound()upper_bound():分别查找不小于(大于等于)和大于给定值的元素的迭代器。

注意事项

  • std::set 中的元素是有序的,因此插入和删除操作相对较慢,但查找操作非常高效。
  • std::set 不允许重复元素。如果你需要允许重复元素的集合,请考虑使用 std::multiset
  • 在 C++11 及更高版本中,你还可以使用范围迭代器和 C++11 范围循环来更方便地遍历 std::set 中的元素。

std::set 是一个强大的数据结构,特别适用于需要快速查找和有序存储不重复元素的情况。

C++ std::bind 详解

std::bind() 是 C++11 中引入的函数,它允许你创建一个可调用对象(函数对象)并将参数绑定到该对象上。这在很多情况下非常有用,特别是在处理回调函数、函数对象、线程和 STL 算法时。

std::bind() 函数位于 <functional> 头文件中,你需要包含该头文件才能使用它。以下是关于 std::bind() 的详细解释和示例用法:

std::bind() 函数签名

std::bind() 的一般函数签名如下:

1
2
template<class Fn, class... Args>
/*unspecified*/ bind(Fn&& fn, Args&&... args);
  • Fn:要绑定的函数或可调用对象。
  • Args:参数列表,这些参数将在调用时传递给 Fn

std::bind() 的返回值

std::bind() 返回一个可调用对象,该对象可以像函数一样被调用,调用时将使用提供的参数。返回类型不是一个具体的类型,因此通常需要使用 autostd::function 来接受返回的对象。

示例用法

以下是一些示例,演示了 std::bind() 的用法:

示例 1:绑定全局函数

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

void greet(const std::string& name) {
    std::cout << "Hello, " << name << "!" << std::endl;
}

int main() {
    auto greetFn = std::bind(greet, "Alice");
    greetFn(); // 调用 greet("Alice")
    return 0;
}

示例 2:绑定成员函数

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

class MyClass {
public:
    void printNumber(int num) {
        std::cout << "Number: " << num << std::endl;
    }
};

int main() {
    MyClass obj;
    auto printFn = std::bind(&MyClass::printNumber, &obj, std::placeholders::_1);
    printFn(42); // 调用 obj.printNumber(42)
    return 0;
}

示例 3:绑定函数对象

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

class Adder {
public:
    int operator()(int a, int b) {
        return a + b;
    }
};

int main() {
    Adder add;
    auto addFn = std::bind(add, std::placeholders::_1, std::placeholders::_2);
    int result = addFn(10, 20); // 调用 add(10, 20)
    std::cout << "Result: " << result << std::endl;
    return 0;
}

占位符 std::placeholders::_1, _2, …

std::bind() 中的 std::placeholders::_1, std::placeholders::_2, … 是用来占位的特殊标记。它们表示参数的位置,允许你在绑定时指定哪些参数将被传递,哪些将保留以后填充。

使用 std::function 来保存可调用对象

通常,你可以使用 std::function 来保存 std::bind() 返回的可调用对象,以便稍后调用它们。这允许你在运行时动态决定要调用哪个函数或函数对象。

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
#include <iostream>
#include <functional>

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

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

int main() {
    std::function<int(int, int)> operation;
    
    bool useAddition = true;
    if (useAddition) {
        operation = std::bind(add, std::placeholders::_1, std::placeholders::_2);
    } else {
        operation = std::bind(subtract, std::placeholders::_1, std::placeholders::_2);
    }

    int result = operation(10, 5); // 动态调用 add() 或 subtract()
    std::cout << "Result: " << result << std::endl;

    return 0;
}

这些示例展示了 std::bind() 的一些基本用法,你可以根据需要灵活地绑定函数和参数,使代码更模块化和可复用。

C++ std::string 详解

std::string 是 C++ 标准库中的字符串类,提供了在处理字符串时非常方便和强大的功能。以下是关于 std::string 的详解:

  1. 包含头文件

    要使用 std::string,需要包含头文件 <string>,通常可以这样做:

    1
    
    #include <string>
    
  2. 字符串的创建和初始化

    可以使用多种方式来创建和初始化 std::string

    1
    2
    3
    
    std::string str1;           // 创建一个空字符串
    std::string str2 = "Hello"; // 创建并初始化一个字符串
    std::string str3(5, 'a');   // 创建一个包含 5 个 'a' 的字符串
    
  3. 字符串的基本操作

    std::string 支持许多基本操作,包括连接、比较、子字符串等等。

    • 连接字符串:

      1
      2
      3
      
      std::string str1 = "Hello";
      std::string str2 = " World";
      std::string result = str1 + str2; // 连接两个字符串
      
    • 比较字符串:

      1
      2
      3
      
      std::string str1 = "apple";
      std::string str2 = "banana";
      int cmp = str1.compare(str2); // 比较两个字符串,返回负数、零或正数
      
    • 提取子字符串:

      1
      2
      
      std::string str = "Hello, World!";
      std::string sub = str.substr(0, 5); // 提取从索引 0 开始的 5 个字符
      
  4. 字符串的访问

    std::string 可以像数组一样访问其字符。

    1
    2
    
    std::string str = "Hello";
    char firstChar = str[0]; // 获取第一个字符 'H'
    
  5. 字符串的长度和大小

    可以使用 length()size() 方法获取字符串的长度。

    1
    2
    
    std::string str = "Hello";
    int len = str.length(); // 获取字符串长度
    
  6. 字符串的搜索和替换

    std::string 提供了查找和替换子字符串的方法。

    • 查找子字符串:

      1
      2
      
      std::string str = "Hello, World!";
      size_t found = str.find("World"); // 查找子字符串的位置
      
    • 替换子字符串:

      1
      2
      
      std::string str = "Hello, World!";
      str.replace(7, 5, "C++"); // 用 "C++" 替换从索引 7 开始的 5 个字符
      
  7. 字符串的迭代

    可以使用迭代器遍历字符串的字符。

    1
    2
    3
    4
    
    std::string str = "Hello";
    for (auto it = str.begin(); it != str.end(); ++it) {
        char c = *it;
    }
    
  8. 字符串的转换

    std::string 可以与其他数据类型进行转换,例如整数和浮点数。

    1
    2
    
    std::string str = "42";
    int num = std::stoi(str); // 将字符串转换为整数
    
  9. 字符串的输入和输出

    可以使用流操作符 <<>> 来进行字符串的输入和输出。

    1
    2
    3
    
    std::string str;
    std::cin >> str; // 从标准输入读取字符串
    std::cout << str; // 输出字符串到标准输出
    
  10. 动态调整字符串大小

    std::string 允许动态调整字符串的大小,以容纳更多或更少的字符。

    1
    2
    
    std::string str = "Hello";
    str.resize(8, ' '); // 调整字符串大小为 8,用空格填充
    

std::string 提供了许多其他功能和方法,以方便地处理字符串。它是 C++ 中处理文本数据的重要工具,可以大大简化字符串操作的复杂性。

C++ 查找表

C++ 中查找表通常使用数据结构来实现,其中最常用的数据结构之一是关联容器(Associative Containers)或哈希表(Hash Table)。这些数据结构可以用来存储键-值对,以便可以通过键来查找值。

以下是一些常见的 C++ 查找表数据结构和它们的用法:

  1. std::mapstd::map 是 C++ 标准库中的关联容器,它使用红黑树实现,可以用来存储键值对,并且按键进行排序。查找操作的时间复杂度是 O(log n)。

    1
    2
    3
    4
    5
    
    #include <map>
    std::map<std::string, int> myMap;
    myMap["Alice"] = 25;
    myMap["Bob"] = 30;
    int age = myMap["Alice"];  // 查找 Alice 的年龄
    
  2. std::unordered_mapstd::unordered_map 也是 C++ 标准库中的关联容器,它使用哈希表实现,查找操作的平均时间复杂度为 O(1)。

    1
    2
    3
    4
    5
    
    #include <unordered_map>
    std::unordered_map<std::string, int> myMap;
    myMap["Alice"] = 25;
    myMap["Bob"] = 30;
    int age = myMap["Alice"];  // 查找 Alice 的年龄
    
  3. std::setstd::set 是一个有序的集合,用于存储唯一的元素。它使用红黑树实现,查找操作的时间复杂度是 O(log n)。

    1
    2
    3
    4
    5
    
    #include <set>
    std::set<int> mySet;
    mySet.insert(10);
    mySet.insert(20);
    bool found = mySet.count(10);  // 检查元素是否存在
    
  4. std::unordered_setstd::unordered_set 是一个无序的集合,用于存储唯一的元素。它使用哈希表实现,查找操作的平均时间复杂度为 O(1)。

    1
    2
    3
    4
    5
    
    #include <unordered_set>
    std::unordered_set<int> mySet;
    mySet.insert(10);
    mySet.insert(20);
    bool found = mySet.count(10);  // 检查元素是否存在
    

这些是 C++ 中常用的查找表数据结构,你可以根据你的需求选择适合的数据结构来实现查找表功能。根据数据量、查找频率以及是否需要元素的排序等因素来选择合适的容器。

C++ 哈希表 详解

哈希表(Hash Table),也称为散列表,是一种数据结构,用于高效地存储和检索数据。它通过将数据与一个哈希函数计算的索引关联起来,从而允许在常量时间内进行数据插入、查找和删除操作。C++ 中可以使用标准库提供的 std::unordered_map 实现哈希表,以下是哈希表的详细解释:

  1. 哈希函数: 哈希表的核心是哈希函数。这个函数将输入的数据(通常是键)转换成一个整数,该整数通常被称为哈希码或哈希值。这个哈希值用于确定数据在哈希表中的存储位置。一个好的哈希函数应该将数据均匀地分布到哈希表的不同位置,以避免冲突(多个数据映射到同一个位置)。

  2. 哈希表的结构: 哈希表通常是一个数组,每个元素被称为一个“桶”或“槽”。每个桶可以存储一个数据项或多个数据项,具体取决于哈希函数和解决冲突的方法。

  3. 解决冲突: 由于哈希函数的限制,可能会发生冲突,即多个数据项被映射到同一个桶中。解决冲突的常见方法包括:
    • 链地址法(Chaining):每个桶存储一个链表或其他数据结构,用于存储冲突的数据项。
    • 开放寻址法(Open Addressing):当发生冲突时,继续尝试找到下一个可用的桶,直到找到一个空桶或遍历整个哈希表。
  4. C++ 中的 std::unordered_map: C++ 标准库提供了 std::unordered_map 类模板,它是一个使用哈希表实现的关联容器,用于存储键值对。以下是 std::unordered_map 的一些常见操作:

    • 插入数据:使用 insertemplace 方法插入键值对。
    • 查找数据:使用 find 方法查找指定键的值。
    • 删除数据:使用 erase 方法删除指定键的值。
    • 迭代哈希表:可以使用迭代器遍历哈希表中的键值对。
    • 获取大小:使用 size 方法获取哈希表中键值对的数量。

下面是一个简单示例,演示如何使用 std::unordered_map

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
#include <iostream>
#include <unordered_map>

int main() {
    // 创建一个哈希表
    std::unordered_map<std::string, int> hashMap;

    // 插入键值对
    hashMap["Alice"] = 25;
    hashMap["Bob"] = 30;

    // 查找键的值
    if (hashMap.find("Alice") != hashMap.end()) {
        std::cout << "Alice's age is " << hashMap["Alice"] << std::endl;
    } else {
        std::cout << "Alice not found in the map." << std::endl;
    }

    // 删除键值对
    hashMap.erase("Bob");

    // 遍历哈希表
    for (const auto& pair : hashMap) {
        std::cout << pair.first << "'s age is " << pair.second << std::endl;
    }

    return 0;
}

这是哈希表的基本概念和在C++中使用哈希表的简单示例。在实际应用中,选择合适的哈希函数和解决冲突的策略以及考虑性能是非常重要的。此外,C++标准库还提供了其他关联容器,如std::map,它使用红黑树实现,适用于一些特定的使用情况。

C++ 哈希表

C++中的哈希表是一种常用的数据结构,用于实现键值对的存储和检索。哈希表基于哈希函数将键映射到一个索引,然后在该索引处存储相应的值。这可以大大提高数据的访问速度,因为您可以通过键快速查找值,而不需要遍历整个数据集。

在C++中,可以使用标准库中的std::unordered_map来创建哈希表。以下是使用std::unordered_map的基本示例:

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
#include <iostream>
#include <unordered_map>
#include <string>

int main() {
    // 创建一个哈希表,将字符串键映射到整数值
    std::unordered_map<std::string, int> myMap;

    // 插入键值对
    myMap["Alice"] = 25;
    myMap["Bob"] = 30;
    myMap["Charlie"] = 35;

    // 查找值
    std::string name = "Bob";
    if (myMap.find(name) != myMap.end()) {
        std::cout << name << "的年龄是:" << myMap[name] << "岁" << std::endl;
    } else {
        std::cout << name << "不在哈希表中" << std::endl;
    }

    // 遍历哈希表
    for (const auto& pair : myMap) {
        std::cout << "键: " << pair.first << ", 值: " << pair.second << std::endl;
    }

    return 0;
}

在上面的示例中,我们首先包含了相关的头文件,然后创建了一个std::unordered_map对象,该对象将字符串映射到整数值。我们插入了几个键值对,然后使用find方法查找特定键的值,并遍历整个哈希表来访问所有键值对。

请注意,C++标准库中的std::unordered_map实现了哈希表,但在某些情况下,您可能需要自定义哈希函数或提供自定义比较函数来处理特殊数据类型。此外,C++11引入了std::unordered_map之外的一些其他哈希容器,例如std::unordered_set(用于存储唯一的值)和std::unordered_multimap(允许多个相同的键)。根据您的需求,您可以选择合适的容器类型。

C struct termios VMIN 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。VMINstruct termios 结构体中的一个成员,用于配置终端设备的非规范模式下的最小字符数。

VMIN 成员用于设置终端设备的非规范模式下的最小字符数要求。在终端设备打开的文件描述符上启用了非规范模式(通过 c_lflag 中的 ICANON 标志),并且设置了非零的 VMIN 值时,系统会等待输入的字符数达到指定的最小值后才返回读取操作。

当读取操作获取到指定的最小字符数后,不会等待超过 VMIN 值的字符数再返回。如果在达到最小字符数之前读取操作遇到文件末尾(EOF)或错误,读取操作也会返回。

以下是一个示例,展示如何使用 VMIN 成员来配置终端设备的非规范模式下的最小字符数:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 配置非规范模式
    serialConfig.c_lflag &= ~ICANON;

    // 设置非规范模式下的最小字符数为 5
    serialConfig.c_cc[VMIN] = 5;

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。我们将终端设备配置为非规范模式,并设置了非零的 VMIN 值来配置最小字符数要求为 5。

需要注意的是,VMIN 成员只在非规范模式下生效,即在设置了 ICANON 标志为 0 的情况下才会生效。在规范模式下,输入数据会等待达到终端设备的行缓冲大小或回车键才返回。

C struct termios VTIME 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。VTIMEstruct termios 结构体中的一个成员,用于配置终端设备的读取超时。

VTIME 成员用于设置终端设备的非规范模式下的读取超时。当在终端设备打开的文件描述符上启用了非规范模式(通过 c_lflag 中的 ICANON 标志),并且设置了非零的 VTIME 值时,系统会等待一段时间以等待输入数据。

VTIME 值的单位是十分之一秒(1/10 秒)。如果设置了非零的 VTIME 值,则在读取输入数据时,系统会等待指定的超时时间。如果在超时时间内没有数据可读,则读取操作会返回,如果有数据可读,读取操作会立即返回。

以下是一个示例,展示如何使用 VTIME 成员来配置终端设备的非规范模式下的读取超时:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 配置非规范模式
    serialConfig.c_lflag &= ~ICANON;

    // 设置读取超时为 2 秒(20 * 1/10 秒)
    serialConfig.c_cc[VTIME] = 20;

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。我们将终端设备配置为非规范模式,并设置了非零的 VTIME 值来配置读取超时为 2 秒。

需要注意的是,VTIME 成员只在非规范模式下生效,即在设置了 ICANON 标志为 0 的情况下才会生效。在规范模式下,输入数据会立即返回,不会等待超时。

C struct termios CSTOPB 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。CSTOPBstruct termios 结构体中的一个标志,用于配置终端设备的停止位数。

CSTOPB 标志用于设置终端设备的停止位数。当设置了这个标志时,终端设备会使用两个停止位作为数据帧的停止位。如果未设置这个标志,终端设备将使用一个停止位。

以下是一个示例,展示如何使用 CSTOPB 标志来配置终端设备的停止位数为两个:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 设置停止位数为两个
    serialConfig.c_cflag |= CSTOPB;

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。通过使用 CSTOPB 标志,我们设置了终端设备的停止位数为两个。

需要注意的是,设置停止位数的选择取决于实际的通信需求和硬件要求。在大多数情况下,使用一个停止位已经足够,但在某些情况下,可能需要使用两个停止位以确保数据传输的可靠性。

C struct termios ISTRIP 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。ISTRIPstruct termios 结构体中的一个标志,用于配置终端设备的输入字符处理。

ISTRIP 标志用于设置终端设备在接收数据时是否对输入的字符进行去掉第8位(最高位)的处理。当设置了这个标志时,终端设备会将接收到的字符的最高位设置为0。

以下是一个示例,展示如何使用 ISTRIP 标志来配置终端设备的输入字符处理:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 设置输入字符处理,去掉第8位
    serialConfig.c_iflag |= ISTRIP;

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。通过使用 ISTRIP 标志,我们设置了输入字符处理,使终端设备在接收数据时去掉输入字符的最高位。

需要注意的是,使用 ISTRIP 标志会改变接收到的字符的数据,因此在使用该标志时,需要根据实际需求来进行配置。在一些情况下,去掉输入字符的最高位可能是有意义的,但在其他情况下,可能需要保留字符的所有位。

C struct termios INPCK 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。INPCKstruct termios 结构体中的一个标志,用于配置终端设备的输入奇偶校验检测。

INPCK 标志用于启用输入奇偶校验检测。当设置了这个标志时,终端设备会在接收数据时进行奇偶校验检测,以验证接收的数据的完整性。

以下是一个示例,展示如何使用 INPCK 标志来配置终端设备的输入奇偶校验检测功能:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 启用输入奇偶校验检测
    serialConfig.c_iflag |= INPCK;

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。通过使用 INPCK 标志,我们启用了输入奇偶校验检测功能。这将使终端设备在接收数据时进行奇偶校验检测,以确保接收到的数据的正确性。

需要注意的是,输入奇偶校验检测功能的使用取决于实际的通信需求和硬件要求。在某些情况下,启用输入奇偶校验检测可以增加数据传输的可靠性,但在其他情况下,可能不需要进行奇偶校验检测。

C struct termios PARODD 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。PARODDstruct termios 结构体中的一个标志,用于配置终端设备的奇偶校验模式。

PARODD 标志用于设置奇偶校验的模式。当设置了这个标志时,终端设备会使用奇校验模式,即在每个数据字节中包括奇数个”1”位,从而保持数据字节的奇校验性。

以下是一个示例,展示如何使用 PARODD 标志来配置终端设备的奇偶校验模式为奇校验:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 设置奇偶校验模式为奇校验
    serialConfig.c_cflag |= PARENB; // 启用奇偶校验功能
    serialConfig.c_cflag |= PARODD; // 设置为奇校验模式

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。通过使用 PARENBPARODD 标志,我们启用了奇偶校验功能,并将奇偶校验模式设置为奇校验。

需要注意的是,奇偶校验模式的选择取决于实际的通信需求和硬件要求。在某些情况下,使用奇校验可以增加数据传输的可靠性,但在其他情况下,可能需要使用偶校验或者不使用奇偶校验。

C struct termios PARENB 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。PARENBstruct termios 结构体中的一个标志,用于配置终端设备的奇偶校验。

PARENB 标志用于启用奇偶校验功能。当设置了这个标志时,终端设备会在发送和接收数据时进行奇偶校验。奇偶校验是一种数据完整性的检查机制,用于检测数据传输过程中的错误。

以下是一个示例,展示如何使用 PARENB 标志来配置终端设备的奇偶校验功能:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 启用奇偶校验功能
    serialConfig.c_cflag |= PARENB;

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。通过使用 PARENB 标志,我们启用了奇偶校验功能。在这个示例中,数据将会在发送和接收时进行奇偶校验。

需要注意的是,奇偶校验功能的使用取决于实际的通信需求和硬件要求。在某些情况下,使用奇偶校验可以增加数据传输的可靠性,但在其他情况下,可能不需要使用奇偶校验。

C struct termios CSIZE 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。CSIZEstruct termios 结构体中的一个标志,用于配置终端设备的数据位数。

CSIZE 标志是一个位掩码,用于指定数据位的大小。在struct termios 结构体中,数据位的大小可以使用以下常量之一来设置:

  • CS5:5位数据位
  • CS6:6位数据位
  • CS7:7位数据位
  • CS8:8位数据位

以下是一个示例,展示如何使用 CSIZE 标志来配置终端设备的数据位大小:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 设置数据位大小为 8 位
    serialConfig.c_cflag &= ~CSIZE; // 清除现有的数据位设置
    serialConfig.c_cflag |= CS8;    // 设置数据位大小为 8 位

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。通过使用 CSIZE 标志,我们可以设置数据位的大小。在这个示例中,我们将数据位的大小设置为 8 位。

需要注意的是,数据位的大小需要根据实际通信需求和硬件要求进行设置。不同的串口设备可能有不同的数据位大小要求。

C struct termios CLOCAL 标志

在C语言中,struct termios 结构体用于表示终端设备的配置参数。CLOCALstruct termios 结构体中的一个标志,用于在串口通信中配置终端设备的行为。

CLOCAL 标志用于设置终端设备在串口通信中的本地连接模式。当设置了 CLOCAL 标志时,终端设备将不会考虑调制解调器(Modem)的状态,即终端设备不会关心调制解调器是否连接。

以下是一个示例,展示如何使用 CLOCAL 标志来配置终端设备:

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
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *devicePath = "/dev/ttyS0"; // 串口设备文件路径

    // 使用 O_RDWR 标志打开串口设备文件
    int fd = open(devicePath, O_RDWR);

    if (fd == -1) {
        perror("Error opening device file");
        return 1;
    }

    // 获取当前终端设备的配置参数
    struct termios serialConfig;
    tcgetattr(fd, &serialConfig);

    // 设置 CLOCAL 标志,忽略调制解调器状态
    serialConfig.c_cflag |= CLOCAL;

    // 将新的配置参数应用到终端设备
    tcsetattr(fd, TCSANOW, &serialConfig);

    // 进行串口通信...

    close(fd);

    return 0;
}

在上述示例中,我们使用 open() 函数打开了一个串口设备文件 “/dev/ttyS0”,然后获取了当前终端设备的配置参数。我们通过设置 CLOCAL 标志,将其添加到 c_cflag 字段中,以忽略调制解调器的状态。最后,我们使用 tcsetattr() 函数将新的配置参数应用到终端设备。

需要注意的是,CLOCAL 标志的使用取决于实际的通信环境和需求。在某些情况下,忽略调制解调器状态可能是合适的,但在其他情况下,可能需要使用调制解调器状态来确保通信的稳定性。

C open() O_LARGEFILE 标志

在C语言中,open() 函数用于打开文件,同时还可以通过添加一些标志来指定文件的打开方式和行为。O_LARGEFILE 是一个标志,用于在一些操作系统中支持大文件的功能。

在一些操作系统中,文件的大小可能受到限制,特别是在32位系统中。O_LARGEFILE 标志的作用是告诉操作系统在打开文件时使用64位的偏移量,以支持大文件。这意味着即使在32位系统中,你仍然可以处理超过2GB大小的文件。

以下是使用 O_LARGEFILE 标志的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fcntl.h>

int main() {
    const char *filename = "largefile.txt";

    // 使用 O_LARGEFILE 标志打开文件
    int fd = open(filename, O_RDWR | O_CREAT | O_LARGEFILE, 0666);

    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }

    // 进行文件操作...

    close(fd);

    return 0;
}

在上面的示例中,我们使用了 O_LARGEFILE 标志来打开文件。请注意,使用这个标志时,可能需要操作系统和文件系统的支持,否则这个标志可能不会产生预期的效果。

需要注意的是,现代操作系统和文件系统通常会自动处理大文件,因此在大多数情况下,你不需要显式地使用 O_LARGEFILE 标志来打开文件。只有在处理旧的或不常见的系统时,可能需要考虑这个标志。

C open() O_CLOEXEC 标志

在C语言中,open() 函数用于打开文件,同时还可以通过添加一些标志来指定文件的打开方式和行为。O_CLOEXEC 是一个标志,用于在打开文件时设置文件描述符的 close-on-exec 标志。

close-on-exec 是一个进程属性,当进程调用 exec() 系列函数执行一个新程序时,所有的文件描述符(除了标准输入、标准输出和标准错误)会被关闭。O_CLOEXEC 标志的作用是在打开文件时,将这个文件描述符设置为 close-on-exec,这样在执行 exec() 函数时会自动关闭它。

以下是使用 O_CLOEXEC 标志的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fcntl.h>

int main() {
    const char *filename = "example.txt";

    // 使用 O_CLOEXEC 标志打开文件
    int fd = open(filename, O_RDWR | O_CREAT | O_CLOEXEC, 0666);

    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }

    // 进行文件操作...

    close(fd);

    return 0;
}

在上面的示例中,我们使用了 O_CLOEXEC 标志来打开文件。这意味着在执行 exec() 函数时,文件描述符 fd 会自动关闭。

需要注意的是,O_CLOEXEC 标志通常用于在多进程环境中,通过设置文件描述符的 close-on-exec 标志,可以避免在执行 exec() 函数时造成不必要的文件描述符泄漏。这对于创建安全的、不会在子进程中意外共享文件描述符的程序非常有用。

C open() O_CREAT 标志

在C语言中,open() 函数用于打开文件,同时还可以通过添加一些标志来指定文件的打开方式和行为。O_CREAT 是一个标志,用于在打开文件时,如果文件不存在就创建文件。

当你使用 O_CREAT 标志打开一个文件时,如果文件已经存在,则打开现有文件;如果文件不存在,则创建一个新的文件。你通常还需要提供一个权限参数来设置新文件的权限。这个权限参数通常是八进制表示的权限位。

以下是使用 O_CREAT 标志的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fcntl.h>

int main() {
    const char *filename = "example.txt";

    // 使用 O_CREAT 标志创建文件
    int fd = open(filename, O_RDWR | O_CREAT, 0666);

    if (fd == -1) {
        perror("Error opening/creating file");
        return 1;
    }

    // 进行文件操作...

    close(fd);

    return 0;
}

在上面的示例中,我们使用了 O_CREAT 标志来创建文件。如果文件 “example.txt” 不存在,那么就会创建一个新的文件,并赋予其权限 0666。

需要注意的是,使用 O_CREAT 标志时,通常还需要提供一个权限参数,以确保新创建的文件拥有适当的权限。如果文件已经存在,那么 O_CREAT 标志不会影响文件的现有权限。

C open() O_NONBLOCK 标志

在C语言中,open() 函数用于打开文件,同时还可以通过添加一些标志来指定文件的打开方式和行为。O_NONBLOCK 是一个标志,用于在打开文件时设置非阻塞模式。

使用 O_NONBLOCK 标志打开文件时,文件描述符会以非阻塞模式打开。在非阻塞模式下,当尝试读取或写入文件时,如果没有数据可读或没有足够的空间可写入,操作会立即返回,而不会阻塞等待。这对于需要处理非阻塞IO的情况非常有用,如网络编程和异步IO。

以下是使用 O_NONBLOCK 标志的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fcntl.h>

int main() {
    const char *filename = "example.txt";

    // 使用 O_NONBLOCK 标志以非阻塞模式打开文件
    int fd = open(filename, O_RDWR | O_NONBLOCK);

    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }

    // 进行非阻塞文件操作...

    close(fd);

    return 0;
}

在上面的示例中,我们使用了 O_NONBLOCK 标志来以非阻塞模式打开文件。这意味着在使用该文件描述符进行读写操作时,操作将立即返回,而不会阻塞等待。如果没有数据可读或没有足够的空间可写入,操作将返回一个适当的错误。

需要注意的是,在非阻塞模式下,读取和写入操作可能不会始终读取或写入预期的数据量。你需要在代码中处理返回值,以适应非阻塞操作可能遇到的情况。

C open() O_RDWR 标志

在C语言中,open() 函数用于打开文件,同时还可以通过添加一些标志来指定文件的打开方式和行为。O_RDWR 是一个标志,用于以读写方式打开文件,即可以读取和写入文件。

以下是使用 O_RDWR 标志的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fcntl.h>

int main() {
    const char *filename = "example.txt";

    // 使用 O_RDWR 标志以读写方式打开文件
    int fd = open(filename, O_RDWR);

    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }

    // 进行文件读写操作...

    close(fd);

    return 0;
}

在上述示例中,我们使用了 O_RDWR 标志来以读写方式打开文件。这意味着我们可以使用文件描述符进行读取和写入操作。如果文件不存在,将会返回错误。如果需要,可以使用 O_CREAT 标志来创建文件。

需要注意的是,使用 O_RDWR 标志打开文件时,你可以同时进行读取和写入操作,但你需要根据实际需要进行适当的读写操作管理和错误处理。同时,打开文件时也需要考虑文件权限和文件是否存在等因素。

C open() S_IWUSR 标志

在C语言中,open() 函数用于打开文件,同时还可以通过添加一些标志来指定文件的打开方式和行为。S_IWUSR 是一个权限标志,用于指定用户可写权限。

在Linux系统中,文件权限通常由三个部分组成:所有者权限、所属组权限和其他用户权限。S_IWUSR 表示用户(所有者)可写权限。当你在使用 open() 函数创建文件时,可以使用这个权限标志来设置文件的所有者的写权限。

以下是使用 S_IWUSR 权限标志的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fcntl.h>

int main() {
    const char *filename = "example.txt";

    // 使用 S_IWUSR 标志创建具有用户可写权限的文件
    int fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

    if (fd == -1) {
        perror("Error opening/creating file");
        return 1;
    }

    // 进行文件操作...

    close(fd);

    return 0;
}

在上述示例中,我们使用了 S_IWUSR 权限标志来创建一个文件,该文件具有用户可写权限。我们同时使用了 S_IRUSR 权限标志,以确保文件也具有用户可读权限。

需要注意的是,权限标志通常会与其他标志(如 O_RDWRO_CREAT 等)一起使用。在使用权限标志时,你还需要考虑文件的其他属性,如文件所有者、所属组等。

C open() S_IRUSR 标志

在C语言中,open() 函数用于打开文件,同时还可以通过添加一些标志来指定文件的打开方式和行为。S_IRUSR 是一个权限标志,用于指定用户可读权限。

在Linux系统中,文件权限通常由三个部分组成:所有者权限、所属组权限和其他用户权限。S_IRUSR 表示用户(所有者)可读权限。当你在使用 open() 函数创建文件时,可以使用这个权限标志来设置文件的所有者的读权限。

以下是使用 S_IRUSR 权限标志的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fcntl.h>

int main() {
    const char *filename = "example.txt";

    // 使用 S_IRUSR 标志创建具有用户可读权限的文件
    int fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

    if (fd == -1) {
        perror("Error opening/creating file");
        return 1;
    }

    // 进行文件操作...

    close(fd);

    return 0;
}

在上述示例中,我们使用了 S_IRUSR 权限标志来创建一个文件,该文件具有用户可读权限。我们同时使用了 S_IWUSR 权限标志,以确保文件也具有用户可写权限。

需要注意的是,权限标志通常会与其他标志(如 O_RDWRO_CREAT 等)一起使用。在使用权限标志时,你还需要考虑文件的其他属性,如文件所有者、所属组等。

C open() O_RDONLY 标志

在C语言中,open() 函数用于打开文件,同时还可以通过添加一些标志来指定文件的打开方式和行为。O_RDONLY 是一个标志,用于以只读方式打开文件,即可以读取文件但无法写入文件。

以下是使用 O_RDONLY 标志的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fcntl.h>

int main() {
    const char *filename = "example.txt";

    // 使用 O_RDONLY 标志以只读方式打开文件
    int fd = open(filename, O_RDONLY);

    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }

    // 进行文件读操作...

    close(fd);

    return 0;
}

在上述示例中,我们使用了 O_RDONLY 标志来以只读方式打开文件。这意味着我们可以使用文件描述符进行读取操作,但是无法使用该文件描述符进行写入操作。

需要注意的是,使用 O_RDONLY 标志打开文件时,文件只能以只读方式访问。如果你需要进行写入操作,你需要使用 O_RDWR 标志。同时,打开文件时还需要考虑文件的权限和文件是否存在等因素。

C 数字转布尔值

在C语言中,整数可以被视为布尔值的一种表示方式,其中非零值被视为“真”(true),而零值被视为“假”(false)。这是因为C语言中的条件表达式要求返回一个真值(非零)或假值(零)。

如果你想将整数显式转换为布尔值,你可以使用条件表达式或逻辑运算符来实现。以下是一些示例:

  1. 使用条件表达式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main() {
    int num = 42;
    int booleanValue;

    // 将整数转换为布尔值
    booleanValue = (num != 0);

    if (booleanValue) {
        printf("The value is true.\n");
    } else {
        printf("The value is false.\n");
    }

    return 0;
}
  1. 使用逻辑运算符:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main() {
    int num = -7;
    int booleanValue;

    // 将整数转换为布尔值
    booleanValue = (num != 0);

    if (booleanValue) {
        printf("The value is true.\n");
    } else {
        printf("The value is false.\n");
    }

    return 0;
}

在这两个示例中,我们使用条件表达式 (num != 0) 将整数转换为布尔值。如果整数非零,那么条件表达式的结果为真(非零),布尔值为 true;如果整数为零,条件表达式的结果为假(零),布尔值为 false。

需要注意的是,C语言中没有明确的布尔类型,而是使用整数作为布尔值的表示,非零值为 true,零值为 false。所以在C语言中,你可以将非零的整数作为 true 值来使用。

C++ 预处理指令详解

C++预处理器是一个在实际编译之前执行的阶段,它处理以 # 开头的预处理指令。这些指令用于在源代码中进行文本替换、条件编译、头文件包含等操作,以及定义宏和条件编译代码块。以下是一些常见的C++预处理指令以及它们的作用:

  1. #include: 用于包含其他文件中的内容。主要用于包含头文件,以便在源文件中使用头文件中的声明和定义。

    示例:

    1
    2
    
    #include <iostream>   // 包含C++标准库的iostream头文件
    #include "myheader.h" // 包含自定义头文件
    
  2. #define: 用于定义宏。预处理器会将宏名称替换为相应的文本。可以用于创建常量、函数宏等。

    示例:

    1
    2
    
    #define PI 3.14159
    #define MAX(x, y) ((x) > (y) ? (x) : (y))
    
  3. #ifdef / #ifndef / #else / #endif: 用于条件编译。根据指定的条件,决定是否编译某段代码。#ifdef 表示 “如果定义了宏”,#ifndef 表示 “如果未定义宏”,#else 表示 “否则”,#endif 表示结束条件块。

    示例:

    1
    2
    3
    4
    5
    
    #ifdef DEBUG
        // 调试时执行的代码
    #else
        // 发布版本时执行的代码
    #endif
    
  4. #if / #elif / #else / #endif: 类似于条件编译,但允许在代码中进行更复杂的条件判断,如使用预定义宏来确定编译路径。

    示例:

    1
    2
    3
    4
    5
    6
    7
    
    #if defined(_WIN32)
        // Windows平台下的代码
    #elif defined(__linux__)
        // Linux平台下的代码
    #else
        // 其他平台下的代码
    #endif
    
  5. #undef: 用于取消宏定义。如果需要取消一个已定义的宏,可以使用 #undef

    示例:

    1
    2
    
    #define MY_MACRO 42
    #undef MY_MACRO
    
  6. #pragma: 用于在编译器中插入特定的指令。#pragma 指令的效果和语法会因编译器而异。

    示例:

    1
    
    #pragma warning(disable : 1234)
    
  7. #error: 用于生成编译错误消息。可以用于在特定条件下阻止编译,或者为开发者提供有用的错误信息。

    示例:

    1
    2
    3
    
    #ifdef OLD_COMPILER
        #error "This version is not supported by the old compiler"
    #endif
    
  8. #line: 用于改变编译器报告的行号和文件名,通常用于错误消息或调试信息的生成。

    示例:

    1
    
    #line 100 "mycode.cpp"
    

C++预处理器的这些指令允许您在编译前进行一些文本操作和条件判断,以便根据不同的情况定制化代码的编译过程。它们在代码的可维护性和可配置性方面非常有用。

友元 friend

在 C++ 中,友元(friend)是一种访问控制机制,它允许一个函数或类访问另一个类的私有成员。友元关系通过在类的定义中使用 friend 关键字来建立。

友元的主要用途是在一些特殊情况下提供灵活的访问权限,但也应该谨慎使用,因为它可能会破坏类的封装性和隔离性。

以下是关于 C++ 中友元的详细解释:

  1. 友元函数:在类定义中使用 friend 关键字声明一个函数为友元函数。这意味着友元函数可以访问该类的私有和受保护成员。友元函数不是类的成员函数,但它具有特殊的权限,可以直接访问私有成员。

  2. 友元类:同样,可以使用 friend 关键字将一个类声明为另一个类的友元类。这意味着友元类的所有成员函数可以访问该类的私有和受保护成员。友元类可以访问另一个类的私有成员,但不会继承它的成员。

友元关系是双向的:如果类 A 是类 B 的友元,那么类 B 也可以访问类 A 的私有成员。

下面是一个示例,演示了如何在 C++ 中使用友元函数和友元类:

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
#include <iostream>

class FriendExample {
private:
    int privateValue;

public:
    FriendExample(int value) : privateValue(value) {}

    friend void FriendFunction(FriendExample& obj);  // 友元函数声明
    friend class FriendClass;  // 友元类声明
};

// 友元函数定义
void FriendFunction(FriendExample& obj) {
    std::cout << "Friend Function accessing privateValue: " << obj.privateValue << std::endl;
}

// 友元类定义
class FriendClass {
public:
    void AccessPrivateValue(FriendExample& obj) {
        std::cout << "Friend Class accessing privateValue: " << obj.privateValue << std::endl;
    }
};

int main() {
    FriendExample obj(42);
    FriendFunction(obj);

    FriendClass friendObj;
    friendObj.AccessPrivateValue(obj);

    return 0;
}

在这个示例中,FriendExample 类的 privateValue 是私有成员。FriendFunction 被声明为 FriendExample 类的友元函数,而 FriendClass 被声明为 FriendExample 类的友元类。因此,这两者都可以访问 FriendExample 类的私有成员。

友元机制在某些情况下很有用,例如在需要特定函数或类能够访问另一个类的私有成员时。然而,过度使用友元可能会破坏封装性,应该谨慎使用。


比特位 读写

在C++中,可以使用位操作运算符来进行比特位的读写操作。位操作运算符允许你直接对变量的二进制位进行操作,从而有效地读取和写入单个比特位。

以下是一些常用的位操作运算符和示例:

  1. 与运算符(&): 用于检查特定比特位的状态。将要检查的比特位置1,其他位置0,然后通过与运算得到结果。

    示例:检查第3个比特位是否为1

    1
    
    bool isBitSet = (number & (1 << 2)) != 0;
    
  2. 或运算符(|): 用于设置特定比特位的状态。将要设置的比特位置1,其他位置保持不变,然后通过或运算得到结果。

    示例:设置第5个比特位为1

    1
    
    number |= (1 << 4);
    
  3. 异或运算符(^): 用于切换特定比特位的状态。将要切换的比特位置1,其他位置保持不变,然后通过异或运算得到结果。

    示例:切换第2个比特位的状态

    1
    
    number ^= (1 << 1);
    
  4. 取反运算符(~): 用于取反变量的所有比特位。

    示例:将所有比特位取反

    1
    
    number = ~number;
    
  5. 左移运算符(«): 将一个数的所有比特位向左移动指定的位数。

    示例:将number的比特位向左移动3位

    1
    
    number = number << 3;
    
  6. 右移运算符(»): 将一个数的所有比特位向右移动指定的位数。对于无符号类型,左侧补0;对于有符号类型,左侧补符号位。

    示例:将number的比特位向右移动2位

    1
    
    number = number >> 2;
    

注意事项:

  • 在进行位操作时,务必确保要操作的比特位在合法的范围内,避免访问未定义的行为。
  • 使用无符号整数类型(例如unsigned int)可以避免在右移操作时出现符号位扩展的问题。

请根据你的具体需求选择适合的位操作运算符来进行比特位的读写操作。

C++ 常用容器 详解

C++ 标准模板库(Standard Template Library,STL)提供了许多常用的容器,这些容器是用于存储和管理数据的数据结构。STL 容器提供了各种不同类型的数据结构,以满足不同的需求。以下是 C++ 中常用的 STL 容器的详细解释:

  1. std::vectorstd::vector 是一个动态数组,可以在运行时调整大小。它支持高效的随机访问和在末尾插入/删除元素,但插入/删除操作在中间位置较慢。在使用时,通常使用 push_back 在末尾插入元素。

  2. std::liststd::list 是一个双向链表,支持在任意位置快速插入/删除元素,但随机访问元素较慢。在使用时,通常使用 push_backpush_front 在末尾和头部插入元素。

  3. std::dequestd::deque 是一个双端队列,支持在头部和尾部快速插入/删除元素,并支持高效的随机访问。在使用时,通常使用 push_backpush_front 在末尾和头部插入元素。

  4. std::setstd::set 是一个集合,它按照元素的键值自动排序,并且不允许重复的元素。它支持高效的插入/删除操作,并可以在 O(log n) 时间内查找元素。

  5. std::mapstd::map 是一个关联容器,它存储键值对,并按照键值自动排序。每个键只能对应一个值,因此不允许重复的键。

  6. std::unordered_setstd::unordered_set 是一个无序集合,它存储不重复的元素,并支持高效的插入/删除操作和查找元素。由于它是无序的,不会进行排序。

  7. std::unordered_mapstd::unordered_map 是一个无序关联容器,它存储键值对,并支持高效的插入/删除操作和查找元素。由于它是无序的,不会进行排序。

  8. std::stackstd::stack 是一个后进先出(LIFO)的堆栈容器,它只允许在堆栈顶部插入和删除元素。

  9. std::queuestd::queue 是一个先进先出(FIFO)的队列容器,它只允许在队列的尾部插入元素,在队列的头部删除元素。

  10. std::priority_queuestd::priority_queue 是一个优先队列容器,它按照元素的优先级进行排序,并支持高效的插入/删除操作。

这些是 C++ 中常用的 STL 容器。选择适当的容器取决于您的具体需求,比如数据访问方式、插入/删除操作的频率、是否需要排序等。STL 提供了这些容器以及丰富的算法和迭代器,使得在 C++ 中进行数据管理和处理变得非常方便和高效。