Category Archives: C++

数据结构学习1——准备&时间复杂度

  数据结构 + 算法 = 程序

准备

1.1 数据结构的概念

在这里插入图片描述

1.2 算法分析

1、算法分析包括对算法的时间空间分析,一般更注重时间复杂度的分析。

2、算法分析包括对算法的事前事后分析,受软硬件环境和机器代码等的影响,通过设计算法来计算的准确时间一般不够客观,因而通过事前估计的方式得出时空复杂度。

1.2.1 时间复杂度的概念

1、时间复杂度
  采用事前估计,用基本语句执行次数的规模来表示算法的运行时间,以 T(n) 表示,同时,更关注随着问题规模 n 的增长,算法消耗时间的增长趋势,因而一般使用渐进时间复杂度,采用O 表示法

2、大O表示法
  设算法运行时间为T(n) ,若存在两个正的常数c和n0,对于任意的n≥n0,都有T(n) ≤ c×f(n),则称T(n) = O(f(n)),也称为函数T(n) 以 f(n)为上界。
  换句话说,实际运行时间不超过 f(n) 的c倍,使用f(n)近似,能够反映变化趋势。

3、时间复杂度的计算
  首先,计算基本语句的执行次数,如下,T(n) = 2n+2。

int sum = 0;            //  1
for(int i=0;i<n;i++)    //  1 + n
    sun+=i;             //  n

  然后, 忽略常量、低次幂和最高次幂的系数,得到f(n) = n,使用大O表示法,则T(n) = O(n)。

4、常见时间复杂度
  常见时间复杂度的比较如下,O(1)的时间复杂度与规模无关,耗时最短,2的指数幂以及耗时更长的算法一般不采用。
在这里插入图片描述
  常见时间复杂度的举例如下,

//O(1):以下语句与规模n无关,执行时间是一个常数,因而时间复杂度为1
    int i=1;
    i+=1;
    int j=1;
    j+=i;
//O(log(n))
    for(int i=1;i<n;i*=2)
        sum+=i;
//O(n)
    for(int i=1;i<n;i++)
        sum+=i;
//O(nlog(n))
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j*=2)
            sum=sum+i+j;    
//O(n2)
    for(int i=1;i<n;i++)
        for(int j=1;j<n;j++)
            sum=sum+i+j;    
//O(n3)
    for(int i=1;i<n;i++)
        for(int j=1;j<n;j++)
            for(int k=1;k<n;k++)
                sum=sum+i+j+k;

1.2.2 实战练习(不断补充…)

  递归计算x的n次方,时间复杂度O(log(n)):

int func(int x,int n)
{
    if(n==0)
        return 1;

    int t = func(x,n/2)
    if(n%2==1)
        return t*t*x;

    return t*t;
}

  调和级数,时间复杂度O(nlog(n))

for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j+=i)
        sum=sum+i+j;            // n + n/2 + n/3 + ... + n/n = n(1+ 1/2 + 1/3 + ... + 1/n) = n(ln(n+1)+r)

系列参考

1) 时间复杂度的计算
2) 递归算法的时间复杂度?
3) 《数据结构(C++)边学边做》任平红等著
4) 《剑指offer》何海涛著

C++_3——库

  这一系列文章的目的是在学习了C++基础后,继续补充一些C++基础和进阶的知识点,包括C++11的相关内容。
以C++11标准为基础。

C++网站:http://www.cplusplus.com/reference/

1 ctime

    //包含头文件
    #include <ctime>

    //获取系统时间
    clock_t time = clock();

    //获取系统时间对应的秒数
    int sec = time/CLOCKS_PER_SEC;

2 string

string value transform

    #include <string>

    int std::stoi(const string& str, size_t* idx = 0, int base = 10); 
    int std::stol(const string& str, size_t* idx = 0, int base = 10);
    int std::stoul(const string& str, size_t* idx = 0, int base = 10);
    int std::stoll(const string& str, size_t* idx = 0, int base = 10);
    int std::stoull(const string& str, size_t* idx = 0, int base = 10);
    int std::stof(const string& str, size_t* idx = 0);
    int std::stod(const string& str, size_t* idx = 0);
    int std::stold(const string& str, size_t* idx = 0); 

    // 以下方法 仅保留6位小数
    string std::to_string (int val);
    string std::to_string (long val);
    string std::to_string (long long val);
    string std::to_string (unsigned val);
    string std::to_string (unsigned long val);
    string std::to_string (unsigned long long val);
    string std::to_string (float val);
    string std::to_string (double val);
    string std::to_string (long double val);

