跳到主内容
C++
文章阅读

c++基础之cassert,cfloat,climits,codecvt,cwchar,local,memory,new,random

2025/12/1676 次阅读22 分钟

cassert - 断言


#include <cassert>
#include <iostream>
/**
 * 断言
 * false: 程序中止,程序中止执行
 */
int main() {
    int a = 7;
    int b = 2;
    assert(a > b);// true:继续正常执行
    std::cout << "a需要大于b" << std::endl;

    //高级用法
    int x = 1;
    int y = 2;
    assert(y == 0 && "y需要=0");
    std::cout << "结束." << std::endl;

    return 0;
}

输出


$ ./cassert
a需要大于b
Assertion failed: (y == 0 && "y需要=0"), function main, file cassert.cpp, line 16.
[1]    74755 abort      ./cassert

cfloat - 浮点数


#include <iostream>
#include <string>
#include <cfloat>
#include <cmath>
using namespace std;
/**
 * 浮点数
 * float: 单精度浮点数,占用4字节
 * double: 双精度浮点数,占用8个字节
 */
int main() {
    float f = 3.14f;
    double d = 2.334;


    ///输出float范围和精度
    cout << "float:\
";
    cout << "Min:" << FLT_MIN << "\
";
    cout << "Max:" << FLT_MAX << "\
";
    cout << "Epsilon:" << FLT_EPSILON << "\
";
    cout << "Digits:" << FLT_DIG << "\
";

    ///double
    cout << "\
double:\
";
    cout << "min:" << DBL_MIN << "\
";
    cout << "max:" << DBL_MAX << "\
";
    cout << "epsilon:" << DBL_EPSILON << "\
";
    cout << "digits:" << DBL_DIG << "\
";

    /// long double
    cout << "\
long double:\
";
    cout << "min:" << LDBL_MIN << "\
";
    cout << "max:" << LDBL_MAX << "\
";
    cout << "epsilon:" << LDBL_EPSILON << "\
";
    cout << "digits:" << LDBL_DIG << "\
";

    /**
     * float:
Min:1.17549e-38
Max:3.40282e+38
Epsilon:1.19209e-07
Digits:6

double:
min:2.22507e-308
max:1.79769e+308
epsilon:2.22045e-16
digits:15

long double:
min:2.22507e-308
max:1.79769e+308
epsilon:2.22045e-16
digits:15
     */


     ///使用cmath函数来操作
    double num = 9.0;
    double root = sqrt(num);//求平方根
    double power = pow(2.0, 3.0);//计算2的3次幂
    cout << "root is :" << root << endl; // =3
    cout << "power is : " << power << endl;//=8
    return 0;
}

输出


$ ./cfloat 
float:
Min:1.17549e-38
Max:3.40282e+38
Epsilon:1.19209e-07
Digits:6

double:
min:2.22507e-308
max:1.79769e+308
epsilon:2.22045e-16
digits:15

long double:
min:2.22507e-308
max:1.79769e+308
epsilon:2.22045e-16
digits:15
root is :3
power is 8

climits - 限制


#include <iostream>
#include <climits>
using namespace std;
/**
 * 这个是和整数相关的限制和特性
 *
 */
int main() {
    cout << "int最大值:" << INT_MAX << endl;
    cout << "int最小值:" << INT_MIN << endl;

    cout << "long最大值:" << LONG_MAX << endl;
    cout << "long最小值:" << LONG_MIN << endl;

    cout << "unsigned long最大值:" << ULONG_MAX << endl;

    cout << "char 最小值:" << CHAR_MIN << endl;
    cout << "char 最大值:" << CHAR_MAX << endl;
    cout << "char bit 位" << CHAR_BIT << endl;

    /**
     * int最大值:2147483647
    int最小值:-2147483648
    long最大值:9223372036854775807
    long最小值:-9223372036854775808
    unsigned long最大值:18446744073709551615
    char 最小值:-128
    char 最大值:127
    char bit 位8
     */
    return 0;
}

输出


