Qeuroal's Blog

静幽正治

Clion 中使用头文件定义类,源文件实现

include什么

导入头文件(.h)和源文件(.cpp)
Note: 只导入 源文件(.cpp) 也可以

CMakeLists.txt

  • 手动添加
1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.15)
project(HCpp)

set(CMAKE_CXX_STANDARD 11)

add_executable(HCpp main.cpp)
add_executable(ElemType ElemType.cpp ElemType.h)
  • 自动添加
1
2
3
4
5
6
7
# 遍历项目根目录下所有的 .cpp 文件
file (GLOB files *.cpp)
foreach (file ${files})
string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file})
add_executable (${exe} ${file})
message (\ \ \ \ --\ src/${exe}.cpp\ will\ be\ compiled\ to\ bin/${exe})
endforeach ()

代码

ElemType.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef HCPP_ELEMTYPE_H
#define HCPP_ELEMTYPE_H

#include <iostream>

class ElemType {
public:
int data;

ElemType();
ElemType(int);
friend std::ostream &operator <<(std::ostream &, const ElemType &);
};

#endif //HCPP_ELEMTYPE_H

ElemType.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "ElemType.h"

ElemType::ElemType() {
this->data = 0;
}

ElemType::ElemType(int data) {
this->data = data;
}

std::ostream& operator <<(std::ostream &out, const ElemType &e)
{
out << e.data;
return out;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include "ElemType.h"
#include "ElemType.cpp"

using namespace std;

int main() {
ElemType e = ElemType(10);
cout << e << endl;
return 0;
}

运算符重载:详见 运算符重载

重载运算符规则

  1. 重载意义

    函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作;

    运算符重载(Operator Overloading)也是一个道理,同一个运算符可以有不同的功能;

    C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。

    比如,我们定义两个string类对象a,b后我们之所以可以使用+运算,是因为string类重载了+运算符。

  2. 语法

    1
    2
    3
    4
    <返回类型说明符> operator <运算符符号>(<参数表>)
    {
    <函数体>
    }
  3. 哪些运算符可以被重载,哪些运算符不能被重载

    • 可以被重载的运算符:

      算术运算符:+ , - , * , / , % , ++ , --

      位操作运算符:& , | , ~ , ^ , << , >>

      逻辑运算符:! , && , ||

      比较运算符:< , > , >= , <= , == , !=

      赋值运算符:= , += , -= , *= , /= , %= , &= , |= , ^= , <<= , >>=

      其他运算符:[] , () , -> , ,(逗号运算符) , new , delete , new[] , delete[] , ->*

    • 不允许的运算符重载:

      . , .* , :: , ?: ,siezof

重载运算符函数在类内部

  1. Notes:

    1. 运算符重载函数是类的成员函数时>

      1. 注意

        要是内部的,必须得是<=1个参数,不需要加 friend(友元函数)

      2. 语法(在类内声明,在类外实现时的代码)
        1
        2
        3
        4
        5
        <返回类型说明符> <类名>::operator <运算符符号>(<参数表>)
        {
        <函数体>
        }

    2. 运算符重载函数不是类的成员函数时(或者说当运算符函数是非成员函数时

      1. 函数在类中声明的时候需要在前面加上 friend(友元函数),不管参数是不是类的对象
      2. 函数的参数与该运算符作用的运算对象数量一样多
    3. 为什么用 const&:

      1. const

        1. 我们不希望在这个函数中对用来进行赋值的“原版”做任何修改。函数加上const后缀的作用是表明函数本身不会修改类成员变量。
        2. 加上const,对于const的和非const的实参,函数就能接受;如果不加,就只能接受非const的实参。
      2. &(引用):

        这样可以避免在函数调用时对实参的一次拷贝,提高了效率。

  2. 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    /**
    * Author: Dgimo
    * Date: 2020/3/30
    * TODO: 类运算符重载
    * Describe:
    */
    #include <iostream>

    using namespace std;


    typedef struct {
    int first;
    int second;
    } TestStuct;


    /*
    * 类运算符重载
    */
    class TestClass {
    private:
    int first;
    int second;

    public:
    TestClass(int, int);
    TestClass();

    void show(string);

    TestClass operator+(const TestClass &);
    TestClass operator+(const TestStuct &);
    friend TestClass operator+(const TestClass &, const TestClass &); // 如果不是内部的,得加friend 声明不是内部的,如果不是两个会怎样?见下面!!!
    friend TestClass operator+(const TestStuct &, const TestStuct &); // 只要在类内部定义的运算符重载,就需要加上friend

    TestStuct ts1, ts2;
    ts1.first = 1, ts1.second = 2;
    ts2.first = 10, ts2.second = 20;

    tc1.show("tc1");
    tc2.show("tc2");

    add_tc = tc1 + tc2; // TestClass + TestClass
    add_tc.show("add_tc");

    add_tc1 = add_tc + ts1; // TestClass + TestStruct
    add_tc1.show("add_tc1");

    add_ts = ts1 + ts2; // TestStrcut + TestStrcut
    add_ts.show("add_ts");

    return 0;
    }

重载运算符在函数外部

  1. 暂时未发现 Bug

  2. 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    /**
    * Author: Dgimo
    * Date: 2020/3/31
    * TODO: 结构体运算符重载
    * Describe:
    */

    #include <iostream>

    using namespace std;

    /*
    * 结构体运算符重载
    */
    typedef struct {
    int first;
    int second;
    } TestStuct;

    TestStuct operator+(TestStuct ts1, TestStuct ts2)
    {
    TestStuct res;
    res.first = ts1.first + ts2.first;
    res.second = ts1.second + ts2.second;
    return res;
    }

    bool show(TestStuct ts)
    {
    cout << "first: " << ts.first << ", second: " << ts.second << endl;
    }

    int main()
    {
    TestStuct ts1;
    ts1.first = 1;
    ts1.second = 2;

    TestStuct ts2;
    ts2.first = 2;
    ts2.second = 3;

    TestStuct res_t = ts1 + ts2;
    show(res_t);
    return 0;
    }

<<>> 只能定义友元函数

  1. 原因

    定义为成员函数,那么就隐含this指针了对吧,你要知道重载其实也是一种函数,那么函数就有调用他的对象,如果是成员函数,那么调用他的对象就肯定是想对应的类对象了对吧,但是<<和>>调用的对象肯定只能是cout或者cin对吧,那么久不能定义为成员函数了,只有定义成友元,那么就可以把cin,cout作为一个参数传进你重载的操作符函数里面去了

  2. 语法:

    1. 在类中:

      1
      2
      3
      4
      5
      6
         friend ostream &operator <<(ostream &out, const ElemType &e);

      2. 在外面:

      ```cpp
      ostream &oprator <<(ostream &out, const ElemType &e);
  3. 代码

    1. 头文件(.h)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      #ifndef ELEMTYPE_H
      #define ELEMTYPE_H

      #include <iostream>

      using namespace std;

      class ElemType
      {
      public:
      int data;
      ElemType();
      ElemType(int);

      friend ostream &operator <<(ostream &out, const ElemType &e); // Success

      protected:

      private:
      };

      #endif // ELEMTYPE_H
    2. 源文件(.cpp)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      #include "ElemType.h"

      ElemType::ElemType()
      {
      //ctor
      this->data = 0;
      }

      ElemType::ElemType(int data)
      {
      this->data = data;
      }

      ostream& operator <<(ostream &out, const ElemType &e)
      {
      out << e.data;
      return out;
      }

JetBrains 家族其他软件的手法相似,按需使用即可,此路不通,找度娘

  • Win10

  1. 配置

    1. 下载 setting_3.zip, setting_4.zip, setting_5.zip, setting_6.zip, setting_7.zip, setting_8.zip

      数字越大,版本越新

    2. 打开 Clion

    3. 选择: File - Import setting

    4. 选择这个 setting 文件即可(一般全部导入,看个人)

  2. 取消开始界面打开默认项目配置

    1. 打开:File - Settings - Appearance & Behavior - System Settings
    2. 取消勾选 Reopen last project on startup
  3. 更改模板注释:

    1. 打开:File - Settings - Editor - File and Code Templates - Includes

    2. 下面有一个面板,在面板输入

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #if ($HEADER_COMMENTS)
      /**
      * author ${USER_NAME}
      * date ${DATE}
      * description
      #if ($ORGANIZATION_NAME && $ORGANIZATION_NAME != "")
      * copyright (c) $YEAR ${ORGANIZATION_NAME}#if (!$ORGANIZATION_NAME.endsWith(".")).#end All rights reserved.
      #end
      */
      #end

    参考文档

  4. Clion 多个 main 函数存在

    1. CMakeLists.txt 中加入

      1
      2
      3
      4
      5
      6
      7
      # 遍历项目根目录下所有的 .cpp 文件
      file (GLOB files *.cpp)
      foreach (file ${files})
      string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file})
      add_executable (${exe} ${file})
      message (\ \ \ \ --\ src/${exe}.cpp\ will\ be\ compiled\ to\ bin/${exe})
      endforeach ()
    2. 为了方便可以自己定义快捷键:(个人用的是: Alt-Shift+R

      1. File - Settings - Keymap
      2. 搜索 Reload cmake project 即可
    3. 参考:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      cmake_minimum_required(VERSION 3.15)
      project(DataStruct)

      set(CMAKE_CXX_STANDARD 11)

      # 遍历项目根目录下所有的 .cpp 文件
      file (GLOB files *.cpp)
      foreach (file ${files})
      string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file})
      add_executable (${exe} ${file})
      message (\ \ \ \ --\ src/${exe}.cpp\ will\ be\ compiled\ to\ bin/${exe})
      endforeach ()
  5. CMakeLists 自动加载

    1. 打开 Files - settings - Build, Execution, Deployment - CMake
    2. 选择 Automatically reload CMake project on editing
  6. 侧边栏无焦点自动隐藏

    1. 选择侧边栏的 扳手
    2. 选择 View Mode - Dock Unpinned
  7. 汉化步骤

    1. 下载 resources-cn.jar
    2. resources-cn.jar 复制到 安装目录下lib文件夹下
    3. 结束
  8. 快捷键

    快捷键 含义 备注
    Ctrl + Shift + U 大小写切换
    Ctrl + Shift + Space 补全单词 new后直接键入构造器、构造器内提供构造信息
    Ctrl + Alt + L 格式化代码
    Ctrl + Shift + Backspace 回到最后编辑的地方
    Ctrl + N 查找类
    Ctrl + F12 快速查看类成员
    Ctrl + P 提示参数信息
    Ctrl + Alt + B 查看接口、抽象类的实现类
    Ctrl + F7 找到某个类、变量、函数等在文中被使用到的的地方
    连续点击Shift 全局搜索
    Shift + F6 重新命名变量
    连续按两次Esc 搜索框就会消失
    Alt + insert 添加自动代码生成信息 新建新的文件
    Ctrl + j 插入完整的代码块 默认只有 for, iter, itit,也可以自己在 Setting—>Live Templates 里添加自定义的代码块
    Ctrl + Alt + -/+ 展开折叠当前函数、类
    Ctrl + Shift + -/+ 展开折叠所有函数、类
    Ctrl + Alt + T 就可以选择给这些代码包裹上 if, for 等(surround with)
  9. 自动换行

    idea设置自动换行其实很简单,只需要进行一步操作就可以了。

    看下图:我们只需要在相应的位置加上文件的类型就可以了,比如我们写的类都是.java文件,那么我们只需要加上 *.java就可以实现自动换行了。

  10. JetBrains学生认证:自行百度

  11. JetBrains sync配置同步:

    Files - Sync ……

  12. Clion 输出乱码

    1. 在设置里将编码都设置为UTF8,并点击OK保存设置;
    2. 按住Ctrl+Shift+Alt+/选中Registry...,然后取消掉 run.processes.with.pty 后面的

双系统时间不一致

  1. 首先我们把硬件时间(BIOS时间)将默认的UTC改为CST,然后重启,使得两个系统的时间保持一致

    1. 上述代码中的1改为0即可将硬件时间修改为默认的UTC时间: sudo timedatectl set-local-rtc 1
    2. 重启系统使改动生效: sudo reboot
  2. 接着我们更新一下系统时间

    1. 这里的操作是安装ntpdate工具: sudo apt-get install ntpdate
    2. 使用ntpdate工具从time.windows.com上面同步时间: sudo ntpdate time.windows.com
  3. 最后我们将时间更新到硬件上,以保证重启后改动不会被还原

    这里的代码意思是使用hwclock命令将本地时间localtime从sysclock同步到hwclock: sudo hwclock --localtime --systohc

Ubuntu 科学上网

  1. 点击这里下载AppImage
  2. 点击这里下载deb

注意:必须要安装 Python2、Python3,才可以使用
deb安装: sudo dpkg -i <deb文件>

Windows Defender关闭

  1. cmd 运行 regedit
  2. 定位到: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SecurityHealthService
  3. 在右侧找到 DWORD(32位) 值,名为 Start
  4. 修改数值数据为4(十六进制)
  5. 重启文件资源管理器,或注销再登录/重启系统

注意:

  1. 如果你安装第三方杀软或安全软件,Windows Defender会自动被关闭。
  2. start的默认值为 3

Ubuntu 安装字体

详见Ubuntu配置Win10字体

Ubuntu 安装zsh

  1. git、vim、oh-my-zsh 的安装和配置
  2. Linux 安装 oh-my-zsh

Ubuntu不能访问Windows下的磁盘

  1. 控制面板—系统和安全—电源选项—选择电源按钮的功能
  2. 首先点击上部分的 更改当前不可用的设置
  3. 然后去掉 启用快速启动(推荐) 前面的勾即可
  4. 这个时候再选择Windows关机后,就是完全关机了

不能读取exfat格式的u盘问题

  1. Ubuntu默认不支持exfat格式的U盘,只需要运行如下的命令:sudo apt-get install exfat-utils
  2. 安装完成后重启即可

不能挂载移动硬盘问题

  1. 在终端输入如下命令,查看分区挂载情况: sudo fdisk -l
  2. 输入下面的命令: sudo ntfsfix /dev/sdb1

    注意:/dev/sdb1 为移动硬盘的设备名,找到硬盘size对应的设备名

Ubuntu 查找文件

  1. 命令:locate <文件名>
  2. 网址:在ubuntu中搜索文件或文件夹的方法

Ubuntu 更新慢

  1. 步骤:

    1. 选择 软件和更新
    2. 打开 软件和更新窗口
    3. 我们点击 Ubuntu软件 选项卡下的 其他站点
    4. 这里有好多国外的源及我们国内的源,我也试了不少,我目前使用的是aliyun的源,感觉速度还不错,其他国内的速度也都不可以,大家也可以试试。不要怕麻烦,就是要折腾
    5. 我们选择 mirrors.aliyun.com,然后点击 选择服务器
    6. 同样需要我们密码认证
    7. 修改成功
    8. 等待系统更新完毕,源更新成功后,会自动关闭窗口。此时,我们安装软件或更新软件(系统)就是用的我们选择的源啦。
  2. 网址:Ubuntu加快软件安装更新速度方法

Python工程的组织结构

包、模块、类、函数、变量

  • 最顶级的组织结构:包(文件夹)
  • 第二个层级:模块(文件)
  • 第三个层级:类
  • 第四个层级:函数、变量(不属于组织结构,是类本身的特性)

其中:

  1. 包:整个文件夹
  2. 模块:一个 py 文件

Python包与模块的名字

区分不同包的同名模块:包名.模块名
形成的模块的路径叫做命名空间
一个包下面可以有子包,模块可以和包平级
普通文件夹想要变成包必须要有init.py文件
__init__.py 本身也是一个模块,可以不写内容只是标注包
特殊地,__init__.py 模块的名字就是包名

__init__.py用途

__init__.py 该文件的作用就是相当于把自身整个文件夹当作一个包来管理,每当有外部import的时候,就会自动执行里面的函数。

import导入模块

对于重复的定义需要从其他模块里引用。
利用 import 模块名 导入

1
2
3
4
5
6
# PackageTest1.Test1
a = 1

# PackageTest1.Test2
import Test1
print(Test1.a)

import导入的总是模块,需要用模块名.变量名的方法引用
可以用as简化书写

1
2
3
4
5
6
# PackageTest1.Test1
a = 1

# PackageTest1.Test2
import Test1 as t1
print(t1.a)

同级目录可以直接导入模块
优点是可以一眼看出属于哪个模块

from import 导入变量

1
2
3
# PackageTest1.Test2
from c1 import a
print(a)

可以使用 from 包 import 模块 导入模块
导入时使用 模块.变量
导入大量变量时用 *

1
2
3
4
5
6
7
8
9
10
# PackageTest1.Test1
a = 1
b = 1
c = 1

# PackageTest1.Test2
from Test1 import *
print(a)
print(b)
print(c)

控制 *

1
2
3
4
5
6
7
8
9
10
11
12
# PackageTest1.Test1
__all__ = ['a', 'b']
a = 1
b = 1
c = 1


# PackageTest1.Test2
from Test1 import *
print(a)
print(b)
print(c) # 这里会报错,因为没有导入c

注意

  1. 使用 * 导入时,导入的是在所在包中 __init__.py 存在的变量
  2. 文件目录:

    initTest

    PackageTest1

    __init__.py
    Test1.py

    Test2.py

    Test.py

    其中:

    PackageTest1.__init__.py

    1
    2
    3
    4
    5
    import pandas as pd
    import numpy as np

    print("You have imported mypackage")
    # __all__ = ['pd', 'np'] # 这里不需要 __all__ 控制

    PackageTest1.Test1.py

    1
    2
    3
    4
    5
    6
    7
    from __init__ import * # 可以使用所在包中的 __init__ 导入大量的包

    class Test1():

    def __init__(self):
    a = np.array([i for i in range(10)])
    print(a)

    Test.py

    1
    2
    3
    4
    from PackageTest1 import * # 因为 PackageTest1.__init__ 中只有 pd, np 这两个变量,因此使用 * 只导入进来了 pd, np
    from PackageTest1.Test1 import Test1 # 因为在 PackageTest1.__init__ 中没有 Test1 这个模块,更没有 Test1 这个类,因此想要使用必须自行导入

    Test1()

PackageTest1.__init__ 中导入 Test1 模块、类

  1. 导入 Test1 模块
    PackageTest1.__init__.py中添加 import PackageTest1.Test1 就可以在 Test.py中使用 Test1.Test1() 创建 Test1 类了。
  2. 导入 Test1
    1. PackageTest1.__init__.py中添加 from PackageTest1.Test1 import Test1 就可以在 Test.py中使用 Test1() 创建 Test1 类了。
    2. PackageTest1.__init__.py中添加 import PackageTest1.Test1 是错误的,会报错: ModuleNotFoundError: No module named 'PackageTest1.Test1.Test1'; 'PackageTest1.Test1' is not a package

__init__.py 用法

  1. 当包被导入时,__init__.py 会首先被自动执行,类似于构造函数
  2. __init__.py 的应用场景
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # PackageTest1.__init__.py
    __all__ = ['a','b'] #初始化*

    import sys #批量导入库
    import datetime
    import io


    #Test.py
    import Test1
    print(Test1.sys.path)

    此时引用因为不是用的 * , 所以必须使用 包.类.成员函数 进行调用。

多层目录各层互相调用

pacgage1

__init__.py
test1.py
test2.py

test3.py

test3 调用 package1,而 test1 调用 test2,此时,在 package1.__init__.py 中添加路径,如下:

1
2
import sys
sys.path.append("./package")

包与模块的几个常见错误

  1. 包和模块是不会被重复导入的,只会执行一次(入口文件的概念)
  2. 避免循环导入,不要形成闭环
  3. 导入模块的时候会执行模块里所有的代码
  4. 如果使用 if __name__ == '__main__' 则不会执行该代码块中的代码

Windows环境

1. 安装 CUDAcudnn

  1. 安装 CUDA

    1. 查看显卡信息,[CUDA版本和驱动版本的对照表]

    2. 下载对应的cuda,[CUDA各个版本下载地址]

      最高的版本:低事实最高版本一个级别

      最好低最高版本低几个版本。

    3. 安装cuda,勾掉以下3个:

      1. NVIDIA Geforce Experience ……
      2. CUDAVisual Studio ……
      3. driver ……display driver
    4. 其他默认即可

    5. 注意:版本对照等重要信息见文末的参考资料

      问题:我的显卡比10.1大,但是没问题,

  2. 安装 cudnn

    1. 下载和 CUDA 对应的版本(建议不要下载最新的,看好官方文档在决定下载哪个)点击这里下载
    2. 解压
    3. 该文件名为 cudnn
    4. 复制到目录:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2
  3. 添加环境变量,详见参考资料

2. 安装 Tensorflow

  1. 安装 Tensorflow-cpu 版:
    1
    pip install --upgrade tensorflow
  2. 安装 Tensorflow-gpu 版:
    1
    pip install --upgrade tensorflow-gpu

3. 报错

  1. 报错信息:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    Traceback (most recent call last):
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 18, in swig_import_helper
    return importlib.import_module(mname)
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
    File "<frozen importlib._bootstrap>", line 986, in _gcd_import
    File "<frozen importlib._bootstrap>", line 969, in _find_and_load
    File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
    File "<frozen importlib._bootstrap>", line 666, in _load_unlocked
    File "<frozen importlib._bootstrap>", line 577, in module_from_spec
    File "<frozen importlib._bootstrap_external>", line 906, in create_module
    File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
    ImportError: DLL load failed: 找不到指定的模块。


    During handling of the above exception, another exception occurred:


    Traceback (most recent call last):
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow.py", line 41, in <module>
    from tensorflow.python.pywrap_tensorflow_internal import *
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 21, in <module>
    _pywrap_tensorflow_internal = swig_import_helper()
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 20, in swig_import_helper
    return importlib.import_module('_pywrap_tensorflow_internal')
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
    ImportError: No module named '_pywrap_tensorflow_internal'


    During handling of the above exception, another exception occurred:


    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\__init__.py", line 24, in <module>
    from tensorflow.python import *
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\__init__.py", line 51, in <module>
    from tensorflow.python import pywrap_tensorflow
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow.py", line 52, in <module>
    raise ImportError(msg)
    ImportError: Traceback (most recent call last):
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 18, in swig_import_helper
    return importlib.import_module(mname)
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
    File "<frozen importlib._bootstrap>", line 986, in _gcd_import
    File "<frozen importlib._bootstrap>", line 969, in _find_and_load
    File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
    File "<frozen importlib._bootstrap>", line 666, in _load_unlocked
    File "<frozen importlib._bootstrap>", line 577, in module_from_spec
    File "<frozen importlib._bootstrap_external>", line 906, in create_module
    File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
    ImportError: DLL load failed: 找不到指定的模块。


    During handling of the above exception, another exception occurred:


    Traceback (most recent call last):
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow.py", line 41, in <module>
    from tensorflow.python.pywrap_tensorflow_internal import *
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 21, in <module>
    _pywrap_tensorflow_internal = swig_import_helper()
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 20, in swig_import_helper
    return importlib.import_module('_pywrap_tensorflow_internal')
    File "C:\Users\toy\AppData\Local\Programs\Python\Python35\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
    ImportError: No module named '_pywrap_tensorflow_internal'

    Failed to load the native TensorFlow runtime.

    See https://www.tensorflow.org/install/install_sources#common_installation_problems

    for some common reasons and solutions. Include the entire stack trace
    above this error message when asking for help.
  2. 安装 vs2019即可:
  3. 选中:
    1. 通用 Windows 平台开发
    2. .NET 桌面开发
    3. ASP.NET 和 Web开发

4. 检测是不是用的 GPU:

  1. tf.test.is_gpu_available()
  2. 如下:
1
2
3
from tensorflow.python.client import device_lib

print(device_lib.list_local_devices())

参考资料

参考视频 1kc2

安装文件 qlgj

Ubuntu环境

安装 CUDA

  1. 查看显卡信息

    1
    $ nvidia-smi

    会得到以下信息:

    其中:

    版本
    显卡驱动 450.51.05
    cuda 11.0

    Note:

    1. 若没有安装过NVIDIA,那么 nvidia-smi 会报错,可直接安装cuda,因为cuda会自动安装显卡驱动
    2. 若安装完cuda, nvidia-smi 仍报错,那么重启就可以了。
  2. 下载cuda [点击这里下载]

    注意:选择和自己系统相匹配的(在下安装的是11.0)

    网站会给出相应的安装命令,如下:

    1
    2
    3
    4
    5
    6
    7
    wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-ubuntu1804.pin
    sudo mv cuda-ubuntu1804.pin /etc/apt/preferences.d/cuda-repository-pin-600
    wget http://developer.download.nvidia.com/compute/cuda/11.0.2/local_installers/cuda-repo-ubuntu1804-11-0-local_11.0.2-450.51.05-1_amd64.deb
    sudo dpkg -i cuda-repo-ubuntu1804-11-0-local_11.0.2-450.51.05-1_amd64.deb
    sudo apt-key add /var/cuda-repo-ubuntu1804-11-0-local/7fa2af80.pub
    sudo apt-get update
    sudo apt-get -y install cuda

    Note: 若 wget http://developer.download.nvidia.com/compute/cuda/11.0.2/local_installers/cuda-repo-ubuntu1804-11-0-local_11.0.2-450.51.05-1_amd64.deb 下载太慢,那么用浏览器打开 wget 后面的网址即可浏览器下载。

cudnn

  1. 添加 cudnn 环境变量

    1. [cuDNN下载地址]

      Note: 一定要下载与cuda相对应的版本,如:cuDNN Library for Linux (x86_64)

    2. 配置环境变量

      1. .bashrc 添加如下:

        1
        2
        export PATH="/usr/local/<cuda-11.0>/bin/:$PATH"
        export LD_LIBRARY_PATH="/home/<username>/cuda/lib64:$LD_LIBRARY_PATH" # cudnn解压的路径
  2. 查看cuda和cudnn是否安装成功

    1
    nvcc -V

    如果安装成功会得到以下信息:

    1
    2
    3
    4
    5
    nvcc: NVIDIA (R) Cuda compiler driver
    Copyright (c) 2005-2020 NVIDIA Corporation
    Built on Thu_Jun_11_22:26:38_PDT_2020
    Cuda compilation tools, release 11.0, V11.0.194
    Build cuda_11.0_bu.TC445_37.28540450_0
  3. 安装 tensorflow
    点击这里查看github教程

    PS: 最好使用科学上网

  4. 检测 tensorflow-gpu 是否安装成功

    1
    2
    3
    import tensorflow as tf

    print(tf.test.is_gpu_available())

Linux多版本CUDA安装/多版本切换

本机系统信息

CUDA版本和显卡驱动

1
nvidia-smi

目前CUDA版本为11.6,如下图所示,显卡驱动为510.47.03(根据下面的对照表,降级成CUDA10.2没问题)

ubuntu系统版本和架构

1
2
$ uname
$ lsb_release -a

CUDA版本和驱动版本的对照表

[Table 3 CUDA Toolkit and Corresponding Driver Versions]

如上图所示,像CUDA10.2版本,需要440.33以上的显卡驱动。例如,如果你通过nvidia-smi命令获得的显卡驱动号大于440.33,你可以将CUDA 11降到10.2是完全没有问题的。而如果你的显卡驱动号是396.26,那么你只能在CUDA7/8/9上进行切换。

因此显卡驱动总是越高越好,依赖关系为:

  • 操作系统版本最优先
  • 显卡驱动次之
  • CUDA再次之+Cudnn
  • 最后是python库,如pytorch版本

下载CUDA

下载网址:[https://developer.nvidia.com/cuda-toolkit-archive]

运行如下wget命令在服务器安装cuda的目录下:

将下载好的文件执行以下操作:

  • 进入到该目录下,使用chmod 755 cuda_10.2.89_440.33.01_linux.run更改文件的执行权限。
  • 不是管理员用户,因此无法使用sudo安装,直接执行以下命令即可 sh cuda_10.2.89_440.33.01_linux.run

安装CUDA

  1. 输入accept接收协议
  2. 选择***只安装CUDA Toolkit***,选择Option回车进入
  3. 修改安装路径

前提:如下使用的两个目录需要先创建

  • /home/<username>/cuda-10.2/
  • /home/<username>/cuda-10.2/mylib/

PS: 这两个目录可以随便换

先选择Toolkit Options回车进入

选择Change Toolkit Install Path回车,输入自定义的CUDA安装目录:

1
/home/<username>/cuda-10.2/
更改`Library install path`
1
/home/<username>/cuda-10.2/mylib

安装成功

此时nvcc还是显示原来的CUDA版本,还需要修改本地文件,如下:

修改环境配置文件

直接使用源文件夹

修改命令相关参数,执行如下命令:

1
vim ~/.bashrc

在最下方添加刚刚安装cuda的路径:

1
2
export PATH="/home/<username>/<cuda-10.2>/bin:$PATH"
export LD_LIBRARY_PATH="/home/<username>/cuda-10.2/lib64:/home/<username>/<cuda-10.2>/mylib/lib64:$LD_LIBRARY_PATH"

之前的CUDA路径可以注释掉,方便之后使用

保存之后,执行以下命令,使配置生效:

1
source~/.bashrc

查看效果:nvcc -V

显示CUDA 10.2,已经安装成功

ln映射文件夹

1
$ ln -s /home/<username>/<cuda-10.2> /usr/local/cuda

PS: /usr/local/cuda 目录可以换

修改命令相关参数,执行如下命令:

1
vim ~/.bashrc

在最下方添加刚刚安装cuda的路径:

1
2
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64:/usr/local/cuda/mylib/lib64:$LD_LIBRARY_PATH"

参考链接

Anaconda创建的虚拟环境多用户共享访问使用

在服务器上的sudo用户安装了anaconda,假设现在配置了虚拟的环境pytorch,需要怎么设置配置文件,才能保证连接服务器的别的标准用户(不具有sudo权限)怎么可以用到这个环境.

软件下载

建议使用国内源,例如清华大学开源软件镜像站。下载对应架构的安装包,例如 Anaconda3-2022.05-Linux-x86_64.sh。注意下载的是 anaconda3。下面假定你在 root 用户中执行指令,并且之前没有安装过 anaconda。

软件安装

  1. 运行下载的文件

    通过 chmod +x 为它增加执行权限

  2. 回车开始安装

  3. 阅读并输入 yes

  4. 指定安装目录,例如 /opt/anaconda3. (不要放在 root 根目录(~)下。)

  5. 等待安装完成

用户组与目录权限

为了使 anaconda 的文件仍归 root 所有,我们将新建一个用户组来为其余用户提供访问权限

1
2
3
4
5
6
groupadd condagroup # 新建一个名为 condagroup 的组;可以使用其它名称
adduser <username> condagroup # 将需要的用户加入该组
chgrp -R condagroup /opt/anaconda3 # 将安装目录转给该组
chmod 770 -R /opt/anaconda3 # 设置 root 用户与 condagroup 组的读写权限
find /opt/anaconda3 -type d -exec chmod g+s {} + # 设置组继承,使以后新建的文件夹仍属于 condagroup 组
chmod g-w /opt/anaconda3/envs # 共享环境只能由 root 修改

新环境位置

接下来,为了确保其余用户可以正确地在自己的目录中使用 -n <name> 新建环境,还要创建系统级的 anaconda 配置文件。在安装目录下(/opt/anaconda3)新建.condarc文件并写入

1
2
3
envs_dirs:
- /opt/anaconda3/envs
- ~/.conda/envs

根据 Conda configuration,envs_dirs是搜索命名环境的目录列表。创建新的命名环境时将放置在第一个可写位置,因此 root 用户将默认创建在安装目录下,成为共享环境,而其余用户会创建在自己的主目录中。

关于更多的管理选项,参见 Administering a multi-user conda installation。

你可能会想把这个文件的权限设为 644来避免被其余用户改动。这也是默认行为,因为 root 用户的默认 umask 是 0022。我们接下来将介绍这个 umask 可能带来的问题。

关于 umask

umask 控制新建文件的权限,简单来说是对指定的权限位进行排除。详细信息参见它的维基百科。

前述文件共享的机制是通过组读写权限完成的,意味着 anaconda 的文件需要有用户组的读与写权限。普通用户的默认 umask 是 0002,提供了用户组写仅限,但 root 用户的0022并没有。因此在使用 root 用户创建共享环境前需要修改 umask。

1
2
3
umask 0002
conda create -n env_name python # 示例
umask 0022 # 你可能会想恢复原值

否则会导致其它用户无权访问某些新下载的包的缓存等问题。在这种情况下,可以找到相应的文件并为其附加用户组写权限chmod g+w。

新增用户

现在你已经安装好了想要的 anaconda。如果有新用户,只需将其加入condagroup用户组,即可使用共享环境或创建新环境。

conda 环境配置

方法1: 局部(用户)配置 (Recommend)

以普通用户执行如下操作, 但是需要在所有的权限设置完毕之后方可使用

  1. 运行命令 eval "$(/opt/anaconda3/bin/conda shell.<YOUR_SHELL_NAME> hook)"

    如: eval "$(/opt/anaconda3/bin/conda shell.zsh hook)"

  2. 运行命令 conda initconda init <YOUR_SHELL_NAME>

    注: conda init 好像只会修改 .bashrc 文件

  3. 设置关闭自启动base环境

    1
    conda config --set auto_activate_base false

    或者修改文件 ~/.condarc

    1
    auto_activate_base: false

方法2: 全局(root)配置 (Not recommend)

以root用户进行如下操作

  1. 使用vim打开 /etc/profile, 在末尾添加

    1
    export PATH=/opt/anaconda3/bin:$PATH

    目的是为了让各用户的终端都能找到conda并运行。如果你安装在其它位置,请对应地更改目录值(下同)

  2. 运行 source /etc/profile 应用这项更改

方法3: 直接配置.bashrc或.zshrc

  1. vim ~/.bashrc

  2. 在最后一行添加:

    1
    export PATH="/opt/anaconda3/bin:$PATH"
  3. 用来激活环境变量

    1
    source ~/.bashrc
  4. 激活:

    • source activate
    • conda activate
  5. 退出激活:

    • source deactivate
    • conda deactivate

常见问题

Q: conda create 创建的目录不在 ~/.conda/envs

A: 删除 /opt/anaconda3/envs/.conda_envs_dir_test 文件

参考:

激活环境时设置环境变量

假设环境名为 env_name, 在env_name环境目录中, 创建文件夹 etc/conda/activate.d。在该文件夹下,创建 *.sh (linux/macos) 或 *.bat (windows) 文件, 将需设置的环境变量或脚本写入其中, 并通过 chmod +x *.sh 添加可执行权限. 当激活虚拟环境时, 该脚本自动运行.

分步骤如下:

  1. 在你的虚拟环境的 etc/conda/activate.d/ 目录下创建一个脚本文件

    1
    2
    3
    conda activate env_name
    mkdir -p $CONDA_PREFIX/etc/conda/activate.d
    touch $CONDA_PREFIX/etc/conda/activate.d/env_vars.sh
  2. 编辑env_vars.sh文件,添加你需要的环境变量:

    1
    2
    3
    4
    #!/bin/sh

    export MY_VAR="my_value"
    export ANOTHER_VAR="another_value"
  3. env_vars.sh 添加可执行权限:

    1
    chmod +x $CONDA_PREFIX/etc/conda/activate.d/env_vars.sh

QAs

  1. UnicodeEncodeError: ‘ascii’ codec can’t encode character ‘\xe4’ in position 89: ordinal not in range(128)

    • 原因: 字符编码问题

    • 解决方案:

      • 方法1: 临时生效

        1. 运行命令

          1
          2
          export LANG="en_US.UTF-8"
          export LC_ALL="en_US.UTF-8"
        2. 再执行你需要执行的命令即可

      • 方法2: 当前用户永久生效

        有时 Linux 系统中编码并不能统一使用,而是只针对某用户下才使用该编码,即当使用 FineBI 的时候,在该系统用户下才能使用该编码。因此编辑配置文件时需要在该用户下编辑。

        1. 打开 ~/.bash_profile

          1
          vim ~/.bash_profile
        2. 在最后一行后面追加:

          1
          2
          export LANG="en_US.UTF-8"
          export LC_ALL="en_US.UTF-8"
        3. 重启终端或运行 source ~/.bash_profile

        注:~/.bash_profile 是每个用户都可使用该文件输入专用于自己使用的 shell 信息。

      • 方法3: 系统级对所有用户永久有效

        对整个系统都有效的修改方式,使整个系统都适应于该系统编码。该方法是在系统配置文件中添加编码方式将默认的方式覆盖掉。执行的命令如下:

        1. 打开 profile

          1
          vim /etc/profile
        2. 打开文件后在最后一行后面追加:

          1
          2
          export LANG="en_US.UTF-8"
          export LC_ALL="en_US.UTF-8"
        3. 重启终端或运行 source /etc/profile

      • 方法4: 使用 locale-gen/etc/locale.conf

        当需要生成并设置系统的默认语言环境时使用。

        1. 编辑 /etc/locale.gen,去掉 en_US.UTF-8 UTF-8 以及 zh_CN.UTF-8 UTF-8 行前的注释符号(#):

          1
          vim /etc/locale.gen
        2. 然后使用如下命令生成 locale:

          1
          locale-gen
        3. /etc/locale.conf 输入内容:

          1
          echo 'LANG=en_US.UTF-8'  > /etc/locale.conf

重点

如果要改变变量的值就用指针,不改变就用变量本身;使用变量本身,那么相当于读取,对原来的数据没有任何影响。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <iostream>

using namespace std;

typedef struct Node {
int data;
struct Node *next;
}Node;

typedef struct Node *LinkList;

void initLinkList(LinkList *L)
{
(*L) = new Node;
(*L)->data = 0;
(*L)->next = NULL;
}

bool insertLinkList(LinkList head, int pos, int value)
{
LinkList p = head, t;
int j = 1;

while (p && j < pos) {
p = p->next;
j++;
}

if (!(p) || j > pos) return false;

t = new Node;
t->data = value;
t->next = p->next;
p->next = t;
head->data++;

return true;
}


bool deleteLinkList(LinkList l, int pos, int *value)
{
LinkList p = l, q;
int j = 1;

while (p->next && j < pos) {
p = p->next;
j++;
}

if (!(p->next) || j > pos) return false;

q = p->next;
p->next = q->next;
delete(q);
l->data--;
return true;
}

void clearLinkList(LinkList l)
{
LinkList p = l->next, q;
l->next = NULL;
while (p) {
q = p->next;
delete (p);
p = q;
l->data--;
}
}

void showLinkList(LinkList l)
{
cout << "Length is " << l->data << endl;
l = l->next;
while (l) {
cout << l->data << "\t";
l = l->next;
}
cout << endl;

return ;
}

int main()
{
LinkList head = NULL;
initLinkList(&head);

int value_del;
deleteLinkList(head, 1, &value_del);

for (int i = 1; i <= 10; i++) {
insertLinkList(head, i, i);
}
insertLinkList(head, 4, 10);
showLinkList(head);

deleteLinkList(head, 4, &value_del);
showLinkList(head);

clearLinkList(head);
showLinkList(head);

return 0;
}

详解

  1. 该链表是有头结点的链表;
  2. 什么时候用 LinkList LLinkList *L
    1. 需要改变指针的值的时候用 LinkList *L,若对指针的值没有改变时,使用 LinkList L
  3. 链表注意事项:
    1. LinkList head;只是定义了一个 变量名为head的指针变量,是一个野指针,那么需要在定义的时候进行初始化,即:LinkList head = NULL
    2. 想要使用 head 指向头结点,需要自己 new 一个结构体,将这个结构体的地址赋给 head
  4. insertLinkListdeleteLinkList的异同:
    1. insertLinkListdeleteLinkList不论在位置 pos 处插入一个结点还是在pos处删除结点,都是对 pos-1 进行操作。
    2. 在使用 while 循环时,insertLinkList是以第j个位置为判断对象,而 deleteLinkList是以第j+1个位置为判断对象的(其中:头结点为第1个位置)。

字体下载

推荐使用 JetBrainsMono Nerd Font

网站

Nerd Fonts

单独字体

  • JetBrainsMono Nerd Font 下载方式(3选1):

  • Hack Nerd Font Github

  • Caskaydia Cove Nerd Font Github

  • MesloLGS NF Github(官网)

    1
    2
    3
    4
    5
    mkdir fonts
    curl -o ./fonts/MesloLGS\ NF\ Regular.ttf https://raw.githubusercontent.com/romkatv/powerlevel10k-media/master/MesloLGS%20NF%20Regular.ttf
    curl -o ./fonts/MesloLGS\ NF\ Bold.ttf https://raw.githubusercontent.com/romkatv/powerlevel10k-media/master/MesloLGS%20NF%20Bold.ttf
    curl -o ./fonts/MesloLGS\ NF\ Italic.ttf https://raw.githubusercontent.com/romkatv/powerlevel10k-media/master/MesloLGS%20NF%20Italic.ttf
    curl -o ./fonts/MesloLGS\ NF\ Bold\ Italic.ttf https://raw.githubusercontent.com/romkatv/powerlevel10k-media/master/MesloLGS%20NF%20Bold%20Italic.ttf

Ubuntu安装win10字体

  1. 进入windows : /media/qeuro/系统/Windows/Fonts

  2. sudo cp *.ttf ~/桌面/WinFonts

  3. sudo cp *.TTF ~/桌面/WinFonts

  4. cd ~/桌面

  5. sudo mv WinFonts /usr/share/fonts/

  6. sudo chmod -R 755 /usr/share/fonts/WinFonts

  7. sudo mkfontscale && sudo mkfontdir && sudo fc-cache -fv

  8. 字体名称对照:

    ttf 字体
    arial.ttf Arial
    couri.ttf Courier New
    times.ttf Times New Roman
    timesbd.ttf Times New Roman Bole
    FZSTK.TTF 方正舒体
    FZYTK.TTF 方正姚体
    msyh.ttf 微软雅黑
    msyhbd.ttf 微软雅黑Bold
    simfang.ttf 仿宋GB_2312
    simhei.ttf 黑体
    simkai.ttf 楷体GB_2312
    SIMLI.TTF 隶书
    simsun.ttc 宋体、宋体PUA、新宋体
    SIMYOU.TTF 幼圆
    STCAIYUN.TTF 华文彩云
    STFANGSO.TTF 华文仿宋
    STHUPO.TTF 华文琥珀
    STKAITI.TTF 华文楷体
    STLITI.TTF 华文隶书
    STSONG.TTF 华文宋体
    STXIHEI.TTF 华文细黑
    STXINGKA.TTF 华文行楷
    STXINWEI.TTF 华文新魏
    STZHONGS.TTF 华文中宋
    SURSONG.TTF 宋体-方正超大字符集

  1. Cupertino:图标 —— icons

  2. dash-to-dock-gnome:dash to dock

  3. grub2-themes-master:grub启动界面 —— sudo ./install.sh -v

  4. High_Ubunterra_CC : Ubuntu 锁屏启动界面(模糊的那个)

  5. Mojave-light: 主题 —— themes

  6. suadesplas: 启动动画

  7. 美化网址有去除启动时紫框的说明是:https://www.cnblogs.com/feipeng8848/p/8970556.html

    即:找到/boot/grub/grub.cfg文件,找到这样一行: if background_color 44,0,30,0;修改成 if background_color 0,0,0,0;就会去除grub在选中Ubuntu系统之后出现的短暂的紫色。

  8. 美化网址:

    1. https://www.cnblogs.com/feipeng8848/p/8970556.html
    2. https://blog.csdn.net/weixin_40389121/article/details/81703577
    3. https://www.cnblogs.com/lishanlei/p/9090404.html
0%