使用WSL+VSCode快速C++入门

使用WSL+VSCode快速C++入门

  • 使用WSL+VSCode快速C++入门
    • 0. 准备工作
      • 0) 系统要求 Windows 10 1803以上
      • 1) 安装WSL(Windows Subsystem Linux)
      • 2) 安装VSCode
      • 3) 将VSCode默认的终端配置为WSL的bash终端
      • 4) 在终端中安装构建工具
    • 1. 第一个源文件
      • 1) 基本代码
      • 2) 第一行代码 Hello, C++
      • 3) 我要开始做加减乘除啦
      • 4) 还有那些类型的数据都一块说了呗
      • 5) 上面类型可以相互转换吗?
      • 6) 多个相同类型一起用怎么办?
      • 7) 我自己写函数
      • 8) 交换一下
      • 9) 枚举
      • 10) 结构体
    • 后续待完善

使用WSL+VSCode快速C++入门


0. 准备工作

0) 系统要求 Windows 10 1803以上

1) 安装WSL(Windows Subsystem Linux)

  • 按下Win + X, 在弹出的菜单中选择应用和功能
  • 在弹出的窗口中的右侧找到相关设置,点击 “程序和功能”
  • 点击新的弹窗左侧 “启用或关闭Windows功能”
  • 勾选 “适用于Windows的Linux子系统”, 点击确定等待安装功能
  • 打开应用商店, 搜索Ubuntu, 找到Ubuntu 16.04 (或者更新) 点击安装, 安装完成后根据提示可以固定到开始菜单(方便后续使用), 然后重启系统
  • 重启系统后, 打开Ubuntu应用, 等待安装完成, 中途会提示输入要设置的子系统用户名和密码, 根据个人喜好设置就行

2) 安装VSCode

  • 进入官网下载VSCode的安装程序进行安装即可 点击下载

3) 将VSCode默认的终端配置为WSL的bash终端

  • 打开VSCode
  • 依次点击菜单项 “文件” - “首选项” - “设置”, 打开用户设置文件
  • 在右侧的用户设置(settings.json)中添加下面配置
{
    "terminal.external.windowsExec": "C:\\WINDOWS\\System32\\bash.exe",
    "terminal.integrated.shell.windows": "C:\\WINDOWS\\System32\\bash.exe"
}