$ ./climits
int最大值:2147483647
int最小值:-2147483648
long最大值:9223372036854775807
long最小值:-9223372036854775808
unsigned long最大值:18446744073709551615
char 最小值:-128
char 最大值:127
char bit 位8

codecvt - 字符转换工具



#include <iostream>
#include <codecvt>
#include <locale>
#include <string>
using namespace std;
/**
 * 字符转换工具
 */
int main() {


    //基于名称的转换器
    wstring_convert<codecvt_byname<wchar_t>> converter("zh_CN.UTF-8");

    // wstring_convert<codecvt_utf8_utf16<wchar_t>> converter;

    //原始字符串
    string narrow_string = "梁典典";

    //转换utf-16宽字符串
    wstring wide_string = converter.from_bytes(narrow_string);
    wcout << L"宽字符串:" << wide_string << endl;
    //转回uft8字符串
    string onverted_string = converter.to_bytes(wide_string);
    cout << "转回来了:" << onverted_string << endl;

    return 0;
}

输出


转回来了:梁典典

cwchar - 宽字符和宽字符串


#include <iostream>
#include <cwchar>
#include <locale>
using namespace std;

/**
 * 提供处理宽字符wchar_t和宽字符串的函数
 * 输入输出
 * 内存操作
 * 字符串操作
 *
 * wchar_t: 宽字符类型,存储宽字符
 * wint_t: 存储宽字符函数的返回值
 *
 * fgetwc: 文件流读取宽字符
 * fputwc: 向文件流写入宽字符
 * fgetws: 文件流读取宽字符串
 * fputws: 向文件流写入宽字符串
 *
 * 主要用途:

    •	处理多字节编码字符集(例如 Unicode)。
    •	开发国际化应用程序时,通过 wchar_t 处理各国语言的字符。

    对于需要处理不同语言的应用,宽字符提供了更好的支持,能够处理比 ASCII 更多的字符编码。
 */
int main() {

    setlocale(LC_ALL, "zh_CN.UTF-8");//设置本地化
    const wchar_t* filename = L"示例.txt";
    FILE* file = fopen("示例.txt", "w");
    cout << "file:" << file << endl;
    if (file) {
        fputws(L"你好,梁典典!\
", file);
        fclose(file);
    }
    wprintf(L"测试");
    file = fopen("示例.txt", "r");
    cout << "读文件:" << file << endl;
    if (file) {
        wchar_t buffer[256];
        if (fgetws(buffer, 256, file)) {
            wcout << "从文件中读取" << buffer << "." << endl;
        }
        else {
            wcout << L"读取失败了." << endl;
        }
        fclose(file);
    }
    else {
        cout << "读文件失败." << endl;
    }

    ///这里读取不到文件,不知道啥情况

    /**
     * 宽字符和宽字符串操作
     * wcscpy: 拷贝宽字符串
     * wcslen,获取宽字符串的长度
     * wcscmp: 比较宽字符串
     * wcsncpy: 拷贝指定长度的宽字符串
     */

    wchar_t str1[100] = L"梁典典";
    wchar_t str2[100] = L"你好";

    ///宽字符串拷贝
    wcscpy(str1, L"你好啊梁典典");
    wcout << L"拷贝狂字符串:" << str1 << endl;

    ///宽字符串长度
    size_t len = wcslen(str1);
    wcout << L"长度:" << len << endl;

    ///比较
    int result = wcscmp(str1, str2);
    wcout << L"比较结果:" << result << endl;

    ///部分拷贝,
    wcsncpy(str2, str1, 5);
    str2[5] = L'\0';
    wcout << "部分字符串拷贝:" << str2 << endl;


    /**
     * 宽字符分类和转换
     */

     //拍判断是不是字母
    wchar_t ch = L'A';
    if (iswalpha(ch)) {
        wcout << ch << L"是字母" << endl;
    }
    //判断是不是数字
    ch = L'9';
    if (iswdigit(ch)) {
        wcout << ch << L"是数组" << endl;
    }

    //转小写
    ch = L'G';
    wchar_t lower_ch = towlower(ch);
    wcout << L"转写:" << lower_ch << endl;

    ch = L'g';
    wchar_t upper_ch = towupper(ch);
    wcout << L"转大写:" << upper_ch << endl;

    /**
     * 宽字符和宽字符串的输入输出
     */
    wchar_t buffer[100];
    wprintf(L"格式化输出:%d %s\
", 42, L"梁典典");

    //格式化输入
    wprintf(L"输入数字和字符串");
    wscanf(L"%d %ls", &buffer);
    wprintf(L"输入的是:%ls\
", buffer);

    //格式化宽字符写入宽字符串
    swprintf(buffer, 100, L"格式话:%d %s", 42, L"梁典典");
    wcout << L"buffer:" << buffer << endl;

    //从宽字符串中读取格式化宽字符
    int number;
    wchar_t word[100];
    swscanf(buffer, L"格式化:%d %s", &number, word);
    wcout << "转换后:" << number << L"字符:" << word << endl;


    return 0;
}