Iterators operate

    // 正向迭代器
    iterator begin() noexcept;
    iterator end() noexcept;
    // 反向迭代器
    iterator begin() noexcept;
    iterator end() noexcept;
    // 常量正向迭代器
    const_iterator cbegin() noexcept;
    const_iterator cend() noexcept;
    // 常量反向迭代器
    const_iterator crbegin() noexcept;
    const_iterator crend() noexcept;

size & resize & empty & clear

    //
    size_t size() const noexcept;
    size_t length() const noexcept;

    void resize (size_t n);
    void resize (size_t n, char c); // 空闲位置用字符 c 补充

    void clear() noexcept;
    bool empty() const noexcept;

Element operate

    //返回引用,可作为左值修改
    char& operator[] (size_t pos);
    char& at (size_t pos);
    char& back();
    char& front();

swap & assign & insert & erase & replace

    void push_back (char c);
    void pop_back();            //删除最后一个字符
    void swap (string& str);    //交换
    //以下有较多重载函数
    string& append (const string& str);             //
    string& assign (const string& str);             //替换
    string& insert (size_t pos, const string& str); //插入
    string& erase (size_t pos = 0, size_t len = npos);
    string& replace (size_t pos, size_t len, const string& str);

copy & find & substr & compare

    const char* c_str() const noexcept;
    const char* data() const noexcept;
    size_t copy (char* s, size_t len, size_t pos = 0) const;

    // 一个字符 搜索第一次出现的位置find 和 搜索最后一次出现位置 rfind
    size_t find (const string& str, size_t pos = 0) const noexcept; //pos 是搜索起始位置
    size_t find (const char* s, size_t pos = 0) const;
    size_t rfind (const string& str, size_t pos = npos) const noexcept; 
    size_t rfind (const char* s, size_t pos = npos) const;

    // 字符串任意字符 搜索第一次的位置 和 搜索最后一次出现位置
    size_t find_first_of (const string& str, size_t pos = 0) const noexcept;
    size_t find_first_of (const char* s, size_t pos = 0) const;
    size_t find_last_of (const string& str, size_t pos = npos) const noexcept;
    size_t find_last_of (const char* s, size_t pos = npos) const;
    size_t find_first_not_of (const string& str, size_t pos = 0) const noexcept;
    size_t find_first_not_of (const char* s, size_t pos = 0) const;
    size_t find_last_not_of (const string& str, size_t pos = npos) const noexcept;
    size_t find_last_not_of (const char* s, size_t pos = npos) const;

    // 子串
    string substr (size_t pos = 0, size_t len = npos) const;

    //比较,重载较多,也可比较其中一部分字符串
    int compare (const string& str) const noexcept;

3 cmath

gamma函数

    double tgamma (     double x);
    float tgamma (      float x);
    long double tgamma (long double x);
    double tgamma (T x);           // additional overloads for integral types
  1. 举个栗子
    /* tgamma example */
    #include <stdio.h>      /* printf */
    #include <math.h>       /* tgamma */

    int main ()
    {
      double param, result;
      param = 0.5;
      result = tgamma (param);
      printf ("tgamma(%f) = %f\n", param, result );
      return 0;
    }
  1. 输出
    tgamma (0.500000) = 1.772454

取整

    // floor向下取整
    // ceil向上取整
    // round四舍五入
    // 返回的是double,不是int
        cout<<floor(4.4)<<endl;//4
        cout<<floor(4.5)<<endl;//4
        cout<<ceil(4.4)<<endl;//5
        cout<<ceil(4.5)<<endl;//5
        cout<<round(4.4)<<endl;//4
        cout<<round(4.5)<<endl;//5