4) 在终端中安装构建工具

  • 重启VSCode后, 打开终端 (快捷键 Ctrl + Shift + `)
  • 运行下面的终端命令安装C++的编译构建工具
$ sudo apt install build-essential vim git cmake make
  • 安装完成后关闭终端, 创建或打开一个工作目录, 再次打开终端(此时终端会自动定位到工作目录)

至此我们的准备工作才算完成了

1. 第一个源文件

1) 基本代码

  • 我们新建一个源文件 main.cc (后缀名可以是 *.cc *.cxx *.cpp)
  • 接着我们输入一些基础代码来编译生成一个可执行文件

int main(int, char **)
{
    return 0;
}

  • 打开终端工具, 输入下面的命令编译生成一个可执行程序
$ g++ main.cc
  • 执行生成的可执行文件a.out
$ ./a.out
  • 使用自定义的可执行文件名称重新编译
$ g++ main.cc -o main # 使用-o 选项指定成的可执行文件名, 这里用的是main
  • 执行自定义名称的可执行
$ ./main

以上的终端命令一般情况下都是没有任何输出打印的内容的,如果打印了一些报错信息, 请检查main.cc中的代码是否有拼写错误以及中英文符号错误

如果我们希望在代码中加入一些打印文本的代码, 下一步该怎么做呢?

别急, 慢慢来

2) 第一行代码 Hello, C++

两种方案:

  • 面向过程
  • 面向对象

面向过程:

  • 我们需要一个面向过程的方法(函数)来打印我们的文本, 是哪个函数呢?
    最常用的当然是 printf()函数了, 赶紧写上:

int main(int, char**)
{
    printf("Hello, C++");
    return 0;
}

赶紧编译试试? 不好, 报错了,提示找不到printf的声明…尴尬!!!

  • 怎么才能使用这个函数呢?

    自己声明一个或者…引入现成的呗

    怎么引入呢?

    在文件的上面加一句文件包含就好啦 #include <文件名> 或者 #include “文件名”

    要引入哪个头文件啊?

    printf函数是标准C库里的函数呀, 当然得包含标准C库头文件了 #include

赶紧试试

#include 

int main(int, char**)
{
    printf("Hello, C++");
    return 0;
}

编译,执行, 一气呵成, 哇, 有输出了啊

面向对象:

  • 如果把标准输出当成一个对象的话,是哪个对象呢?

标准输出有两个, 一个是正常输出, 一个是错误输出, 分别对应

std::cout 和std::cerr

  • 怎么使用这个对象呢?

包含头文件

#include 

引入命名空间std, 然后使用

using namespace std;

当然也可以不在全局引入命名空间std

虽然你可以使用using的方式引入整个命名空间, 但是个人建议最好别这么干?

为什么呢?

你想啊, 要是你写了一个跟标准库里面你还没用过的同样名字的函数, 你该怎么使用?

你要真想偷点懒啊, 你改成

// using namespace std; 
using std::cout;

这样你就之引入了std::cout这一个对象了, 其他的就不收受影响了

我建议啊, 你最好直接使用 std::cout, 虽然多写了一些代码, 但是这样写好处更多啊, 比如不用担心自己写的函数和标准库同名, 多个命名空间都在使用时, 可以很清楚的知道使用的时哪个命名空间里的了

那试试:

#include 

int main(int, char **)
{
    std::cout << "Hello, C++" << std::endl;
    return 0;
}
  • std::endl是什么呀?

和std::cout一样, 标准库中的一个对象, 区别是功能不一样

什么功能呀?

换行 + 刷新缓冲区, 相当于 \n + std::flush

std::flush是啥?

也是一个对象啊, 用来刷新缓冲区

  • 编译运行看看
$ g++ main.cc -o main
$ ./main
Hello, C++

哈哈,终于有自己写的输出文本了

3) 我要开始做加减乘除啦

  • 做加法
#include 

int main(int, char**)
{
    int sum = 1 + 2;
    std::cout << sum  << std::endl;
    return 0;
}
$ g++ main.cc -o add
$ ./add
3
  • 做减法
#include 

int main(int, char**)
{
    int sum = 10 - 5;
    std::cout << sum  << std::endl;
    return 0;
}
$ g++ main.cc -o minus
$ ./minus
5
  • 做乘法
#include 

int main(int, char**)
{
    int a = 4 * 8;
    std::cout << a  << std::endl;
    return 0;
}
$ g++ main.cc -o times
$ ./times
32
  • 做除法
#include 

int main(int, char**)
{
    int a = 10 / 2;
    std::cout << a  << std::endl;
    return 0;
}
$ g++ main.cc -o divide
$ ./divide
5
  • 先乘除后加减
#include 

int main(int, char**)
{
    int a = 11 + 2 * 6 - 24 / 6;
    std::cout << a  << std::endl;
    return 0;
}
$ g++ main.cc -o main
$ ./main
19
  • 有括号先算括号
#include 

int main(int, char**)
{
    int a = (1 + 2) * (9 - 4) - (4 + 23) / 3;
    std::cout << a  << std::endl;
    return 0;
}
$ g++ main.cc -o main
$ ./main
6
  • 怎么都是整数啊?不能整除的怎么办?

被除数 / 除数 = 商 + 余数, 商保留, 余数不要了

  • 那怎么用小数不用商和余数啊?

用浮点数呗, 就是数学意义上的实数, float, double

#include 

int main(int, char **)
{
    float a = 13.5f / 3;  
    std::cout << a << std::endl;
    
    double b = 25.25 * 10;
    std::cout << b << endl;
    return 0;
}
$ g++ main.cc -o main
$ ./main
4.5
252.5
  • float和double有什么区别?

从含义上讲没区别, 区别就在这两个类型占用内存大小不一样,所以表示的实数精度也不一样, float 有32位二进制, double 有64位二进制, 二进制位数越多, 能表示的数越多, 精度也越高

4) 还有那些类型的数据都一块说了呗

  • 整型

    (unsigned) char -> 8位, 1字节

    (unsigned) int -> 32位, 4字节

    (unsigned) short -> 16位, 2字节

    (unsigned) long -> 跟编译器有关 不低于32位, 不高于64位

    (unsigned) long long -> 64位, 8字节

  • 浮点型

    float double

  • 布尔型 bool -> 表示真或假, 只有两个值 true: 表示真, false: 表示假

  • 字符型
    char, (unsigned ) char -> char 一般表示ASCII字符, 表示我们在键盘上所有能看到的符号(英文,数字,各种标点符号)的二进制表示

unsigned表示没有正负, signed表示有正负, 不写的情况下, 默认用的时signed

5) 上面类型可以相互转换吗?

当然可以了, 分为隐式转换(自动转换)和显示转换(强制转换)

隐式转换(自动转换):

  • 转布尔型

    0: false, 非0: true

  • 转整型

    有符号同字节数类型转无符号类型, 如 int -> unsigned int
    较小字节数转较大字节数同符号性类型, 如 short -> int
    浮点型转整形, 小数位舍去, 溢出位舍去 如 (float) 355.5f -> (unsigned char) 99

  • 转浮点型
    整数转浮点

6) 多个相同类型一起用怎么办?

使用数组

  • 定义方式:
    类型 数组名[相同类型的个数];
    或者
    类型 数组名[相同类型的个数] = {多个初始值};

如:

int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  • 怎么访问数组的某个序号的值

取出值:

int a = array[5]; // 常用方式 取出数组array的第6个值, 序号永远从0开始表示第一个
int b = *(array + 2); // 指针方式取出array的第3个值(高级用法, 指针部分详解)

修改值:
数组名[序号] = 某个值

array[7] = 70; // 修改数组array的第8个数为70
*(array + 6) = 66; // 修改array的第7个数为66(高级用法, 指针部分详解)
  • 序号的范围, 从0 到 定义数组时的个数 - 1
    如:
int An[3] = {1, 2, 3}; //序号从0 ~ 2
double Bn[] = {1.0, 1.1, 1.2, 1.3}; //自动识别出个数4, 序号从0 ~ 3
  • 序号, 也称下标, 索引

7) 我自己写函数

前面用了一个printf函数用来打印, 但是打印的时候都要用\n才能换行, 我想自己写一个函数,能自动换行, 可以不?

当然可以

  • 先声明一下
void my_printf(??);

我要输入文本做参数怎么写参数啊?

文本其实就是多个字符组成的字符数组啊, 只不过系统系统为我们增加了一个结束字符 ‘\0’, 也就是二进制的0, 这个值不代表任何字符, 所以用来当结束标记正好

"Hello, C++"这段文本怎么表示啊

const char name[11]; // 为啥 11?

H e l l o , 空格 C + + \0 

正好十一个

const 是干嘛的?

修饰 name这个字符型数组内容不能改呀

试试就知道什么意思啦

"Hello, C++" [6] = 'A'; //编译报错
const char name[11];
name[2] = 'B'; // 表一报错啊

要是需要修改呢?

那就别用const 修饰好呗

char hello[11] = "Hello, C++";

hello[0] = 'h'; //编译通过, hello 的文本内容变成了 "hello, C++";
  • 继续声明
void my_printf(const char text[]);
  • 定义函数
void my_printf(const char text[]) 
{
    printf(text);
    printf("\n");
}

试试看

#include 

void my_printf(const char text[]);
int main(int,char**)
{

    my_printf("Hello");
    my_printf("C++");
    return 0;
}
void my_printf(const char text[]) 
{
    printf(text);
    printf("\n");
}

$ g++ main.cc -o main
$ ./main
Hello
C++

8) 交换一下

我有两个数, 都是int , 现在我要交换这两个数的值怎么做? 我这样为什么不行啊?


void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
}

//...

int a = 5;
int b = 6;

swap(a, b);

// 怎么a和b没交换成功啊

在C++的函数参数中, 都是以值的方式进行传递的, 上面写的参数地的都是a 和 b的值, 也就是把这两个数的值做了一下拷贝到函数内, 函数内无论怎么修改都是修改拷贝出来的值, 所以才无法真正交换呀

哦哦, 那怎么才能交换啊

我两个篮子里分别放了1个苹果和1个桃儿, 现在然你交换,我应该把什么给你你才能交换呢?

A. 苹果和桃儿的照片
B. 两个篮子

那肯定是篮子啊

同样的道理

a 和 b分别存了5 和 6, 要交换a 和 b的两个值, 那肯定就得把a 和 b的内存位置通过参数传给函数嘛, 然后函数里面通过直接操作内存内容交换不就把a 和 b交换过来了嘛

那怎么知道a和b的内存位置啊?

取内存地址呗, &符号了解一下, biu的一下就把内存地址取出来了

地址 ad = &a;

地址怎么表示啊?

你取出来的是什么类型变量的地址呀

a是int 类型, int 类型的地址什么类型啊

int * 表示int 地址类型, 我们称它int指针类型, 你要喜欢叫地址类型还是指针类型都随你咯

哦哦, 那是不是这样表示

int * ad = &a;

或者这样

int *ad;
ad = &a;

很聪明, 两种都可以

哦哦, 那我可不可以偷懒一点一次把地址类型都定义好
像这样

int* ad,bd; 

你这不行啊,你这分明是定义一个int *ad和一个int bd嘛

啊? 我把*写的离int 近一些都不行吗?

不行不行, 你这一行里有几个*才能表示几个指针啊, 再懒也不能一个*表示多个指针啊

你要真想偷懒, 你可以把int *这个指针类型重新起个名字

怎么起啊?

typedef 或者using, 看你喜欢了


typedef int * intptr;

using int_pointer = int *;

哦哦, 这么神奇啊, 也就是说, 如果 直接用指针类型定义变量, 一行里面要定义几个指针就必须几个*对吧, 不加*的就是原始类型不是指针类型了

int *a, *b; 

那为了用*表示一个指针, 我还是把 *靠近指针变量名比较好, 这样不会看错

指针定义了, 地址也取好了, 怎么把地址的值取出来啊

用*啊

int *ad = &a;

*ad = 10; // 把ad指针指向的内存内容改成10 

@_@晕啊, 怎么也是用*啊

你这么理解: 定义的时候, *用来定义指针, 使用的时候*用来取指针指向内存的内容,&符号呢,你就用来取地址好了

那我可不可以取完地址直接用*来赋值啊
像这样

int a = 100;

*(&a) = 101;

完全可以啊, 而且还经常用

哦哦, 那我知道交换怎么写了

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

int a = 10;
int b = 20;

int *ad = &a;
int *bd = &b;
swap(ad, bd); 

// 或者直接不用中间的指针变量了
swap(&a, &b);
  • 不用指针的交换

除了使用变量本身, 我还可以直接操作标量的内存吗?

可以呀, 你用引用给变量起个新名字就好了

int a = 100;

int& a_a = a;

a_a = 101; // a也变成了101

用别名去访问也能修改内存内容
但是, 取别名的时候要注意了, 定义的时候就得告诉这个别名到底是谁的


int a = 100;

int& aa; // 错误, 必须起名的同时指定是谁的别名

aa = a; 

别名又叫引用, 定义应用的时候, 必须有原始变量来初始化, 不然不知道给谁其别名了

引用也可以做参数呢, 既然是参数, 那就说明不确定是谁的别名了, 传进来的是谁, 那就是谁的别名(所以就没有必要初始化了), 哈哈,够任性吧

void change(int &a, int new_value)
{
    a = new_value;
}

int a = 100;

change(a, 102); // a = 102;

那是不是前面的交换也可以用引用来实现, 比如这样

void swap(int& a, int& b)
{
    int temp = a;
    a = b;
    b = temp;
}

机智如你, 还能举一反三了, 不错不错

9) 枚举

好纠结啊, 我想用一个变量表示水果, 可是水果就只有目前的几种, 苹果, 梨, 桃儿, 香蕉, 可是我要是用整数表示改用哪些呢?

而且我明明上面用了0表示苹果, 写到下面的时候又给忘了, 还记错成了2,有没有好的解决办法啊

  • 你试试枚举
enum fruit {
    apple, orange, banana, peach, pear
};

fruit one = apple;

这总记得住吧

嗯嗯, 记得住, 可是这些名字都用的什么数啊?

你给枚举定义的时候什么数都不写,那就从0开始, 依次递增, 你写了, 那就按你写的来, 后面没写的就从你写的那个值的下一个开始依次递增

比如上面的 apple = 0, orange = 1, banana = 2, peach = 3, pear = 4

但是如果你改成

enum fruit {
    apple, orange = 3, banana, peach = 1, pear
};

那 apple = 0, orange = 3, banana = 4, peach = 1, pear = 2 了

10) 结构体

老师让我统计学生信息, 我都再Excel表格了, 可是每个学生信息都好多啊
姓名, 学号, 性别, 班级, 年龄等等, 我用一个变量没法表示一个学生啊

  • 你试试结构体
enum Gender {
    male, female  
};
struct Student
{
    char name[16];
    int number;
    enum Gender gender;
    int age;
    // ...
};

Student xiaoming;

怎么分别修改学生的信息啊, 比如年龄, 去年到今年, 年龄都得+1岁

.操作符啊, 你可以理解为 “的”

比如

xiaoming.age = xiaoming.age + 1; // 小明的年龄+1岁

你要是懒, 还能写成

xiaoming.age++;// 或者 ++xiaoming.age;

区别嘛,单独使用几乎没啥区别, 如果是类似这种

int age = xiaoming.age++;
// 和 int age = ++xiaoming.age;

你这么理解

int age = xiaoming.age++:
int age = xiaoming.age; xiaoming.age = xiaoming.age + 1;

int age = ++xiaoming.age:
xiaoming.age = xiaoming.age + 1; int age = xiaoming.age;

哦哦, ++放在前面就是先+1再把+1后的值拿来用, ++放在后面就是先把值拿来用, 用完了再把原来的值+1, 对吧

孺子可教也

后续待完善

你可能感兴趣的:(C++,基础)