输出


file:0x1ed2b3ad0
测试读文件:0x1ed2b3ad0
格式化输出:42输入数字和字符串1

local - 国际化


#include <iostream>
#include <locale>
#include <string>
#include <ctime>
using namespace std;
/**
 * 国际化
 */
int main() {

    //创建默认的
    locale loc;

    // 使用locle对象
    cout.imbue(loc);//设置cout的locale

    //显示当前的locale名称
    cout << "current locale" << loc.name() << endl;
    //输出current localeC

    //    使用locale格式化数字

    locale loc2("en_US.UTF-8");//设置美国英语
    cout.imbue(loc2);//设置cout的locale
    double number = 1234.7890;
    cout << "formatted number is " << number << endl;
    //输出:formatted number is 1,234.79



    /**
     * 比较字符串
     */

    locale loc3("en_US.UTF-8");
    string str1 = "apple";
    string str2 = "banana";

    bool c1 = use_facet<collate<char>>(loc3).compare(str1.c_str(), str1.c_str() + str1.size(),
        str2.c_str(), str2.c_str() + str2.size());
    if (c1 < 0) {
        cout << str1 << " comes before " << str2 << endl;
    }
    else {
        cout << str1 << " comes after " << str2 << endl;
    }
    ///输出:apple comes after banana

    /**
     * 日期和时间格式化
     */


    locale loc4("en_US.UTF-8");
    cout.imbue(loc4);
    time_t now = time(nullptr);
    tm* timeinfo = localtime(&now);
    char buffer[100];
    strftime(buffer, sizeof(buffer), "%A, %B, %d %Y", timeinfo);
    cout << "current date: " << buffer << endl;
    ///current date: Friday, September, 13 2024
    return 0;
}

输出


current localeC
formatted number is 1,234.79
apple comes after banana
current date: Wednesday, September, 18 2024

memory - 内存管理



#include <iostream>
#include <memory>
using namespace std;
/**
 * 内存管理库
 * c++11新特性
 * 智能指针的主要类型:
 * unique_ptr: 独占所有权的智能指针,同一个时间只有有一个unique_ptr指向特定内存
 * shared_ptr: 共享所有权的智能指针,多个shared_ptr可以指向同一个内存,内存在最后一个shared_ptr被销毁时释放
 * weak_ptr: 弱引用智能指针,用于shared_ptr配合使用,避免循环引用导致的内存泄露
 */

class MyClass {
public:
    void doSomething() {
        cout << "dosomethiing" << endl;
    }
};


class Node {
public:
    shared_ptr<Node> next;
    weak_ptr<Node> prev;

    Node() :next(nullptr), prev() {}
};