4 random

正态分布

    std::default_random_engine generator;                       //定义引擎
    std::normal_distribution<double> distribution(均值,标准差);  //定义正态分布
    double number = distribution(generator);                    //产生分布值
  1. 举个栗子
    // normal_distribution
    #include <iostream>
    #include <string>
    #include <random>

    int main()
    {
      const int nrolls=10000;  // number of experiments
      const int nstars=100;    // maximum number of stars to distribute

      std::default_random_engine generator;
      std::normal_distribution<double> distribution(5.0,2.0);

      int p[10]={};

      for (int i=0; i<nrolls; ++i) {
        double number = distribution(generator);
        if ((number>=0.0)&&(number<10.0)) ++p[int(number)];
      }

      std::cout << "normal_distribution (5.0,2.0):" << std::endl;

      for (int i=0; i<10; ++i) {
        std::cout << i << "-" << (i+1) << ": ";
        std::cout << std::string(p[i]*nstars/nrolls,'*') << std::endl;
      }

      return 0;
    }
  1. 输出
    normal_distribution (5.0,2.0):
    0-1: *
    1-2: ****
    2-3: *********
    3-4: ***************
    4-5: ******************
    5-6: *******************
    6-7: ***************
    7-8: ********
    8-9: ****
    9-10: *

5 algorithm

sort & reverse & rotate

    // 排序原理:快速排序
    // 比较函数comp:小于为升序排列,大于为降序排列,不输入比较函数,默认为升序排列
    void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
    // e.g.
    bool myfunction (int i,int j) 
    { 
        return (i<j); 
    }

    std::sort (myvector.begin(), myvector.end(), myfunction);

    // 排序原理:middle之前的元素,为,按顺序,升序排列最小的n个元素,或降序排列最大的n个元素;middle之后没有特定顺序
    // 比较函数comp作用同上
    void partial_sort (RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, Compare comp);

    // 倒序排列
    void reverse (BidirectionalIterator first, BidirectionalIterator last);

    // 旋转容器,以middle为第一个元素,之前元素,有序移动到尾部
    ForwardIterator rotate (ForwardIterator first, ForwardIterator middle, ForwardIterator last);
    //e.g.  1 2 3 4 5 6 7 8 9 ---> 4 5 6 7 8 9 1 2 3

min & max & min_element & max_element

    // 比较大小
    // 比较函数comp:返回值为 a是否小于b
    const T& min (const T& a, const T& b, Compare comp);
    // 比较函数comp:返回值为 a是否小于b,是的,还是“是否小于“
    const T& max (const T& a, const T& b, Compare comp);

    // 找最大/最小值的位置,返回的是指向该值的指针
    ForwardIterator min_element (ForwardIterator first, ForwardIterator last, Compare comp);
    ForwardIterator max_element (ForwardIterator first, ForwardIterator last, Compare comp);

merge

    // 合并两个排序序列
    // result 是合并到的容器的某个位置,一般是起始位置
    OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
                          InputIterator2 first2, InputIterator2 last2,
                          OutputIterator result, Compare comp);

copy

    // 复制first到last范围的内容 到 起始位置result
    OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result);

    // 有选择地复制
    OutputIterator copy_if (InputIterator first, InputIterator last,
                            OutputIterator result, UnaryPredicate pred);
    //e.g.
    std::vector<int> foo = {25,15,5,-5,-15};
    std::vector<int> bar (foo.size());
    auto it = std::copy_if (foo.begin(), foo.end(), bar.begin(), [](int i){return !(i<0);} );

    // 倒着来
    BidirectionalIterator2 copy_backward (BidirectionalIterator1 first,
                                          BidirectionalIterator1 last,
                                          BidirectionalIterator2 result);

move & swap

    // 移动到result后,原位置的元素,处于未指定但有效状态
    OutputIterator move (InputIterator first, InputIterator last, OutputIterator result);
    BidirectionalIterator2 move_backward (BidirectionalIterator1 first,
                                          BidirectionalIterator1 last,
                                          BidirectionalIterator2 result);

    // C++11之后,swap交换基于move
    void swap (T& a, T& b);
    void swap (T (&a)[N], T (&b)[N]);
    //交换一定范围的值
    ForwardIterator2 swap_ranges (ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2);

replace & fill & generate & remove

    // 单值替换单值:将范围内的 old_value 替换成 new_value
    void replace (ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value);

    // 单值替换条件值(多值)
    void replace_if (ForwardIterator first, ForwardIterator last, UnaryPredicate pred, const T& new_value );

    // 范围内填充单值 val
    void fill (ForwardIterator first, ForwardIterator last, const T& val);
    OutputIterator fill_n (OutputIterator first, Size n, const T& val); //返回最后一个填充值的下一个位置(C++11)

    // 范围内填充生成值
    void generate (ForwardIterator first, ForwardIterator last, Generator gen);
    //e.g.
    // function generator:
    int RandomNumber () { return (std::rand()%100); }
    std::vector<int> myvector (8);
    std::generate (myvector.begin(), myvector.end(), RandomNumber);

    // 移除范围内的值
    // 坑:这是假移除,符合移除要求的值被移动到末尾,最后返回的位置指向 尾部 所有移除元素的第一个元素位置
    ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val);

find

    // 测试所有元素满足条件
    bool all_of (InputIterator first, InputIterator last, UnaryPredicate pred);
    // 测试有元素满足条件
    bool any_of (InputIterator first, InputIterator last, UnaryPredicate pred);
    // 测试所有元素不满足条件
    bool none_of (InputIterator first, InputIterator last, UnaryPredicate pred);
    // 对每个元素执行一次函数fn
    Function for_each (InputIterator first, InputIterator last, Function fn);

    // 找到元素第一次出现的位置
    InputIterator find (InputIterator first, InputIterator last, const T& val);
    // 按条件第一次出现的位置
    InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate pred);

    // 出现次数
    count (InputIterator first, InputIterator last, const T& val);
    count_if (InputIterator first, InputIterator last, UnaryPredicate pred);

    // 在1号范围内,第一次出现2号范围内元素的位置,  可自定义pred 判断是否符合匹配情况
    ForwardIterator1 search (ForwardIterator1 first1, ForwardIterator1 last1,
                             ForwardIterator2 first2, ForwardIterator2 last2,
                             BinaryPredicate pred);

6 thread & future

(新开一个博客写多线程…)

C++_2——thread & Linux多线程

线程的创建、资源回收

#include <thread>
// 构造函数
    // 默认构造
    thread() noexcept;
    // 显式构造:输入重载函数的函数指针,导致编译错误
    template <class Fn, class... Args>
    explicit thread (Fn&& fn, Args&&... args);
#include <thread>

void func1(){}
void func2(int arg){}
void func3(int arg1, int arg2){}

// 创建
std::thread t1(func1);
std::thread t2(func2, 10);
std::thread t3(func3, 10, 20);

// 资源回收
if(t1.joinable())
    t1.join();
if(t2.joinable())
    t2.join();
if(t3.joinable())
    t3.join();

// 线程后台运行,不再受程序控制
std::thread t4(func1);
t4.detach();

摘抄1
在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程将可能早于子线程结束。如果主线程需要知道子线程的执行结果时,就需要等待子线程执行结束了。主线程可以sleep(xx),但这样的xx时间不好确定,因为子线程的执行时间不确定,join()方法比较合适这个场景。

摘抄2
是主线程等待子线程的终止。也就是说主线程的代码块中,如果碰到了t.join()方法,此时主线程需要等待(阻塞),等待子线程结束了(Waits for this thread to die.),才能继续执行t.join()之后的代码块。

摘抄3
detach调用之后,目标线程就成为了守护线程,驻留后台运行,与之关联的std::thread对象失去对目标线程的关联,无法再通过std::thread对象取得该线程的控制权。当目标线程主函数执行完之后,目标线程就结束了,运行时库负责清理与该线程相关的资源。
当一个thread对象到达生命期终点而关联线程还没有结束时,则thread对象取消与线程之间的关联,目标线程线程则变为分离线程继续运行。
detach使主线程不用等待子线程可以继续往下执行,但即使主线程终止了,子线程也不一定终止。
————————————————
版权声明:本文为CSDN博主「AllenSun-1990」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xikangsoon/article/details/104511675/