int main() {

    unique_ptr<MyClass> myPtr(new MyClass());
    myPtr->doSomething();//使用智能指针调用成员函数
    //当main函数结束的时候,myPtr被销毁,自动释放MyClass的内存



    /**
     * 使用shared_ptr
     */


    shared_ptr<MyClass> obj1(new MyClass());
    shared_ptr<MyClass> obj2 = obj1;
    obj1->doSomething();
    obj2->doSomething();
    //当obj1,obj2都被销毁时,myclass对象的内存被释放

    /**
     * weak_ptr
     * 通常不单独使用,而是和shared_ptr结合使用,解决循环引用的问题
     */

     /**
      * 创建了两个节点 node1 和 node2,它们都被管理在 std::shared_ptr 中。
     node1->next = node2;: node1 的 next 指针指向 node2,这意味着 node1 持有 node2 的所有权。
     node2->prev = node1;: node2 的 prev 是一个 weak_ptr,指向 node1,但并不持有 node1 的所有权。
      */



    shared_ptr<Node> n1 = make_shared<Node>();
    shared_ptr<Node> n2 = make_shared<Node>();
    n1->next = n2;
    n2->prev = n1;

    //循环引用,使用weak_ptr避免了内存泄露



    /**
     * 分配器: 提供了基本的内存分配和释放功能
     */

    allocator<int> alloc;
    int* p = alloc.allocate(1);//分配内存
    alloc.construct(p, 2);//构造对象

    std::cout << *p << std::endl;

    alloc.destroy(p); // 销毁对象
    alloc.deallocate(p, 1); // 释放内存

    cout << "end" << endl;


    /**
     * align: 调整指针的对齐方式
     */

    alignas(16) char buffer2[64];
    void* p2 = buffer2;
    size_t space = sizeof(buffer2);
    void* aligned_ptr = align(16, sizeof(int), p2, space);
    if (aligned_ptr) {
        cout << "内存对齐" << endl;
    }
    else {
        cout << "对齐失败" << endl;
    }

    return 0;
}

输出


$ ./memory
dosomethiing
dosomethiing
dosomethiing
2
end

new - 内存分配




#include <iostream>
#include <new> //头文件
using namespace std;


class MyClass {
public:
    int value;
    MyClass() : value(0) {}
};

int main() {


    /**
     * 动态分配单个对象
     */

    MyClass* obj = new MyClass; //分配一个 myclass对象
    obj->value = 10; //使用箭头操作符访问成员

    cout << "value is " << obj->value << endl;
    delete obj; //释放内存



    /**
     * 动态分配数组
     */


    int* arr = new int[10]; //分配一个包含10个整数的数组
    for (int i = 0; i < 10; ++i) {
        arr[i] = i * 2;//初始化数组
    }

    for (int i = 0; i < 10; ++i) {
        cout << "arr[" << i << "] = " << arr[i] << endl;
    }

    delete[] arr;


    /**
     * 使用nothrow避免异常
     */

    int* arr2 = new(nothrow) int[10000000000000000];
    if (!arr2) {
        cout << "内存分配失败" << endl;
    }
    else {
        cout << "内存分配成功" << endl;
        delete[] arr2;
    }

    /**
     * 异常处理
     * 内存分配失败的时候,会抛出一个std::bad_alloc异常
     * 使用try-catch来捕获异常
     */
    try {
        int* arr3 = new int[10000000000000000];
        cout << "内存分配成功:arr3" << endl;
        delete[] arr3;
    }
    catch (const std::bad_alloc& e) {
        cout << "捕获到内存分配失败" << e.what() << endl;
    }


    return 0;
}

$ ./new   
value is 10
arr[0] = 0
arr[1] = 2
arr[2] = 4
arr[3] = 6
arr[4] = 8
arr[5] = 10
arr[6] = 12
arr[7] = 14
arr[8] = 16
arr[9] = 18
内存分配失败
捕获到内存分配失败std::bad_alloc

random - 随机数