线程锁

c++之多线程中“锁”的基本用法

#include <thread>
#include <mutex>

/**************
    std::mutex
    对于非原子操作,多线程访问一个内存变量,任意时刻允许一个线程上锁
    易产生死锁,锁多了较为繁杂
*/
std::mutex mtx;         
mtx.lock()              //上锁失败则阻塞直到成功
mtx.try_lock()          //上锁失败则线程不阻塞
mtx.unlock()

/**************
    std::lock_guard
    构造时,调用传入对象的lock()
    析构时,调用传入对象的unlock()
    解决了死锁的问题
*/  
std::lock_guard<std::mutex> lg(mtx);        //此时调用mtx.lock()
                                            //当抛出异常或离开lg的作用域,lg被析构之后,调用mtx.unlock()

/**************
    std::uniqu_lock
    以std::lock_guard为基础,更为灵活
*/                                              
std::unique_lock<std::mutex> ul(mtx)
//提前解锁
ul.unlock()                             
//尝试锁,并判断是否锁住
std::uniqu_lock<std::mutex> ul(mtx.std::try_to_lock)    
ul.owns_lock    //bool量                                     

线程池

摘抄4
线程池(thread pool):一种线程的使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

本人太菜,写不出来,这里贴几个开源线程池:

  1. https://github.com/kanade2010/ThreadPool
    这个比较简单易懂,应对一般的问题应该够了。
  2. https://github.com/mtrebi/thread-pool
    这个功能比较强大,测试也比较严谨,就是看起来让人头皮发麻。

摘抄5
notify_one()与notify_all()常用来唤醒阻塞的线程。
notify_one():只唤醒等待队列中的第一个线程;不存在锁争用,所以能够立即获得锁。其余的线程不会被唤醒,需要等待再次调用notify_one()或者notify_all()。
notify_all():会唤醒所有等待队列中阻塞的线程,存在锁争用,只有一个线程能够获得锁。其余未获取锁的线程继续尝试获得锁(类似于轮询),而不会再次阻塞。当持有锁的线程释放锁时,这些线程中的一个会获得锁。而其余的会接着尝试获得锁。
————————————————
版权声明:本文为CSDN博主「吃素的施子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/feikudai8460/article/details/109604690

线程通信与同步

  1. 信号量:https://blog.csdn.net/ljianhui/article/details/10813469
  2. 互斥量:见线程锁部分
  3. 条件变量:condition_variable
    [c++11]多线程编程(六)——条件变量(Condition Variable)
  4. 读写锁:shared_lock
    C++实现读写锁ReadWriteLock
    C++11读写锁的实现 是上一篇的修改版
    C++多线程-读写锁 看文章总结部分

摘抄6
互斥量可以保护共享数据的修改,如果线程正在等待共享数据的某个条件出现,仅用互斥量的话就需要反复对互斥对象锁定解锁,以检查值的变化,这样将频繁查询的效率非常低。
条件变量可以让等待共享数据条件的线程进入休眠,并在条件达成时唤醒等待线程,提供一种更高效的线程同步方式。条件变量一般和互斥锁同时使用,提供一种更高效的线程同步方式。
————————————————
版权声明:本文为CSDN博主「低头看天,抬头走路」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/rusbme/article/details/97620877

摘抄7
在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。读写锁可以专门处理这种多读少写的情况。
————————————————
版权声明:本文为CSDN博主「cwl_java」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42528266/article/details/103913191

线程安全

摘抄8
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

线程与进程

  1. 进程是系统进行任务调度和资源分配的最小单位,进程之间可以并发执行。
  2. 线程是程序执行流的最小单元,一个进程可以有多个线程,进程内的线程在其他进程不可见。
  3. 线程切换快于进程切换。
  4. 任务调度:时间片轮转,每个任务执行一个时间片的时间长度,时间片结束后,强制暂停,执行另一个时间片的程序,等到该任务的时间片再次到来。

进程间的通信方式

  1. 匿名管道:https://blog.csdn.net/ljianhui/article/details/10168031
    半双工,父子进程

    //创建
    #include <unistd.h>
    int pipe (int fd[2]);   //返回:成功返回0,出错返回-1    fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。
    //创建子进程
    pid_t id = fork(); 

在这里插入图片描述

  1. 消息队列:https://blog.csdn.net/ljianhui/article/details/10287879
  2. 有名管道:https://blog.csdn.net/ljianhui/article/details/10202699
  3. 信号量:https://blog.csdn.net/ljianhui/article/details/10243617
  4. 信号:https://blog.csdn.net/ljianhui/article/details/10128731
  5. 共享内存:https://blog.csdn.net/ljianhui/article/details/10253345
  6. 数据报套接字:https://blog.csdn.net/ljianhui/article/details/10697935
  7. 数据流套接字:https://blog.csdn.net/ljianhui/article/details/10477427

其他摘抄和参考

摘抄1
从C++11开始,标准库里已经包含了对线程的支持,std::thread是C++11标准库中的多线程的支持库,pthread.h是标准库没有添加多线程之前的在Linux上用的多线程库,而之前在windows上的多线程支持要包含wndows.h

  1. [c++11]多线程编程(一)——初识
  2. [c++11]多线程编程(二)——理解线程类的构造函数
  3. C++ thread用法总结(整理)
  4. Thread之三:Thread Join()的用法
  5. 基于C++11实现线程池的工作原理
  6. C++11条件变量:notify_one()与notify_all()的区别
  7. c++11 之emplace_back 与 push_back的区别
  8. 进程与线程
  9. 进程间的通信方式——pipe(管道)
  10. Linux进程间通信——使用消息队列

C++_1——基本数据类型

  这一系列文章的目的是在学习了C++基础后,继续补充一些C++基础和进阶的知识点,包括C++11的相关内容。

A 简单数据类型

a1 整型

  c++提供的基本整型包括char、short、int、long、long long(c++11)及其无符号版本,共十种。

 整型长度问题

  c++中,整型的长度不是固定的,取决于系统,基本情况如下面的表所示,可以使用sizeof()来确定具体系统中整型的长度。 整型变量 长度
short 至少16位
int 至少与short一样长
long 至少32位,且至少与int一样长
long long 至少64位,且至少与long一样长

  实际上,一个字节(bytes)未必是8位(bit),具体的取值取决于字符集的数目,比如ASCII这个字符集,8位即可表示所有字符集中的字符,因此是8位,而在Unicode中,可能会用到更大的字符集,8位就不够了。一般,在8位字节系统中,short为2字节,int和long为4字节,long long 为8字节。

 cout输出不同的进制

    std::cout<<std::hex;    //后续的数值以16进制显示
    std::cout<<std::oct;    //后续的数值以8进制显示

 整型常量

整型常量类型一般是int,除非有特定后缀或值过大。
  ① 后缀包括(u无符号,L长整型,LL及其组合),如23333L,23456uL。
  ② 值过大,对于十进制整型,会选择能装下该值的有符号最小类型,对于8/16进制整型,会选择能装下该值的无符号最小类型。

 char & wchar_t

1 字符型char:
  ① 8位,满足常见的ASCII或者EBCDIC字符集(Unicode的一个小子集),可用单引号行形式表示字符,可直接表示(‘A’)、或通过反斜杠进制数(‘\012’、‘\0xa’)。
  ② char既可能有符号,也可能无符号,取决于c++实现,但可以显式地指定。存储ASCII可用默认,因为ASCII只需要低七位,128个字符。

2 宽字符型wchar_t:
  ① 需求无法满足,位数需要多于8位,表示扩展字符集。
  ② 与之对应的是wcin和wcout,用于处理wchar_t流。
  ③ 显式表示宽字符常量或字符串常量,加前缀L,如表示宽字符P:L‘P’。

 ASCII、Unicode、ISO10646

  ① ASCII是Unicode子集。
  ② Unicode 为各种字符和符号提供标准数值编码,每个字符的编号称为码点