#include <iostream>
#include <random>
#include <iomanip>
#include <chrono>
using namespace std;
using namespace std::chrono;
/**
 * 生成随机数的工具
 *
*/
int main() {

    //方式1: 使用random_device 生成种子
    random_device rd;

    //方式2: 使用当前时间作为种子
    unsigned seed = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();

    ///使用基于 mersenne twister 随机数生成器,不传种子会固定生成
    mt19937 generator(rd());

    cout << "random number:" << generator() << endl;
    //输出random number:3499211612 (备注,每次输出都是一样的...)


/**
 * 使用均匀分布
 */
    mt19937 generator2(rd());

    //设置范围
    uniform_int_distribution<int> distribution(1, 10);
    for (int i = 0; i < 5;i++) {
        cout << "随机数:" << distribution(generator2) << endl;
    }
    /*
        输出:
        随机数:7
随机数:10
随机数:6
随机数:2
随机数:4
    */


    /**
     * 使用正态分布的随机数
     *
     */

    mt19937 generator3(seed);//使用当前时间作为种子
    //创建正态分布的随机数,均值为0,标准差为1
    normal_distribution<double> distribution2(0.0, 1.0);

    //设置输出格式,保留2位小数
    cout << fixed << std::setprecision(2);

    //生成
    for (int i = 0; i < 5; i++) {
        cout << "随机数:" << distribution2(generator3) << endl;
    }
    /*
        输出
        随机数:-0.15
随机数:0.13
随机数:-1.87
随机数:0.46
随机数:-0.21
    */

    return 0;
}

输出


$ ./random
random number:2805560704
随机数:7
随机数:7
随机数:6
随机数:3
随机数:5
随机数:0.35
随机数:0.44
随机数:0.71
随机数:-0.34
随机数:-0.91

utility - 实用工具类和函数


#include <iostream>
#include <utility>
#include <vector>
#include <type_traits>
using namespace std;



void process(int& i) {
    cout << "左值引用 i :" << i << endl;
}

void process(int&& i) {
    cout << "右值引用 i :" << i << endl;
}


template <typename T>

void forward_example(T&& t) {
    process(std::forward<T>(t));
}





struct MyClass
{
    MyClass(int, double) {}
};

template <typename T>
void test() {
    //获取T的构造函数类型,不调用 (注意导包#include <type_traits>.)
    using R = decltype(T(std::declval<int>(), std::declval<double>()));
    cout << is_same<R, MyClass>::value << endl;
}


/**
 包含了实用的工具类和函数:
 pair: 模板类,存储2个不一样类型的值
 make_pair:函数模板,创建pair对象,
 swap: 函数模板,交换两个对象的值
*/
int main() {


    /*
        使用pair,和maake_pair

    */

    auto p = make_pair(10, 20);
    cout << "firset value is " << p.first << endl;
    cout << "second value is " << p.second << endl;


    /**
     * swap
     *
     */


    int a = 1;
    int b = 2;
    cout << "初始化值,a=" << a << ",b=" << b << endl;

    swap(a, b);//交换
    cout << "交换后,a=" << a << ",b=" << b << endl;


    /**
     * move函数
     */

    vector<int> v1 = { 1,2,3,4,5,6,7,8 };
    vector<int> v2 = std::move(v1);


    cout << "v1 size: " << v1.size() << endl;//空的
    cout << "v2 size:" << v2.size() << endl; //8


    /**
     * forward函数
     * std::forward 是 C++ 中一个模板函数,主要用于完美转发(perfect forwarding)场景。它的作用是根据传递的参数类型(左值或右值)将参数以相同的类型进行转发,避免不必要的拷贝或移动操作。

关键概念

为了理解 std::forward,你需要先了解几个相关概念:

    1.	左值和右值:左值(lvalue)是一个可以取地址的对象,右值(rvalue)是临时的值,通常在表达式中出现,如字面常量或返回的临时对象。
    2.	完美转发:在泛型编程中,你希望函数模板能够保持参数的原始类型(左值或右值)并正确地将其传递给其他函数。std::forward 就是用于在这种情况下保持参数的值类别。

std::forward 的作用

当你想要在函数模板中保持参数的左值或右值特性时,std::forward 能根据传递的参数类型正确地转发:

    •	如果传递的是左值,std::forward 会保留其左值特性。
    •	如果传递的是右值,std::forward 会保留其右值特性。
     */


    int x = 10;
    forward_example(x); //左值
    forward_example(20); //右值
    forward_example(std::move(x)); //右值



    /**
     * declval
     */

    test<MyClass>();

    return 0;
}

输出


$ ./utility               
firset value is 10
second value is 20
初始化值,a=1,b=2
交换后,a=2,b=1
v1 size: 0
v2 size:8
左值引用 i :10
右值引用 i :20
右值引用 i :10
返回顶部