以\u开头 以\U开头
用8个十六进制位表示的通用编码名 用16个十六进制位表示的通用编码名

  ③ ISO10646和Unicode相似,都是通用字符编码,两者标准同步。

 C++11 新整型

1、char16_t / char32_t char16_t char32_t
无符号,长16位 无符号,长32位
前缀u 前缀U

2、size_t
  1)size_t在不同架构下的定义不同。
  2)32位系统下一般定义为unsigned int ,占4字节;64位系统下一般定义为unsigned long,占8字节。
  3)size_t方便移植程序,如计数等操作,保证能容纳实现所建立的最大对象的字节大小。

3、long long 和 unsigned long long

a2 浮点型

  ① 浮点型包括float、double、long double。
  ② 浮点型常量一般是double。
  ③ 指数范围一般是-37~37。
  ④ 浮点型的位数表示的是有效位数,float一般32位,double一般64位。

a3 类型转换

 整型提升

    1 如果有一个操作数的类型是 long double,则将另一个操作数转换为 long double。
    2 否则,如果有一个操作数的类型是 double,则将另一个操作数转换为 double。
    3 否则,如果有一个操作数的类型是 float,则将另一个操作数转换为 float。
    4 否则,说明操作数都是整型,因此执行整型提升。
    5 在这种情况下,如果两个操作数都是有符号或无符号的,且其中一个操作数的级别比另一个低,则转换为级别高的类型。
    6 如果一个操作数为有符号的,另一个操作数为无符号的,且无符号操作数的级别比有符号操作数高,则将有符号操作数转换为无符号操作数所属的类型。
    7 否则,如果有符号类型可表示无符号类型的所有可能取值,则将无符号操作数转换为有符号操作数所属的类型。
    8 否则,将两个操作数都转换为有符号类型的无符号版本。

 强制转换

//1 通用 
    (typename) value    
    typename (value)        
 //2 C++11
    static_cast<typename> (value)

a4 类型别名

C++允许给类型建立别名,建立方式有两种:

    #define aliasName typeName
    #define BYTE char

    typedef typeName aliasName;
    typedef char byte;

但前者只是简单替换,有时不适用,举例如下

    #define FLOAT_POINTER float*

    FLOAT_POINTER a,b;  //等价于 float* a,b; 这样b只是一个float

B 复合数据类型

b1 数组

1 数组全部初始化为零,有以下几种方式
  ① 显式全部初始化为0

    int array[3] = {0,0,0};

  ② 循环遍历每个元素初始化为0
  ③ 利用编译器的特点,显式地初始化第一个元素为0,编译器将其他元素设为0

    int array[3] = {0};

  ④ C++11新特性中,列表初始化可省略等号,当大括号内没有值时,所有元素将被初始化为0

    int array[3] = {};
    int array[3]{}; //作用同上

b2 字符串 C-string

这里讨论C-风格字符串,不讨论string类。

1 结尾空字符 \0
  ① 字符串常量结尾隐式添加 \0
  ② 字符串数组的sizeof()和strlen(),前者计算数组大小,后者只计算不包括空字符的字符串长度

    char str[10] = "abcdefg";
    sizeof(str);//10
    strlen(str);//7

2、原始字符串 raw string
  1)原始字符串将保证字符串内容不加修改地输出,转义字符将以两个常规字符的形式表示。
  2)方法是使用定界符 "()" 并通过前缀R来表示原始字符串,如下:

    std::cout<<R"(aaa "bbb" ccc"\n")"<<std::endl;
    //输出:  aaa "bbb" ccc"\n"

  3)如果字符串内容中包含 )" ,当编译器见到第一个 )" 时,会认为字符串结束,就会造成错误。解决方法是定义一个特殊的定界符,规则是在引号与括号之间增加任意字符,同时保证首尾增加的字符是一样的,如下:

    std::cout<<R"+6(aaa "bbb" ccc"\n")+6"<<std::endl;//这里定义了 "+6( 和 )+6" 的定界符,其中增加了+6这些字符
    //输出:  aaa "bbb" ccc"\n"

  当然,任意字符,不包括空格、左右括号、斜杠、控制字符(制表符、换行符等)。

b3 结构体 struct

1 C++11结构初始化

    struct myStructType
    {
        int id;
    };
    myStructType myStruct{};    //这将结构内成员都设置为0

b4 共用体 union

1 意义:数据项使用两种或更多格式的时候(不同时使用不同格式),常用于节省内存。
2 共用体的长度为其最大成员的长度。

b5 枚举 enum

1 定义,枚举含有多个枚举量,每个枚举量,对应到从0开始的整数,或显式地赋值,当显示赋值部分枚举量时,未赋值的量对应的整数为前一个加1

    enum myEnumType{a,b,c,d,e,f,g};
    enum myEnumType{a = 8,b,c,d,e,f,g};

2 枚举只有赋值运算符,枚举可提升为整型
3 整型可强制转换为枚举量,前提是整型的值在枚举范围内,枚举范围的上限是大于最大值的最小2的幂再减1,对于下限,最小值大于0,则下限是0,否则与上限规则类似
4 应用:定义符号常量用在switch等地方

    enum {a,b,c,d,e,f,g};

    int main()
    {
        int code = 1;
        switch (code)
        {
            case a : break;
            case b : break;
            default : break;
        return 0;
    }

5 C++11:作用域内枚举
  1)对于下面的情况,在同一个作用域内定义相同的枚举量,会出现冲突

    enum egg {Small, Medium, Large};
    enum apple {Small, Medium, Large};

  2)C++11提供了一种新枚举,枚举量的作用域为类或结构体,使用枚举名来限制枚举量

    enum class egg {Small, Medium, Large};
    enum class apple {Small, Medium, Large};
    enum struct peach {Small, Medium, Large};
    enum struct box {Small, Medium, Large};

    egg s = egg::Small;

  3)常规枚举能够实现整型提升,但作用域内的枚举不能隐式转换为整型。
  4)作用域内枚举的底层类为int,可以通过下述方式修改底层类型,底层类型必须为整型。

    enum class : short egg{Small, Medium, Large};

b6 指针

1 动态联编(dynamic binding)与内存泄露(memory leak)
  new/delete配对使用。
  同一个内存重复释放将导致不确定的结果,应避免,例如不能创建两个指向同一个内存块的指针。
  动态数组分配内存后,系统程序跟踪分配的内存量,但是跟踪结果不公开,不能通过sizeof来确定动态数组包含的字节数。

2 指针算数
  1)指针算数:指针变量+1,代表指向下一个元素的地址:例如指向double类型的指针,值+1,则指针指向的地址+8。
  2)数组名与指针
  一般情况下,C++将数组名解释为数组第一个元素的地址,stack[1] -> *(stack + 1)。
  特殊情况下,对数组名取地址,这个地址将指向整个数组,这与第一个元素的地址是一样的,但是指向的内存块大小不同。

int array[10];
cout<<array<<endl;  //输出 array[0] 的地址           类型为 int*            指针+1 导致 地址+4
cout<<&array<<endl; //输出 array 整个数组的地址      类型为 int(*)[10]      指针+1 导致 地址+4*10

注:默认 int为4字节。

  数组名与指针的区别:前者为常量不能修改,但可以使用sizeof查看数组长度,后者修改值代表指针指向地址的前后移动,而sizeof得到的是指针的长度。

3 指针与字符串
  C++多数表达式中,char数组名、char指针、字符串常量都被解释为字符串第一个字符的地址。要打印该地址,需要将地址量转换成(int*),否则将打印从第一个字符直至空字符。

C++11 :auto 与 decltype

  auto:自动类型推断,要求显示初始化,使得编译器能够将变量类型设置成初始值类型。一般用于复杂类型,在模板中常见,对于简单数据类型容易误用。
  decltype:将变量声明为表达式指定的类型。

    int y;
    decltype(y) x;  //定义x,使x的变量类型与y相同

  对于模板来说,只有实例化之后才能确定类型,因此decltype常用。

    template <typename T, typename U>
    void ef(T t , U u)
    {
        decltype(T*U) tu;
        //...
    }